#include <string.h>
#include "cache.h"
#include "cardinfo.h"
#include "dig_io.h"
#include "hash.h"
#include "slot.h"
#include "spi.h"
#include "sys_defs.h"

#define SLOT_ID_MSG 0x40
#define SLOT_NAME_LEN 4
#define SLOT_TIMEOUT 1000

CardInfo slotInfo[NUM_SLOTS];
    
char msg_buff[64];

int slotUpdateInterval = 1000;

int slotSendRecv(unsigned int addr, unsigned int* recv)
{
    unsigned int i, rd = 0;
    
    if (recv == NULL)
    {
        return 2;
    }
    
    for (i = 0; i < SLOT_TIMEOUT && rd == 0; i++)
    {
        SPI1STATbits.SPIROV = 0;
        rd = spiSlaveRead(addr, INFO_CMD);
    }
    *recv = rd;
    
    if (i >= SLOT_TIMEOUT)
    {
        return 1;
    }
    
    return 0;
}

int slotGetInfo(unsigned char slot)
{   
    unsigned int i, recv, offset;
    
    char* nameBuf;
    unsigned char len;
    unsigned char inout;
    unsigned char hash;
    unsigned char colSlot;
    
    int err;
    
    slotSelect(slot);
    
    /* Read in CardInfo struct */
    for (i = 0; i < sizeof(CardInfo) - 1; i++)
    {
        if ((err = slotSendRecv(i, &recv)))	
        {           
            memset(&slotInfo[slot], 0, sizeof(CardInfo));
            slotInfo[slot].typeId = TYPE_OPEN;
            return err;
        }
        
        *((unsigned char*)&slotInfo[slot] + i) = recv & ARG_MASK;
    }

    /* Check if recieved data is valid */
    if (slotInfo[slot].typeId > TYPE_OPEN) {
        memset(&slotInfo[slot], 0, sizeof(CardInfo));
        slotInfo[slot].typeId = TYPE_OPEN;
        return 3;
    }
    
    /* Apply variables to cache */
    offset = 0;
    for (i = 0; i < slotInfo[slot].varCount; i++)
    {
        nameBuf = slotInfo[slot].varNames + offset;
        len = strlen(nameBuf);        
        hash = hashCode(nameBuf, len);
        
        offset += len;
        inout = slotInfo[slot].varNames[++offset];
        offset++;
        
        /* Check for collisions in the cache */
        do {
            cacheGetEntry(hash, 0, &colSlot, 0);
            if (colSlot != TYPE_OPEN)
            {
                /* Insert into the next available bucket */
                hash++;
            }
        }
        while (colSlot != TYPE_OPEN);
        
        /* Apply to cache */
        cacheSetEntry(hash, 0xFFFF, slot, inout);
    }

    return 0;
}

void slotInit()
{
    unsigned char i;
    int err = 0;
    
    /* Retrieve daughtercard information */
    for (i = 0; i < NUM_SLOTS; i++)
    {
        switch (err = slotGetInfo(i)) {
            case 1:
                sprintf(msg_buff, "Slot %d timed out.  ", i);
                serPutString(SER_CONSOLE, msg_buff);
                break;
            case 3:
                sprintf(msg_buff, "Data from slot %d corrupted.  ", i);
                serPutString(SER_CONSOLE, msg_buff);
                break;
        }
        if (err) {
            serPutString(SER_CONSOLE, "Assigned as OPEN\r\n");
        }
		else
		{
			sprintf(msg_buff, "Slot %d recognized. Assigned as CLOSED.\r\n", i);
			serPutString(SER_CONSOLE, msg_buff);
		}
    }
}

