#include <allegro.h>
#include <winalleg.h>
#include <stdio.h>
#include <stdint.h>
#include "gba.h"

#define BUFLEN ((456<<1)*3)
signed short soundbuf[BUFLEN<<2];
void addsoundbuffer();
int soundbufpos=0;

unsigned char snda,sndb;

int soundacount,soundawp,soundarp;
int soundbcount,soundbwp,soundbrp;

unsigned char soundafifo[32];
unsigned char soundbfifo[32];

FILE *soundf;

#define TONECONST  (131072.0f/27360.0f)
#define WAVECONST (2097152.0f/27360.0f)
#define NOISECONST (524288.0f/27360.0f)
int soundlook[4][8]=
{
        {1,0,0,0,0,0,0,0},
        {1,1,0,0,0,0,0,0},
        {1,1,1,1,0,0,0,0},
        {1,1,1,1,1,1,0,0},
};
unsigned char waveram[64];

void pollsound()
{
        signed short temp;
/*        if (!soundf)
        {
                soundf=fopen("sound.pcm","wb");
                printf("Opening file!\n");
        }
//        printf("Writing to it!\n");
        putc(snda,soundf);
        putc(sndb,soundf);*/
        soundbuf[soundbufpos]=soundbuf[soundbufpos+1]=0x8000;
        temp=(snda*256);
        temp/=4;
        if (sound.ctrlh&0x0200) soundbuf[soundbufpos]+=temp;
        if (sound.ctrlh&0x0100) soundbuf[soundbufpos+1]+=temp;
        temp=(sndb*256);
        temp/=4;
        if (sound.ctrlh&0x2000) soundbuf[soundbufpos]+=temp;
        if (sound.ctrlh&0x1000) soundbuf[soundbufpos+1]+=temp;
        
        sound.count1-=(TONECONST*8);
        while (sound.count1<=sound.latch1)
        {
                sound.count1+=2047-sound.latch1;
                if (!sound.latch1) sound.count1=2048;
                sound.stat1++;
                sound.stat1&=7;
        }
        soundbuf[soundbufpos]+=(soundlook[sound.wave1][sound.stat1]*sound.curvol1*256);
        soundbuf[soundbufpos+1]+=(soundlook[sound.wave1][sound.stat1]*sound.curvol1*256);

        sound.count2-=(TONECONST*8);
        while (sound.count2<=sound.latch2)
        {
                sound.count2+=2047-sound.latch2;
                if (!sound.latch2) sound.count2=2048;
                sound.stat2++;
                sound.stat2&=7;
        }
        soundbuf[soundbufpos]+=(soundlook[sound.wave2][sound.stat2]*sound.curvol2*256);
        soundbuf[soundbufpos+1]+=(soundlook[sound.wave2][sound.stat2]*sound.curvol2*256);
        
        sound.count3-=WAVECONST;
        while (sound.count3<=sound.latch3)
        {
                sound.count3+=2047-sound.latch3;
                if (!sound.latch3) sound.count3=2048;
                sound.stat3++;
                sound.stat3&=63;
        }
        soundbuf[soundbufpos]+=(waveram[(sound.stat3&sound.wavemask)|sound.waveset]*sound.curvol3*24);
        soundbuf[soundbufpos+1]+=(waveram[(sound.stat3&sound.wavemask)|sound.waveset]*sound.curvol3*24);

        sound.count4+=NOISECONST;
        while (sound.count4>=sound.latch4)
        {
                sound.count4-=sound.latch4;
                if (!sound.latch4) sound.count4=-1;
                sound.stat4=sound.nshift&1;
                sound.nshift>>=1;
                if (sound.stat4)
                {
                        if (sound.polylen) sound.nshift^=0x60;
                        else               sound.nshift^=0x6000;
                }
        }
        soundbuf[soundbufpos]+=(sound.stat4*sound.curvol4*256);
        soundbuf[soundbufpos+1]+=(sound.stat4*sound.curvol4*256);

//rpclog("%i %i\n",sound.envtime,sound.env2);
        sound.envtime--;
        if (sound.envtime<=0)
        {
                sound.envtime=427;
                if (sound.env1)
                {
//                        rpclog("SOUND1 %i %i %i\n",sound.envcnt1,sound.env1,sound.curvol1);
                        sound.envcnt1--;
                        if (!sound.envcnt1)
                        {
                                sound.envcnt1=sound.env1;
                                if (sound.envdir1 && sound.curvol1<15)  sound.curvol1++;
                                if (!sound.envdir1 && sound.curvol1>0) sound.curvol1--;
                        }
                }
                if (sound.env2)
                {
//                        rpclog("SOUND2 %i %i %i\n",sound.envcnt2,sound.env2,sound.curvol2);
                        sound.envcnt2--;
                        if (!sound.envcnt2)
                        {
                                sound.envcnt2=sound.env2;
                                if (sound.envdir2 && sound.curvol2<15)  sound.curvol2++;
                                if (!sound.envdir2 && sound.curvol2>0) sound.curvol2--;
                        }
                }
                if (sound.env4)
                {
                        sound.envcnt4--;
                        if (!sound.envcnt4)
                        {
                                sound.envcnt4=sound.env4;
                                if (sound.envdir4 && sound.curvol4<15)  sound.curvol4++;
                                if (!sound.envdir4 && sound.curvol4>0) sound.curvol4--;
                        }
                }
        }

        soundbufpos+=2;
        if (soundbufpos>=BUFLEN)
        {
                soundbufpos=0;
                addsoundbuffer();
        }
}

