#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "main.h"
#include "blobs.h"
#include "render.h"

blob_bitmap blobgfx[4];

// ====== Generate ======

void InitBlob(blob_bitmap *bg)
{
    int x, y;
    int xc, yc;
    int c;

    int BlobWidth = bg->width;
    int BlobHeight = BlobWidth;
    int BlobSize = BlobWidth * BlobHeight;

    float dist;
    float distsize = BlobSize/65536.0;
    float distparam = 2*32768;

    int i = 0;
    for (y=0; y<BlobHeight; y++)
    {
        for (x=0; x<BlobWidth; x++)
        {
            xc = x - (BlobWidth>>1);
            yc = y - (BlobHeight>>1);
            dist = xc * xc + yc * yc;
            if (dist<distsize) dist = distsize;
            c = (int)((BlobSize * (distparam*distsize)) / (dist * dist));
            if (xc==0 && yc==0) c = 255;
            if (c>255) c = 255;
            bg->bitmap[i++] = c;
        }
    }
}

void InitBlobGfx()
{
    int i;
    for (i=0; i<4; i++)
    {
        blobgfx[i].width = pow(2,i+5);
        blobgfx[i].bitmap = (unsigned char*)malloc(sizeof(unsigned char) * pow(blobgfx[i].width,2));
        InitBlob(&blobgfx[i]);
    }
}

void DrawZoomBlobBilinear(blob *bl, unsigned char *src, unsigned int *vram, unsigned char r, unsigned char g, unsigned char b)
{
    int xc = bl->x;
    int yc = bl->y;
    float zf = bl->z;

    blob_bitmap *bg = &blobgfx[bl->size];
    int BlobWidth = bg->width;
    int BlobHeight = BlobWidth;

    int blobSHR = (int)(float)(log(BlobWidth)/log(2));

    int x, y;
    int x0 = 0, y0 = 0;
    int x1 = (int)(BlobWidth * zf - 1), y1 = (int)(BlobHeight * zf - 1);

    int xi = (int)(xc - (BlobWidth >> 1) * zf);
    int yi = (int)(yc - (BlobHeight >> 1) * zf);

    if ((xi < GameScreenWidth) && (xi > -BlobWidth * zf) && (yi < GameScreenHeight) && (yi > -BlobHeight * zf))
    {
    	int fpSHR = 16, fpMUL = (int)pow(2,fpSHR);
        int su = 0, sv = 0;

        if (xi<0)
        {
            x0 = - xi; xi = 0;
            su = (int)((x0<<fpSHR)/zf);
        }
        if (yi<0)
        {
            y0 = - yi; yi = 0;
            sv = (int)((y0<<fpSHR)/zf);
        }
        if (xi > GameScreenWidth - (zf * BlobWidth)) x1 = GameScreenWidth + x0 - xi - 1;
        if (yi > GameScreenHeight - (zf * BlobWidth)) y1 = GameScreenHeight + y0 - yi - 1;

        int vofs= xi + yi * GameScreenWidth;

        int di = (int)(fpMUL/zf);
        int u, v, up, vp;
        int pu, pv;
        int i = vofs;

        int ul, vl;
        int c0, c1, c2, c3;
        int cx_top, cx_bottom;
        int c;

        v = sv;
        for (y = y0; y<=y1; y++)
        {
            u = su;
            pv = (v >> fpSHR)<<blobSHR;
            vl = v & (fpMUL - 1);
            for (x = x0; x<=x1; x++)
            {
                pu = u >> fpSHR;
                c0 = bg->bitmap[pu + pv];
                c1 = bg->bitmap[pu+1 + pv];
                c2 = bg->bitmap[pu + pv+BlobWidth];
                c3 = bg->bitmap[pu+1 + pv+BlobWidth];

                ul = u & (fpMUL - 1);
                cx_top = c0 + (((c1 - c0) * ul) >> fpSHR);
                cx_bottom = c2 + (((c3 - c2) * ul) >> fpSHR);
                c = cx_top + (((cx_bottom - cx_top) * vl) >> fpSHR);

                c = *(src+i) + c;
                if (c > 255) c = 255;
                *(src+i) = c;
                *(vram+i) = (c<<16) | (c<<8) | c;
                i++;
                u+=di;
            }
            v+=di;
            i += -(x1-x0+1) + GameScreenWidth;
        }
    }
}