void slotSet(unsigned char slot)
{
    switch(slot)
	{
		case 0:
		{
			digSetOutBit(0,1);  // Gate
			digSetOutBit(1,0);  // Add 0
			digSetOutBit(2,0);  // Add 1
			digSetOutBit(3,0);	// Add 2
			break;
		}
		case 1:
		{
			digSetOutBit(0,1);  // Gate
			digSetOutBit(1,1);  // Add 0
			digSetOutBit(2,0);  // Add 1
			digSetOutBit(3,0);	// Add 2
			break;
		}

		case 2:
		{
			digSetOutBit(0,1);  // Gate
			digSetOutBit(1,0);  // Add 0
			digSetOutBit(2,1);  // Add 1
			digSetOutBit(3,0);  // Add 2
			break;
		}

		case 3:
		{
			digSetOutBit(0,1);  // Gate
			digSetOutBit(1,1);  // Add 0
			digSetOutBit(2,1);  // Add 1
			digSetOutBit(3,0);	// Add 2
			break;
		}
		case 4:
		{
			digSetOutBit(0,1);  // Gate
			digSetOutBit(1,0);  // Add 0
			digSetOutBit(2,0);  // Add 1
			digSetOutBit(3,1);  // Add 2
			break;
		}
	
		case 5:
		{
			digSetOutBit(0,1);  // Gate
			digSetOutBit(1,1);  // Add 0
			digSetOutBit(2,0);  // Add 1
			digSetOutBit(3,1);	// Add 2
			break;
		}
		case 6:
		{
			digSetOutBit(0,1);  // Gate
			digSetOutBit(1,0);  // Add 0
			digSetOutBit(2,1);  // Add 1
			digSetOutBit(3,1);	// Add 2
			break;
		}
		case 7:
		{
			digSetOutBit(0,1);  // Gate
			digSetOutBit(1,1);  // Add 0
			digSetOutBit(2,1);  // Add 1
			digSetOutBit(3,1);  // Add 2
			break;
		}
		default: 
		{
			digSetOutBit(0,0);  // Gate
			digSetOutBit(1,0);  // Add 0
			digSetOutBit(2,0);  // Add 1
			digSetOutBit(3,0);	// Add 2
			break;
		}
	}
}

void slotSelect(unsigned char slot)
{
    slotSet(slot);
    spiCS(1);
}

void slotDeselect()
{
    slotSet(-1);
    spiCS(0);
}

unsigned char slotGetType(unsigned char slot)
{
    if (slot < NUM_SLOTS) {
        return slotInfo[slot].typeId;
    }
    return 0;
}

const char* slotGetName(unsigned char slot)
{
    if (slot < NUM_SLOTS)
    {
        const char* ret = slotInfo[slot].name;
        return ret;
    }
    
    return 0;
}

const char* slotDisplayName(unsigned char type)
{
    switch (type)
    {
        case TYPE_OPEN:
            return "Open";
        case TYPE_CPU:
            return "CPU";
        case TYPE_PS:
            return "Power Supply";
        case TYPE_LVLS:
            return "LVLS";
        case TYPE_HVLS:
            return "HVLS";
        case TYPE_ENVIRON:
            return "Environment";
        case TYPE_GFDET:
            return "GFDET";
        case TYPE_UNASS1: case TYPE_UNASS2: case TYPE_UNASS3:
            return "Unassigned";
        default:
            return "Unknown";
    }
    
    return 0;
}

void slotDumpInfo(unsigned char slot)
{
    if (slot < NUM_SLOTS)
    {
        cardInfoDump(&slotInfo[slot]);
    }
}

void slotCardUpdate(CardInfo *card)
{
    unsigned int i;
    unsigned int len;
    unsigned char hash;
    unsigned char io;
    unsigned int offset = 0;
    
    unsigned int dat;
    
    /* Iterate through variable names */
    for (i = 0; i < card->varCount; i++)
    {
        len = strlen(card->varNames + offset);
        hash = hashCode(card->varNames + offset, len);
        offset += len;
        io = card->varNames[++offset];
        offset++;
        
        switch (io)
        {
            case CACHE_IO_OUT:
                /* Update value */
                dat = spiSlaveRead(hash, READ_CMD);
                cacheValues[hash] = dat;
                break;
            case CACHE_IO_IN:
                spiSlaveWrite(hash, cacheValues[hash]);
                break;
        }
    }
}

void slotUpdate()
{
    unsigned int i;
    for (i = 0; i < NUM_SLOTS; i++)
    {
        if (slotInfo[i].typeId != TYPE_OPEN)
        {
            slotSelect(i);
            slotCardUpdate(&slotInfo[i]);
            slotSelect(99);
        }
    }
}
