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

particle particles[MAXPARTICLES];
int particle_num = 0;
int particles_free[MAXFREEPARTICLES];
int freeparticle_num = 0;

extern blob_bitmap blobgfx[4];
extern image star;
blob_bitmap bstar[4];


void InitStar(blob_bitmap *bg)
{
    int x, y;
    int c, a, r, g, b;

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

	unsigned int *src = star.data;

	int i = 0;
    for (y=0; y<BlobHeight; y++)
    {
        for (x=0; x<BlobWidth; x++)
        {
            c = *src++;
            a = (c>>24) & 255;
            r = (c>>16) & 255;
            g = (c>>8) & 255;
            b = c & 255;
            c = (int)((r * 0.30 + g * 0.59 + b * 0.11) * a) >> 8;
            bg->bitmap[i++] = c;
        }
    }
}

void InitParticleGfx()
{
    bstar[0].width = star.width;
    bstar[0].bitmap = (unsigned char*)malloc(sizeof(unsigned char) * pow(bstar[0].width,2));
	InitStar(&bstar[0]);
}

void CreateParticle(vector *pos, vector *vel, vector *acl, colorARGB *col, int gfx, float life, float lifedec)
{
	int i;
	if (freeparticle_num==0)
	{
		i = particle_num;
	}
	else
	{
		i = particles_free[freeparticle_num];
		freeparticle_num--;
	}

	particles[i].pos.x = pos->x;
	particles[i].pos.y = pos->y;
	particles[i].vel.x = vel->x;
	particles[i].vel.y = vel->y;
	particles[i].acl.x = acl->x;
	particles[i].acl.y = acl->y;

	particles[i].col.a = col->a;
	particles[i].col.r = col->r;
	particles[i].col.g = col->g;
	particles[i].col.b = col->b;

	particles[i].gfx = gfx;
	particles[i].life = life;
	particles[i].lifedec = lifedec;
	particles[i].exists = true;
	if (freeparticle_num==0) particle_num++;
}

void UpdateParticles()
{
	int i;
	for (i=0; i<particle_num; i++)
	{
		if (particles[i].exists)
		{
			particles[i].pos.x += particles[i].vel.x;
			particles[i].pos.y += particles[i].vel.y;
			particles[i].vel.x += particles[i].acl.x;
			particles[i].vel.y += particles[i].acl.y;
			particles[i].life -= particles[i].lifedec;
			if (particles[i].life<=0)
			{
				particles[i].life = 0;
				particles[i].exists = false;
				particles_free[++freeparticle_num] = i;
			}
		}
	}
}


void DrawZoomParticleBilinear(particle *p, blob_bitmap *bg, unsigned int *vram)
{
    int xc = p->pos.x;
    int yc = p->pos.y;
    float zf = p->life;

	unsigned char pa = p->col.a * zf;
	unsigned char pr = p->col.r;
	unsigned char pg = p->col.g;
	unsigned char pb = p->col.b;

	unsigned int a1, r1, g1, b1;
	unsigned int r2, g2, b2;

    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);

                a1 = (c * pa) >> 8;
                r1 = (c * pr) >> 8;
                g1 = (c * pg) >> 8;
                b1 = (c * pb) >> 8;

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

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

                *(vram+i) = (a1<<24) | (r1<<16) | (g1<<8) | b1;
                i++;
                u+=di;
            }
            v+=di;
            i += -(x1-x0+1) + GameScreenWidth;
        }
    }
}


void DrawZoomParticleNearest(particle *p, blob_bitmap *bg, unsigned int *vram)
{
    int xc = p->pos.x;
    int yc = p->pos.y;
    float zf = p->life;

	unsigned char pa = p->col.a * zf;
	unsigned char pr = p->col.r;
	unsigned char pg = p->col.g;
	unsigned char pb = p->col.b;

	unsigned int a1, r1, g1, b1;
	unsigned int r2, g2, b2;

    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 = bg->bitmap[(u>>fp) + up];

                a1 = (c * pa) >> 8;
                r1 = (c * pr) >> 8;
                g1 = (c * pg) >> 8;
                b1 = (c * pb) >> 8;

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

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

                *(vram+i) = (a1<<24) | (r1<<16) | (g1<<8) | b1;
                i++;
                u+=di;
            }
            v+=di;
            i += -(x1-x0+1) + GameScreenWidth;
        }
    }
}


void DrawZoomParticle(particle *p, blob_bitmap *bg, unsigned int *vram, bool filtering)
{
    if (filtering) DrawZoomParticleBilinear(p, bg, vram);
        else DrawZoomParticleNearest(p, bg, vram);
}

void DoParticleExplosion(vector *pos, int number, int gfx, float gravity, float life, float lifedec, float speedmul, colorARGB col, bool randcol)
{
	vector vel, acl;

	int i;
	for (i=0; i<number; i++)
	{
		vel.x = speedmul * (float)(rand() - RAND_MAX/2) / (RAND_MAX/2);
		vel.y = speedmul * (float)(rand() - RAND_MAX/2) / (RAND_MAX/2);
		acl.x = 0;
		acl.y = gravity;

		if (randcol)
		{
			col.a = 255;
			col.r = (int)(((float)rand() / RAND_MAX) * 255);
			col.g = (int)(((float)rand() / RAND_MAX) * 255);
			col.b = (int)(((float)rand() / RAND_MAX) * 255);
		}

		CreateParticle(pos, &vel, &acl, &col, gfx, life, lifedec);
	}
}


void DoParticleImpactExplosion(vector *pos, vector *vel, int degrees, int number, int gfx, float gravity, float life, float lifedec, float speedmul, colorARGB col, bool randcol)
{
	vector acl;
	vector rotvel;
	float flrand;
	float sprand;
	float angle;

    Vnormalize(vel);
	int i;
	for (i=0; i<number; i++)
	{
		flrand = (float)rand() / RAND_MAX;
		angle = ((float)degrees/57.3) * (flrand - 0.5);
		Vrotate (vel, &rotvel, angle);

		sprand = speedmul * ((float)rand() / (float)RAND_MAX);
		rotvel.x *= sprand;
		rotvel.y *= sprand;
		acl.x = 0;
		acl.y = gravity;
		if (randcol)
		{
			col.a = 255;
			col.r = (int)(((float)rand() / RAND_MAX) * 255);
			col.g = (int)(((float)rand() / RAND_MAX) * 255);
			col.b = (int)(((float)rand() / RAND_MAX) * 255);
		}

		CreateParticle(pos, &rotvel, &acl, &col, gfx, life, lifedec);
	}
}


void DoParticles()
{
	UpdateParticles();
}
