/*
    Gamepad & memory card emulation
    ===============================

    Written by LDChen
*/

#include "fpse.h"

#define ACKPAD   sio->Delta |= DSR;
#define NACKPAD  
// sio->Status &= ~DSR; sio->Delta |= DSR;

#define B13         0x2000
#define READ_CARD   0x80

#define KPAD    1
#define KMEM    2

// This struct should be allocate in SIO_Type in the future...
typedef struct {
    int    Type;
    int    Ptr;
/* Gamepad fields */
    int    PadEnd;
    void (*Joy_PollBack)(void);
    void (*Joy_Vibrate)(int motor);
    char   Buf[16];
/* Memory card fields */
    char   MemBuf[256];
    union {
        UINT8  l[4];
        UINT32 hl;
    } Addr;
    int    EndFlg,Crc;
    char  *CardName;
    char   MemoryCard[128*1024];
} pad_type;

static pad_type *flx=NULL;
static pad_type  pad[2];
static int       nflx=0;
static char      cd1[] = {"slot1"};
static char      cd2[] = {"slot2"};
static char      memcdpath[] = { "memcards\\" };

static int MC_Event();
static int MC_VSync();

int PAD_Init(SIO_Type *sio)
{
    int  f,x;
    char buf[80];

// Prepare the struct
    memset(pad,0,sizeof(pad));

    strcpy(buf,memcdpath);
    if (FPSEIni.Mcd1Name != NULL) strcat(buf,FPSEIni.Mcd1Name);
                             else strcat(buf,cd1);
    pad[0].CardName = strdup(buf);

    strcpy(buf,memcdpath);
    if (FPSEIni.Mcd2Name != NULL) strcat(buf,FPSEIni.Mcd2Name);
                             else strcat(buf,cd2);
    pad[1].CardName = strdup(buf);

// GamePad header
    pad[0].Joy_PollBack = JOY0_Poll;
    pad[0].Joy_Vibrate  = JOY0_Vibrate;
    pad[0].PadEnd       = JOY0_InitHeader(pad[0].Buf);
    pad[1].Joy_PollBack = JOY1_Poll;
    pad[1].Joy_Vibrate  = JOY1_Vibrate;
    pad[1].PadEnd       = JOY1_InitHeader(pad[1].Buf);

    for (x=0;x<2;x++)
    {
// Memory card header
        pad[x].MemBuf[2] = 0x5A;
        pad[x].MemBuf[3] = 0x5D;

// Load memory card
        f = open(pad[x].CardName, O_RDONLY|O_BINARY);
        if (f!=-1)
        {
            read(f,pad[x].MemoryCard,128*1024);
            close(f);
        }
    }

// Handlers
    EventCallBack[INT_SIO0] = MC_Event;
    VSyncCallBack[INT_SIO0] = MC_VSync;

// Times to wait before irq
    sio->IrqSrc   = SIO_IRQ_ASYNC;
    sio->IrqFlags = 0x00000003;

    return FPSE_OK;
}

void PAD_Close(SIO_Type *sio)
{
    free(pad[0].CardName);
    free(pad[1].CardName);
    JOY0_Close();
    JOY1_Close();
}

