#include "gpulocal.h"

#define   CACHE_MAX 16

// User-defined types
typedef struct {
    INT16  *Texture;
    UINT32  tpage,clutid;
} TextureCache;

static UINT16       rgbtbl[65536]; /* bgr -> rgb */
static TextureCache tcache[CACHE_MAX];
static int          tcache_index = 0;

void CacheInit(void)
{
    int i,r,g,b;

    for(b=i=0;b<32;b++)
        for(g=0;g<32;g++)
            for(r=0;r<32;r++,i++)
                rgbtbl[i] = (r<<10) | (g<<5) | b;

    for(i=0;i<32768;i++)
        rgbtbl[i+32768]=rgbtbl[i] | 0x8000;

    tcache[0].Texture = (INT16 *)malloc(256*256*CACHE_MAX*sizeof(INT16));
    tcache[0].tpage = tcache[0].clutid = -1;
    for (i=1; i<CACHE_MAX; i++)
    {
        tcache[i].Texture = tcache[0].Texture + 256*256*i;
        tcache[i].tpage = tcache[i].clutid = -1;
    }
}

void CacheDeinit(void)
{
    free(tcache[0].Texture);
}

static void pixel2texel(UINT16 *t, UINT16 *p,int n)
{
    do {
        *t++=rgbtbl[*p++];
    } while(--n);
}

static void load_texture(INT16 *texture,int tpage,int clut)
{
    UINT16 *ctbl,*tex;
    UINT16 *t,ctbl2[256];
    int x,y,mode;

    mode = (tpage>>7)&3;

    ctbl = vram+(clut&0x7fff)*16;
    tex  = vram+(tpage&15)*64 + (tpage&16)*16*FRAME_W;
    t = texture;

    switch (mode) {
    case 0: // 4bit
        pixel2texel(ctbl2,ctbl,16);
        for(y=0;y<256;y++) {
            for(x=0;x<256;x+=4) {
                int c = *tex++;
                t[0] = ctbl2[c&15];
                t[1] = ctbl2[(c>>4)&15];
                t[2] = ctbl2[(c>>8)&15];
                t[3] = ctbl2[(c>>12)];
                t+=4;
            }
            tex += FRAME_W-256/4;
        }
        break;

    case 1: // 8bit
        pixel2texel(ctbl2,ctbl,256);
        for(y=0;y<256;y++) {
            for(x=0;x<256;x+=2) {
                int c = *tex++;
                t[0] = ctbl2[c&255];
                t[1] = ctbl2[c>>8];
                t+=2;
            }
            tex += FRAME_W-256/2;
        }
        break;

    case 2: /* 16bit */
        for(y=0;y<256;y++) {
            pixel2texel(t,tex,256);
            tex += FRAME_W;
            t += 256;
        }
        break;
    }
}

INT16 *get_texture(UINT32 tpage, UINT32 clut)
{
    TextureCache *tc;
    int           tchanged,cchanged,i;

    tpage&=(3<<7)|31;
    clut &= 0x7fff;

    tchanged = tpage_changed(tpage);
    cchanged = clut_changed(clut);

    if (tchanged | cchanged) {
// parge cache
        for(i=0, tc = tcache;i<CACHE_MAX;i++, tc++) {
            if ((cchanged && tc->clutid==clut ) ||
                (tchanged && tc->tpage==tpage)) {
                    tc->clutid = -1;
                    tc->tpage  = -1;
            }
        }
    } else {
// search cache
        for(i=0, tc = tcache;i<CACHE_MAX;i++, tc++) {
            if (tc->tpage==tpage && tc->clutid==clut)
                return tc->Texture;
        }
    }

#if 0
    printf("Texture Cache miss hit tpage=%04x clut=%04x idx->%d\n",
            (int)tpage,(int)clut,(int)tcache_index);
#endif

    tc = &tcache[tcache_index];
    if (++tcache_index >= CACHE_MAX) tcache_index = 0;

    tc->clutid = clut;
    tc->tpage  = tpage;

    load_texture(tc->Texture,tpage,clut);

    return tc->Texture;
}
