#include "despair.h"
#include "music.h"
#include <math.h>
#ifdef _WIN32
#include <conio.h>
#include <dos.h>
#endif

#include "vga.h"
#include "jclib.h"
#include "sincos.h"
#include "llscreen.h"
#include "llkey.h"
#include "timer.h"

#define WIDTH  320
#define HEIGHT 200

#define NUM_ANGLES 4096
static float cos_table[NUM_ANGLES];
#define FCos(a) (cos_table[((word)(a))>>4])
#define FSin(a) (FCos((a)+16384))

#define EPSILON 1E-15

static byte *g_img;

//static void (*OldTimer)(void);



// RGB 0..63
static void CalcPal(byte *pal, int r, int g, int b)
{
    int i;
    float dr, dg, db;
    float cr, cg, cb;

    cr = cg = cb = 0;
    dr = r / 63.0f;
    dg = g / 63.0f;
    db = b / 63.0f;

    for (i = 0; i < 64; i++)
    {
        pal[i*3+0] = (byte)cr;
        pal[i*3+1] = (byte)cg;
        pal[i*3+2] = (byte)cb;
        cr += dr;
        cg += dg;
        cb += db;
    }

    cr = (float)r;
    cg = (float)g;
    cb = (float)b;
    dr = (63 - r) / 63.0f;
    dg = (63 - g) / 63.0f;
    db = (63 - b) / 63.0f;

    for (i = 64; i < 128; i++)
    {
        pal[i*3+0] = (byte)cr;
        pal[i*3+1] = (byte)cg;
        pal[i*3+2] = (byte)cb;
        cr += dr;
        cg += dg;
        cb += db;
    }
}


struct thing
{
    float cx, cy;
    float vx, vy;
    float mass;
};

#define NUM_THINGS 32
static struct thing things[NUM_THINGS];

#define GRAVITY 1.0f
#define SQ(a) ((a)*(a))

static int finish = 0;

static void AdvanceThings()
{
    struct thing *p = things;
    float ax, ay;
    int i, j;

    p = things;

    for (i = 0; i < NUM_THINGS - 1; i++, p++)
    {
        struct thing *q = things;

        ax = ay = 0;
        for (j = 0; j < NUM_THINGS; j++, q++)
        {
            float f, fx, fy, r2, r, c, s, ir;

            if (i == j)
                continue;

            r2 = SQ(p->cx - q->cx) + SQ(p->cy - q->cy);
            if (r2 < 1000)
                r2 = 1000;
            f = GRAVITY * p->mass * q->mass / r2;

            r = (float)sqrt(r2);
            ir = 1 / r;

            c = (q->cx - p->cx) * ir;
            s = (q->cy - p->cy) * ir;

            f /= p->mass;

            fx = f * c;
            fy = f * s;

            ax += fx;
            ay += fy;
        }

        p->vx = p->vx + ax;
        p->vy = p->vy + ay;
        p->cx += p->vx;
        p->cy += p->vy;
    }
}

void RenderThing(float cx, float cy);

void RenderThings()
{
    struct thing *p = things;
    int i;

    for (i = 0; i < NUM_THINGS - 1; i++)
    {
        RenderThing(p->cx, p->cy);
        p++;
    }
}

byte bitmap[64*64];

void CalcBitmap(void)
{
    int i, j;
    float val;

    memset(bitmap, 0, sizeof(bitmap));
    for (i = 0; i < 64; i++)
    {
        for (j = 0; j < 64; j++)
        {
            val = (float) sqrt( (32 - i) * (32 - i) + (32 - j) * (32 - j) );
            if (val < 32)
            {
                val = (val / 64);
                val *= 3.1415926536f;
                bitmap[i * 64 + j] = (byte)(40.0f * (float) pow(cos(val), 2.0f));
            }
        }
    }
}

bool DoPsys(dword time)
{
    memset(g_img, 0, 64000);
    //VGA_PutColor(0, 63, 0, 0);
    RenderThings();
    //VGA_PutColor(0, 0, 63, 0);
    AdvanceThings();
    //VGA_PutColor(0, 0, 0, 63);
//    PollMusic();   // Time up to 20 ticks (don't allow music to stop).
    //VGA_PutColor(0, 63, 63, 0);
    //VGA_PutColor(0, 0, 0, 0);
    if (!InShittyWinblows)
    {
//        _disable();
//        VGA_VSync();
//        _enable();
    }
    //LLS_Update();
    SplitDump();

    return TRUE;
}

bool InitPsys(dword time)
{
    int i;

    g_img = LLS_Screen[0];

    for (i = 0; i < NUM_THINGS; i++)
    {
        things[i].cx = (float) (WIDTH/4 + (rand() % (WIDTH/2)));
        things[i].cy = (float) (HEIGHT/4 + (rand() % (HEIGHT/2)));
        //things[i].vx = (rand() / (float)RAND_MAX)/4.0;
        //things[i].vy = (rand() / (float)RAND_MAX)/4.0;
        things[i].vx = 0;
        things[i].vy = 0;
        things[i].mass = 1.0;
    }
    things[NUM_THINGS - 1].cx = WIDTH / 2;
    things[NUM_THINGS - 1].cy = HEIGHT / 2;
    things[NUM_THINGS - 1].vx = 0;
    things[NUM_THINGS - 1].vy = 0;
    things[NUM_THINGS - 1].mass = 80.0;

    CalcPal(DestPal, 27, 63, 7);
    for (i = 128; i < 256; i++)
    {
        DestPal[i*3] = DestPal[127*3];
        DestPal[i*3+1] = DestPal[127*3+1];
        DestPal[i*3+2] = DestPal[127*3+2];
    }

    CalcBitmap();

    VGA_DumpPalette((void*)DestPal, 0, 256);
    MustFade = FALSE;

    for (i = 0; i < NUM_ANGLES; i++)
    {
        cos_table[i] = (float) cos(2 * 3.1415926536 * i / NUM_ANGLES);
    }

//    OldTimer = TIMER_HookFunction;
//    TIMER_HookFunction = NULL;
//    PollMusic();   // Time up to 20 ticks (don't allow music to stop).

    CurFunction = DoPsys;

    return TRUE;
}

bool EndPsys(dword time)
{
//    TIMER_HookFunction = OldTimer;
    CurFunction = NULL;
    return TRUE;
}


extern bool SetUsePsys(dword time) {
    CurFunction = DoPsys;
    return TRUE;
}

void RenderScan(byte *dest, byte *src, dword np);
//#pragma aux RenderScan parm [EDI] [ESI] [ECX] modify [EAX EDI ESI ECX]

void RenderThing(float cx, float cy)
{
    int x0, y0, x1, y1;
    int ns = 64, np = 64;
    byte *p = bitmap, *scr = g_img;

    x0 = ((int)cx) - 32;
    x1 = ((int)cx) + 32;
    y0 = ((int)cy) - 32;
    y1 = ((int)cy) + 32;

    if (x1 < 0 || x0 >= 320 || y1 < 0 || y0 >= 200)
        return;

    if (y0 < 0)
    {
        ns += y0;
        p += (-y0 * 64);
        y0 = 0;
    }

    if (x0 < 0)
    {
        np += x0;
        p += (-x0);
        x0 = 0;
    }

    scr += y0 * 320 + x0;

    if (y1 > 200)
    {
        ns -= (y1 - 200);
    }

    if (x1 > 320)
    {
        np -= (x1 - 320);
    }

    while (ns--)
    {
        RenderScan(scr, p, np);

        scr += 320;
        p += 64;
    }
}