void DrawZoomBlobNearest(blob *bl, unsigned char *src, unsigned int *vram, unsigned char r, unsigned char g, unsigned char b)
{
    int xc = bl->x;
    int yc = bl->y;
    float zf = bl->z;

    blob_bitmap *bg = &blobgfx[bl->size];
    int BlobWidth = bg->width;
    int BlobHeight = BlobWidth;

    int blobSHR = (int)(float)(log(BlobWidth)/log(2));

    int x, y;
    int x0 = 0, y0 = 0;
    int x1 = (int)(BlobWidth * zf - 1), y1 = (int)(BlobHeight * zf - 1);

    int xi = (int)(xc - (BlobWidth >> 1) * zf);
    int yi = (int)(yc - (BlobHeight >> 1) * zf);

    if ((xi < GameScreenWidth) && (xi > -BlobWidth * zf) && (yi < GameScreenHeight) && (yi > -BlobHeight * zf))
    {
    	int fp = 16, mulfp = (int)pow(2,fp);
        int su = 0, sv = 0;

        if (xi<0)
        {
            x0 = - xi; xi = 0;
            su = (int)((x0<<fp)/zf);
        }
        if (yi<0)
        {
            y0 = - yi; yi = 0;
            sv = (int)((y0<<fp)/zf);
        }
        if (xi > GameScreenWidth - (zf * BlobWidth)) x1 = GameScreenWidth + x0 - xi - 1;
        if (yi > GameScreenHeight - (zf * BlobWidth)) y1 = GameScreenHeight + y0 - yi - 1;

        int vofs= xi + yi * GameScreenWidth;
        unsigned int c;

        int di = (int)(mulfp/zf);
        int u, v, up;
        int i = vofs;

        v = sv;
        for (y = y0; y<=y1; y++)
        {
            u = su;
            up = ((v>>fp)<<blobSHR);
            for (x = x0; x<=x1; x++)
            {
                c = *(src+i) + bg->bitmap[(u>>fp) + up];
                if (c > 255) c = 255;
                *(src+i) = c;
                *(vram+i) = (c<<16) | (c<<8) | c;
                i++;
                u+=di;
            }
            v+=di;
            i += -(x1-x0+1) + GameScreenWidth;
        }
    }
}


void DrawBlob(blob *bl, unsigned char *src, unsigned int *vram, unsigned char r, unsigned char g, unsigned char b)
{
    int xc = bl->x;
    int yc = bl->y;

    blob_bitmap *bg = &blobgfx[bl->size];
    int BlobWidth = bg->width;
    int BlobHeight = BlobWidth;

    int blobSHR = (int)(float)(log(BlobWidth)/log(2));

    int x, y;
    int x0 = 0, y0 = 0;
    int x1 = BlobWidth - 1, y1 = BlobHeight - 1;

    int xi = xc - (BlobWidth >> 1);
    int yi = yc - (BlobHeight >> 1);

    if ((xi < GameScreenWidth) && (xi > -BlobWidth) && (yi < GameScreenHeight) && (yi > -BlobHeight))
    {
        if (xi<0)
        {
            x0 = - xi; xi = 0;
        }
        if (yi<0)
        {
            y0 = - yi; yi = 0;
        }
        if (xi > GameScreenWidth - BlobWidth) x1 = GameScreenWidth + x0 - xi - 1;
        if (yi > GameScreenHeight - BlobHeight) y1 = GameScreenHeight + y0 - yi - 1;

        int vofs = xi + yi * GameScreenWidth;
        int bofs = x0 + (y0 << blobSHR);

        unsigned char alpha;
		unsigned char r1, g1, b1;
		unsigned char r2, g2, b2;
        unsigned int c;

        for (y = y0; y<=y1; y++)
        {
            for (x = x0; x<=x1; x++)
            {
                c = *(src+vofs) + bg->bitmap[bofs];
                if (c > 255) c = 255;
                *(src+vofs) = c;

				alpha = c;
				if (alpha<192) alpha = 0;
				if (c<128) c = 128;
				r1 = g1 = b1 = c;
				r1 = (r * c) >> 8;
				g1 = (g * c) >> 8;
				b1 = (b * c) >> 8;

				c = *(vram+vofs);
				r2 = (c >> 16) & 255;
				g2 = (c >> 8) & 255;
				b2 = c & 255;

				r1 = r2 + (((r1 - r2) * alpha) >> 8);
				g1 = g2 + (((g1 - g2) * alpha) >> 8);
				b1 = b2 + (((b1 - b2) * alpha) >> 8);

                *(vram+vofs) = (r1<<16) | (g1<<8) | b1;
                vofs++;
                bofs++;
            }
            vofs += -(x1-x0+1) + GameScreenWidth;
            bofs += -(x1-x0+1) + BlobWidth;
        }
    }
}


