
#include "despair.h"

#include "llscreen.h"
#include "llkey.h"
#include "pix.h"
#include "sincos.h"

#include <math.h>
#include <stdio.h>

#define PI 3.14159264f

typedef struct {
    byte angle;
    byte rad;
} TAngTab;

typedef struct {
    dword disp;
} TRadTab;

#define MAXDISP (128)

#define TEXW 256


static void BuildTables(TAngTab *tb, TRadTab *rd) {
    int i, j;
    float rad, ang;
    float maxrad = 0;

    for (i = -100; i < 100; i++) {
        for (j = -128; j < 128; j++) {
            rad = (float) sqrt(i*i+j*j);
            ang = 3.0f*PI/2-(float)atan2((i+0*(float)cos(j*PI/140))*PI/100, (j+0*(float)sin(i*PI/100))*PI/100);
            tb->angle = (byte)(ang*128/PI);// + 192*(cos((rad)*PI/80));
            tb->rad   = (byte)((rad+0*(1+cos((i+j)*PI/100))));
/*
            rad = sqrt(i*i+j*j);
            ang = 3.0*PI/2-atan2((i+40*cos(j*PI/140))*PI/100, (j+70*sin(i*PI/100))*PI/100);
            tb->angle = ang*128/PI;// + 192*(cos((rad)*PI/80));
            tb->rad   = (rad+20*(1+cos((i+j)*PI/100)));
*/

//            tb->angle = (i+100);
//            tb->rad   = (j+128);
            if (maxrad < rad)
                maxrad = rad;
            tb++;
        }
    }

    if (rd != NULL) {
        for (j = -MAXDISP/2+1; j <= MAXDISP/2; j++) {
            for (i = 0; i < 256; i++) {
                rd->disp = TEXW*(int)(j*cos(i*PI/128)) + (int)(j*sin(i*PI/128));
                rd++;
            }
        }
    }
}

static void DrawRipple(byte *dest, const byte *tex, TAngTab *angtb, TRadTab *radtb, dword *radd, dword *gadd) {
    int i, j, k;

    k = 0;
    for (i = 0; i < 200; i++) {
        for (j = 0; j < 256; j++) {
            byte c;

            c = tex[(k
                     + radtb[(byte)(angtb->angle + 64) + gadd[angtb->rad]].disp
                     + radtb[angtb->angle + radd[angtb->rad]].disp
                    ) & 65535];
//            c = tex[(k + radtb[(((byte)(angtb->angle+64)) + 256*gadd[angtb->rad])].disp + radtb[(angtb->angle + 256*radd[angtb->rad])].disp) & 65535];
//            c = tex[(k + radtb[(angtb->angle + 256*radd[angtb->rad])].disp) & 65535];
            *dest = c;
            dest++;
            angtb++;
            k++;
        }
        dest += 320-256;
    }
}

static byte *tex;
static TAngTab *angtb;
static TRadTab *radtb;
static dword radd[256];
static dword gadd[256];
int    basetime = 0;


static bool DoRipple(dword time) {
    int i;
    float maxr, maxg;

    time -= basetime;

    maxr = (MAXDISP/2-0.5f)*(1-(float)cos((float)(time)*PI/874.0))/2;
    maxg = (MAXDISP/2-0.5f)*(1-(float)cos((float)(time)*PI/554.0))/2;

    for (i = 0; i < SIZEARRAY(radd); i++) {
        int r;

        r = (int) (maxr*(1+(float)sin((float)(-(int)time+i)*PI/64.0f))/2);
//        r = r*(i*i/400.0)/((i*i/400.0)+1);
        r += MAXDISP/2;
        radd[i] = 256*r;
        r = (int) (maxg*(sin((float)(-(int)time+i)*PI/44.0))/2);
        r += MAXDISP/2;
        gadd[i] = 256*r;
    }
    DrawRipple(LLS_Screen[0] + 32, tex, angtb, radtb, radd, gadd);

    SplitDump();
    //LLS_Update();
    return TRUE;
}

extern bool InitRipple(dword time) {

    angtb = NEW(TEXW*200*sizeof(*angtb));
    REQUIRE(angtb != NULL);
    radtb = NEW(256*MAXDISP*sizeof(*radtb));
    REQUIRE(radtb != NULL);

    BuildTables(angtb, radtb);

    memset(LLS_Screen[0], 0, 320*200);

    tex = NEW(65536);
    REQUIRE(tex != NULL);
    memset(tex, 0, 65536);
    PIX_Load("GFX\\ripp2.gif", tex, DestPal, NULL, NULL, FALSE);
    EnhancePal();
    VGA_DumpPalette(DestPal, 0, 256);
    basetime = time;
    CurFunction = DoRipple;
    return TRUE;
}

extern bool EndRipple(dword time) {
    DISPOSE(tex);
    DISPOSE(radtb);
    DISPOSE(angtb);
    CurFunction = NULL;
    return TRUE;
}

// -----------------------------

static byte *RadTable;

//static byte (*ctable)[256];//256

static int CirclerType = 0;