void clocksounda()
{
        int c;
        soundaclocks++;
        
        snda=soundafifo[soundarp];
        soundarp++;
        soundarp&=31;
        soundacount--;
        if (soundacount<0)
        {
                soundacount=0;
                soundarp--;
                soundarp&=31;
        }
        if (soundacount<=16)
        {
                sounddma(1);
/*                if (soundacount<=16)
                {
                        for (c=0;c<16;c++)
                        {
                                soundafifo[soundawp++]=0;
                                soundawp&=31;
                        }
                        printf("Sound awp %i\n",soundawp);
                        soundawp&=31;
                }*/
        }
}

void clocksoundb()
{
        int c;
        soundbclocks++;

        sndb=soundbfifo[soundbrp];
        soundbrp++;
        soundbrp&=31;
        soundbcount--;
        if (soundbcount<0)
        {
                soundbcount=0;
                soundbrp--;
                soundbrp&=31;
        }
        if (soundbcount<=16)
        {
                sounddma(2);
/*                if (soundbcount<=16)
                {
                        for (c=0;c<16;c++)
                        {
                                soundbfifo[soundbwp++]=0;
                                soundbwp&=31;
                        }
                        printf("Sound bwp %i\n",soundawp);
                        soundbwp&=31;
                }*/
        }
}

void resetsounda()
{
        soundacount=soundawp=soundarp=0;
}

void resetsoundb()
{
        soundbcount=soundbwp=soundbrp=0;
}

void writesounda(unsigned char v)
{
        if (soundacount>=32) return;
        soundafifo[soundawp++]=v;
//        printf("AWP %i\n",soundbwp);
        soundawp&=31;
        soundacount++;
}

void writesoundb(unsigned char v)
{
        if (soundbcount>=32) return;
        soundbfifo[soundbwp++]=v;
//        printf("BWP %i\n",soundbwp);
        soundbwp&=31;
        soundbcount++;
}

int infocus=1;
int soundon=1;
int soundthreadon=0,quitsoundthread=0;
void updatesoundbuffer();

void initsound()
{
        if (!soundon) return;
        inital();
        sound.envtime=427;
}

void addsoundbuffer()
{
        givealbuffer(soundbuf);
        return;
}


/*DMG SOUND*/

void writesound(uint32_t a, uint16_t v)
{
        rpclog("Write SOUND %08X %04X\n",a,v);
        switch (a&0x1FE)
        {
                case 0x62: /*SOUND1CNT_H*/
                sound.vol1=v>>12;
                sound.envdir1=v&0x800;
                sound.env1=(v>>8)&7;
                sound.wave1=(v>>6)&3;
                if (!sound.env1) sound.curvol1=sound.vol1;
//                rpclog("%04X %i %i %i\n",v,sound.env1,sound.vol1,sound.curvol1);
                break;
                case 0x64: /*SOUND1CNT_X*/
                sound.latch1=v&2047;
                if (v&0x8000)
                {
//                        rpclog("Restarting!\n");
                        sound.count1=2048;
                        sound.stat1=1;
                        sound.curvol1=sound.vol1;
                        sound.envcnt1=sound.env1;
                }
                break;
                case 0x68: /*SOUND2CNT_L*/
                sound.vol2=v>>12;
                sound.envdir2=v&0x800;
                sound.env2=(v>>8)&7;
                sound.wave2=(v>>6)&3;
                if (!sound.env2) sound.curvol2=sound.vol2;
//                rpclog("%04X %i %i %i\n",v,sound.env2,sound.vol2,sound.curvol2);
                break;
                case 0x6C: /*SOUND2CNT_H*/
                sound.latch2=v&2047;
                if (v&0x8000)
                {
//                        rpclog("Restarting!\n");
                        sound.count2=2048;
                        sound.stat2=1;
                        sound.curvol2=sound.vol2;
                        sound.envcnt2=sound.env2;
                }
                break;
                case 0x70:
                sound.wavebank=(v&0x40)?32:0;
                sound.wavemask=(v&0x20)?63:31;
                sound.waveset=((v&0x60)==0x60)?32:0;
                sound.waveon=v&0x80;
                break;
                case 0x72:
                switch (v>>13)
                {
                        case 0: sound.curvol3=0; break;
                        case 1: sound.curvol3=16; break;
                        case 2: sound.curvol3=8; break;
                        case 3: sound.curvol3=4; break;
                        case 4: case 5: case 6: case 7: sound.curvol3=12; break;
                }
                break;
                case 0x74:
                sound.latch3=v&2047;
                if (v&0x8000)
                {
//                        rpclog("Restarting!\n");
                        sound.count3=2048;
                        sound.stat3=sound.wavebank;
                }
                break;
                
                case 0x78: /*SOUND1CNT_L*/
                sound.vol4=v>>12;
                sound.envdir4=v&0x800;
                sound.env4=(v>>8)&7;
                break;
                case 0x7C:
                if (v&7) sound.latch4=(v&7)<<(((v>>4)&15)+1);
                else     sound.latch4=1<<((v>>4)&15);
                sound.polylen=v&4;
                if (v&0x8000)
                {
//                        rpclog("Restarting!\n");
                        sound.count4=0;
                        sound.stat4=0;
                        sound.nshift=(v&4)?0x40:0x4000;
                        sound.curvol4=sound.vol4;
                        sound.envcnt4=sound.env4;
                }
                break;
                
                case 0x90: case 0x92: case 0x94: case 0x96:
                case 0x98: case 0x9A: case 0x9C: case 0x9E:
                a=((a&0xE)*2)+(sound.wavebank^32);
//                rpclog("WaveRAM write %02X\n",a);
                waveram[a]=(v>>4)&0xF;
                waveram[a+1]=v&0xF;
                waveram[a+2]=(v>>12)&0xF;
                waveram[a+3]=(v>>8)&0xF;
                break;
        }
}