void DrawZoomBlob(blob *bl, unsigned char *src, unsigned int *vram, unsigned char r, unsigned char g, unsigned char b, bool filtering)
{
    if (filtering) DrawZoomBlobBilinear(bl, src, vram, r, g, b);
        else DrawZoomBlobNearest(bl, src, vram, r, g, b);
}


void ClearBlob(blob *bl, unsigned char *src, unsigned int *vram)
{
    int xc = bl->x;
    int yc = bl->y;

    blob_bitmap *bg = &blobgfx[bl->size];
    int BlobWidth = bg->width;
    int BlobHeight = BlobWidth;

    int x, y;
    int x0 = 0, y0 = 0;
    int x1 = BlobWidth - 1, y1 = BlobHeight - 1;

    int xi = xc - (BlobWidth >> 1);
    int yi = yc - (BlobHeight >> 1);

    if ((xi < GameScreenWidth) && (xi > -BlobWidth) && (yi < GameScreenHeight) && (yi > -BlobHeight))
    {
        if (xi<0)
        {
            x0 = - xi; xi = 0;
        }
        if (yi<0)
        {
            y0 = - yi; yi = 0;
        }
        if (xi > GameScreenWidth - BlobWidth) x1 = GameScreenWidth + x0 - xi - 1;
        if (yi > GameScreenHeight - BlobHeight) y1 = GameScreenHeight + y0 - yi - 1;

        int vofs = xi + yi * GameScreenWidth;

        for (y = y0; y<=y1; y++)
        {
            for (x = x0; x<=x1; x++)
            {
                *(src+vofs) = 0;
          //      *(vram+vofs) = 0;
                vofs++;
            }
            vofs += -(x1-x0+1) + GameScreenWidth;
        }
    }
}


void ClearZoomBlob(blob *bl, unsigned char *src, unsigned int *vram)
{
    int xc = bl->x;
    int yc = bl->y;
    float zf = bl->z;

    blob_bitmap *bg = &blobgfx[bl->size];
    int BlobWidth = bg->width;
    int BlobHeight = BlobWidth;

    int x, y;
    int x0 = 0, y0 = 0;
    int x1 = (int)(BlobWidth * zf - 1), y1 = (int)(BlobHeight * zf - 1);

    int xi = (int)(xc - (BlobWidth >> 1) * zf);
    int yi = (int)(yc - (BlobHeight >> 1) * zf);

    if ((xi < GameScreenWidth) && (xi > -BlobWidth * zf) && (yi < GameScreenHeight) && (yi > -BlobHeight * zf))
    {
        if (xi<0)
        {
            x0 = - xi; xi = 0;
        }
        if (yi<0)
        {
            y0 = - yi; yi = 0;
        }
        if (xi > GameScreenWidth - (zf * BlobWidth)) x1 = GameScreenWidth + x0 - xi - 1;
        if (yi > GameScreenHeight - (zf * BlobWidth)) y1 = GameScreenHeight + y0 - yi - 1;

        int vofs= xi + yi * GameScreenWidth;
        int i = vofs;

        for (y = y0; y<=y1; y++)
        {
            for (x = x0; x<=x1; x++)
            {
                *(src+i) = 0;
                *(vram+i) = 0;
                i++;
            }
            i += -(x1-x0+1) + GameScreenWidth;
        }
    }
}