#define pal DestPal
/*
static void MatchColors(void) {
    int i, j, k, found_num;
    unsigned long found_dist, this_dist;

    if (JCLIB_Load("GFX\\circler.mix", ctable, 65536) == 65536)
        return;
    for (i = 0; i < 256; i++) {
        for (j = 0; j < 256; j++) {
            int r, g, b;
            found_dist = 0x7FFFFFFFL;
            found_num = 0;
            r = (8*pal[3*i+0] + 4*pal[3*j+0])/12;
            g = (8*pal[3*i+1] + 4*pal[3*j+1])/12;
            b = (8*pal[3*i+2] + 4*pal[3*j+2])/12;
            for (k = 0; k < 768; k+=3) {
                this_dist = Pow2(r - (sint32)pal[k])
                          + Pow2(g - (sint32)pal[k+1])
                          + Pow2(b - (sint32)pal[k+2]);
                if (this_dist < found_dist) {
                    found_dist = this_dist;
                    found_num = k;
                    if (this_dist == 0)
                        break;
                }
            }
            ctable[i][j] = found_num/3;
        }
//        putchar('ú'); fflush(stdout);
    }
    {
        FILE *f = fopen("GFX\\circler.mix", "wb");
        fwrite(ctable[0],   256, 64, f);
        fwrite(ctable[64],  256, 64, f);
        fwrite(ctable[128], 256, 64, f);
        fwrite(ctable[192], 256, 64, f);
        fclose(f);
    }
}
*/
static void DrawCircler(byte *dest, const byte *tex, TAngTab *angtb, byte *radtb, byte *radd) {
    int i, j;

    for (i = 0; i < 200; i+=1) {
        for (j = 0; j < 256; j+=1) {
            byte x, y;

            x = radd[angtb->angle];
            y = radtb[angtb->rad];
//            *dest++ = ctable[*dest][tex[y][x]];
            *dest++ = tex[(y<<8)+x];
            angtb++;
        }
        dest += 320-256;
    }
}

static bool DoCircler(dword time) {
    int i;
    float r = 20;

    switch (CirclerType) {
        case 0:
            for (i = 0; i < 256; i++) {
//                RadTable[i] = 128*((cos((i*3-time)*PI/80))*(10.0+9.5*pow(sin(time*PI/150), 2))/(230 + 50*sin(time*PI/150) - i));
                r = 15+10*(float)cos((time+i)*PI/30);
                RadTable[i] = (byte) (r+i);//*256/(i+r);
            }
            for (i = 0; i < 256; i++) {
                r = 30*(float)cos((time)*PI/88);
                RadTable[i+256] = (byte) (i+r*cos(i*PI/128*4));
            }
            break;
        case 1:
            for (i = 0; i < 256; i++) {
                r = 25+20*(float)cos((-(int)time+i)*PI/70);
                RadTable[i] = (byte) (r*256/(i+r));
            }
            for (i = 0; i < 256; i++) {
                r = 10*(float)cos((time)*PI/88);
                RadTable[i+256] = (byte)(i+r*cos(i*PI/128*7));
            }
            break;
        case 2:
            for (i = 0; i < 256; i++) {
                r = 25+15*(float) cos((time+i)*PI/40);
                RadTable[i] = (byte) (r*256/(i+r));
            }
            for (i = 0; i < 256; i++) {
                r = (byte) (20*cos((time)*PI/68));
                RadTable[i+256] = (byte) (50*cos((time)*PI/98) + i+r*cos(i*PI/128*6));
            }
            break;
        default:
            for (i = 0; i < 256; i++) {
                r = (byte) 10+10*(float)cos((- (int) time+i)*PI/40);
                RadTable[i] = (byte)(r*256/(i+r));
            }
            for (i = 0; i < 256; i++) {
                int kk;
                r = 30*(float)cos((time)*PI/128);
                kk = (int) (i + 130*(float)pow(cos((time)*PI/448),4));
                RadTable[i+256] = (byte) (kk + r*(float)cos(kk*PI/128*5));
            }
            break;
    }

    DrawCircler(LLS_Screen[0] + 32, tex, angtb, RadTable, RadTable + 256);
    LLS_Update();

    return TRUE;
}


extern bool InitCircler(dword time) {

//    LLS_Init(LLSM_VIRTUAL, LLSVM_MODE13);
    LLS_Init(LLSM_DIRECT, LLSVM_MODE13);
    angtb = NEW(TEXW*200*sizeof(*angtb));
    REQUIRE(angtb != NULL);
    RadTable = NEW(256*2*sizeof(*RadTable));
    REQUIRE(RadTable != NULL);

    BuildTables(angtb, NULL);

    memset(LLS_Screen[0], 0, 320*200);

    tex = NEW(65536);
    REQUIRE(tex != NULL);
    memset(tex, 0, 65536);
    PIX_Load("GFX\\circler.gif", tex, DestPal, NULL, NULL, FALSE);
    VGA_DumpPalette(DestPal, 0, 256);

//    REQUIRE( (ctable = NEW(256*sizeof(*ctable))) != NULL);
//    MatchColors();
    basetime = time;
    CurFunction = DoCircler;
    return TRUE;
}

extern bool EndCircler(dword time) {
//    DISPOSE(ctable);
    DISPOSE(tex);
    DISPOSE(radtb);
    DISPOSE(angtb);
    CurFunction = NULL;
    return TRUE;
}

extern bool ChangeCircler(dword time) {
    CirclerType++;
    return TRUE;
}