void PAD_WriteData(SIO_Type *sio)
{
 UINT8 *buf = sio->TxBuf;
 int    v;
 UINT8  crc;

 if (flx == NULL) return;

 if (flx->Ptr == 0)
    {
        switch (*buf) {
        case 0x01:
            flx->Type = KPAD; flx->Ptr = 1;
            sio->IrqSrc   = SIO_IRQ_ASYNC;
            sio->IrqFlags = 0x00000003;
            break;
        case 0x81:
            flx->Type = KMEM; flx->Ptr = 1;
            sio->IrqSrc   = SIO_IRQ_VSYNC;
            if (compile)
                sio->IrqFlags = 0x00000001;
            else
                sio->IrqFlags = 0x00000002;
            break;
        default: break;
        }
        ACKPAD
        sio_async(sio,flx->Buf,1);
        NACKPAD
        return;
    }

 if (flx->Type == KPAD)
    {
        if (flx->Ptr == 1)
            {
                if ((*buf & 0x40) != 0x40)
                    {
                        ACKPAD
                        sio_async(sio,flx->Buf,1);
                        NACKPAD
                        flx->Ptr = 0;
                        return;
                    }
                else flx->Joy_PollBack();

            } else flx->Joy_Vibrate(*buf);
        if (flx->Ptr < flx->PadEnd-1) ACKPAD
        sio_async(sio,flx->Buf + flx->Ptr,1);
        NACKPAD
        if (++flx->Ptr >= flx->PadEnd) flx->Ptr = 0;
        return;
    }

 if ((flx->Type & 0x7F) == KMEM)
    {
        if (flx->Ptr == 1)
            {
                switch (*buf) {
                case 'R':
                    flx->Type |= READ_CARD;
                    flx->EndFlg = 140;
                    break;
                case 'W':
                    flx->EndFlg = 138;
                    break;
                default:
                    flx->Ptr = 0;
                    return;
                }
            }
        if (flx->Type & READ_CARD)
            {
                switch (flx->Ptr) {
                case 4:
#ifdef MSB_FIRST
                    flx->Addr.l[2] = *buf;
#else
                    flx->Addr.l[1] = *buf;
#endif
                    flx->MemBuf[5] = *buf;
                    flx->MemBuf[6] = 0x5C;
                    flx->MemBuf[7] = 0x5D;
                    flx->MemBuf[8] = *buf;
                    break;
                case 5:
#ifdef MSB_FIRST
                    flx->Addr.l[3] = *buf;
#else
                    flx->Addr.l[0] = *buf;
#endif
                    flx->MemBuf[9] = *buf;
                    memcpy(flx->MemBuf+10,
                           flx->MemoryCard+flx->Addr.hl*128,
                           128);
                    crc = 0; // pad[flx].Addr.l[0] ^ pad[flx].Addr.l[1];
                    for (v=8;v<128+10;v++)
                        crc ^= flx->MemBuf[v];
                    flx->MemBuf[v] = crc;
                    flx->MemBuf[v+1] = 0x47;
                    break;
                }
            }
        else {
                switch (flx->Ptr) {
                case 4:
#ifdef MSB_FIRST
                    flx->Addr.l[2] = *buf;
#else
                    flx->Addr.l[1] = *buf;
#endif
                    flx->MemBuf[135] = 0x5C;
                    flx->MemBuf[136] = 0x5D;
                    flx->MemBuf[137] = 0x47;
                    break;
                case 5:
#ifdef MSB_FIRST
                    flx->Addr.l[3] = *buf;
#else
                    flx->Addr.l[0] = *buf;
#endif
                    flx->Crc = 0; // flx->Addr.l[0] ^ flx->Addr.l[1];
                    break;
                case 134:
                    printf("caculated crc=%02x\n",flx->Crc);
                    if (*buf != flx->Crc)
                    {
                        flx->MemBuf[137] = 0x4E;
                        break;
                    }
                    memcpy(flx->MemoryCard+flx->Addr.hl*128,
                           flx->MemBuf+7,128);
                }
                if (flx->Ptr > 3 &&
                    flx->Ptr < 134)
                {
                    flx->MemBuf[flx->Ptr+1] = *buf;
                    flx->Crc ^= *buf;
                }
            }
        if (flx->Ptr < flx->EndFlg) ACKPAD
        sio_async(sio,flx->MemBuf + flx->Ptr,1);
        NACKPAD
        if (++flx->Ptr >= flx->EndFlg) flx->Ptr = 0;
//        pad[flx].ptr = 0;
//        return;
    }
}

void PAD_UpdateStatus(SIO_Type *sio)
{
 if (!(sio->Ctrl.Control16 & 2)) {
    pad[0].Ptr = pad[1].Ptr = 0;
    VSync_Register &= ~(1<<INT_SIO0);
    Event_Register &= ~(1<<INT_SIO0);
    
    flx = NULL;
    return;
 }

 if (sio->Ctrl.Control16 & B13) nflx = 1;
                           else nflx = 0;
 flx = pad + nflx;
}

static int MC_Event()
{
    VSync_Register &= ~(1<<INT_SIO0);

    return 1;
}

static int MC_VSync()
{
    Event_Register &= ~(1<<INT_SIO0);

    return 1;
}