#include "archinc.h"
#include "i2cmaster.h"
#include "ee24lc256.h"
#include "timer0.h"
#include "cli.h"
#include "clichar.h"
#include "strutil.h"

#include <stdio.h>
#include <stdlib.h>

#ifdef CONFIG_INCLUDE_EEPROM_24LC256

static unsigned char slaveAddr;

static int eeprobeHandler(int argc, char * argv[], int flags);
static int eereadHandler(int argc, char * argv[], int flags);
static int eewriteHandler(int argc, char * argv[], int flags);
static int readpageHandler(int argc, char * argv[], int flags);
static int writepageHandler(int argc, char * argv[], int flags);
static int zeropageHandler(int argc, char * argv[], int flags);

static void queueOffset(unsigned short sendOffset)
{
  unsigned char tmpByte;

  tmpByte = (unsigned char)((sendOffset >> 8) & 0xff);
  i2cmasterPut(slaveAddr, tmpByte);

  tmpByte = (unsigned char)(sendOffset & 0xff);
  i2cmasterPut(slaveAddr, tmpByte);
}

unsigned long dummy;

int ee24lc256AckPoll(void)
{
  unsigned char writeLen;
  char * pOutBuf;
  int bufLen;
  int retVal;
  int i;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  for(i=0;i<10000;i++) {
    retVal = i2cmasterProbe(slaveAddr);
    if (retVal == 0) {
      break;
      }
    //timer0Delay(1);
    }

  sprintf(pOutBuf, "AckPoll cnt %d\r\n", i);
  clicharSendOutputBuffer(pOutBuf, bufLen);

  return retVal;
}

int ee24lc256WriteByte(unsigned short writeOffset, unsigned char writeData)
{
  int i;
  int retVal;

  // ack poll to make sure previous write has completed
  // ee24lc256AckPoll();

  queueOffset(writeOffset);
  i2cmasterPut(slaveAddr, writeData);
  retVal = i2cmasterSend(slaveAddr, true);

  // ack poll to make sure write has completed
  // ee24lc256AckPoll();

  return retVal;
}

int ee24lc256ReadByte(unsigned short readOffset, unsigned char * pReadData)
{
  int retStat;
  int i;
  unsigned char tmpAddr;

  // ack poll to make sure write has completed
  // ee24lc256AckPoll();
  
  queueOffset(readOffset);
  i2cmasterSend(slaveAddr, false);
  i2cmasterReceive(slaveAddr, 1, true);

  return i2cmasterGet(&tmpAddr, pReadData);
}

int ee24lc256ReadPage(unsigned short pageNum, unsigned short readLen, unsigned char *pReadData)
{
  int i;
  unsigned short readOffset = ((pageNum * 64) & 0x7fff);
  unsigned char tmpAddr;

  if ((pReadData == NULL) || (readLen < 1))
    return -1;

  // we can read the whole part in theory, but make consistent with 64-byte 'paged' writes
  if (readLen > 64)
    readLen = 64;

  // ack poll to make sure write has completed
  // ee24lc256AckPoll();

  queueOffset(readOffset);
  i2cmasterSend(slaveAddr, false);
  i2cmasterReceive(slaveAddr, readLen, true);
 
  for(i=0;i<readLen;i++) {
    i2cmasterGetData(pReadData++);
    }
  
  return 0;
}

int ee24lc256WritePage(unsigned short pageNum, unsigned short writeLen, unsigned char * pWriteData)
{
  int i;
  int retVal;
  unsigned short writeOffset = ((pageNum * 64) & 0x7fff);

  if ((pWriteData == NULL) || (writeLen < 1))
    return -1;

  if (writeLen > 64)
    writeLen = 64;

  // ack poll to make sure write has completed
  // ee24lc256AckPoll();

  queueOffset(writeOffset);

  // send bytes 0 to (n-1) with no stop condition
  for (i=0; i <writeLen; i++) {
    i2cmasterPut(slaveAddr, *pWriteData++);
    }
  retVal = i2cmasterSend(slaveAddr, true);

  // ack poll to ensure that write has completed
  ee24lc256AckPoll();

  return retVal;
}

// return pages=1, size=size-of-part if not a paged architecture
int ee24lc256GetDeviceInfo(unsigned int *pNumPages, unsigned int *pPageSize)
{
  if ((pNumPages == NULL) || (pPageSize == NULL))
    return -1;

  *pNumPages = 512;
  *pPageSize = 64;

  return 0;
}

int ee24lc256Init(unsigned char eeAddr)
{
  int retVal;
  int i;

  slaveAddr = ((eeAddr & 0x0f) | 0xa0);

  for (i=0;i<100;i++) {
    retVal = i2cmasterProbe(slaveAddr);
    if (retVal == 0)
      break;
    }
  
  cliRegisterCommand("eeprobe", eeprobeHandler, "eeprobe - look for eeprom");

  if (retVal == 0) {
    cliRegisterCommand("eeread", eereadHandler, "eeread [offset] - read a single byte");
    cliRegisterCommand("eewrite", eewriteHandler, "eewrite [offset] [value] - write a single byte");
    cliRegisterCommand("readpage", readpageHandler, "readpage [page] [length] - read a ee page");
    cliRegisterCommand("writepage", writepageHandler, "writepage [page] [length] - write a ee page");
    cliRegisterCommand("zeropage", zeropageHandler, "zeropage - clears the ee page buffer");
    }

  return retVal;
}

static int eereadHandler(int argc, char * argv[], int flags)
{
  unsigned short offset;
  unsigned char readData;
  char * pOutBuf;
  int bufLen;
  int retVal;

  if (argc != 2)
    return -1;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  offset = (unsigned short)strtol(argv[1], NULL, 16);

  retVal = ee24lc256ReadByte(offset, &readData);
  if (retVal == 0)
    sprintf(pOutBuf, "%s: read [%02x] from offset[%04x]\n", argv[0], readData, offset);
  else
    sprintf(pOutBuf, "%s: read ERROR from offset[%04x]\n", argv[0], offset);
 
  clicharSendOutputBuffer(pOutBuf, bufLen);

  return retVal;  
}

static int eeprobeHandler(int argc, char * argv[], int flags)
{
  char * pOutBuf;
  int bufLen;
  int retVal;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  retVal = i2cmasterProbe(slaveAddr);

  sprintf(pOutBuf, "%s: eeprom %s at [%02x]\n", argv[0], ((retVal == 0) ? "found" : "not found"), slaveAddr);
 
  clicharSendOutputBuffer(pOutBuf, bufLen);
  
  return 0;  
}

static int eewriteHandler(int argc, char * argv[], int flags)
{
  unsigned short offset;
  unsigned char writeData;
  char * pOutBuf;
  int bufLen;
  int retVal;

  if (argc != 3)
    return -1;

  pOutBuf = clicharGetOutputBuffer(&bufLen);
  offset = (unsigned short)strtol(argv[1], NULL, 16);
  writeData = (unsigned char)strtol(argv[2], NULL, 16);

  sprintf(pOutBuf, "%s: writing [%02x] to offset[%04x]\n", argv[0], writeData, offset); 
  clicharSendOutputBuffer(pOutBuf, bufLen);

  return ee24lc256WriteByte(offset, writeData);
}

static unsigned char eePageBuffer[64];

static int readpageHandler(int argc, char * argv[], int flags)
{
  unsigned short readPage;
  unsigned char readLen;
  char * pOutBuf;
  int bufLen;
  int retVal;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  if (argc == 3)
    readLen =  (unsigned char)strtol(argv[2], NULL, 10);
  else if (argc == 2)
    readLen = 64;
  else 
    return -1;

  readPage = (unsigned short)strtol(argv[1], NULL, 10);

  if ((readLen > 64) || (readLen <= 0))
    readLen = 64;

  retVal = ee24lc256ReadPage(readPage, readLen, eePageBuffer);

  if (retVal != 0) {
    sprintf(pOutBuf, "%s: error reading [%02x] bytes from page[%04x]\n", argv[0], readLen, readPage);
    clicharSendOutputBuffer(pOutBuf, bufLen);
    }
  else {
    strutilHexdump((char *)eePageBuffer, 64);
    }

  return retVal;  
}

static int writepageHandler(int argc, char * argv[], int flags)
{
  unsigned short writePage;
  unsigned char writeLen;
  char * pOutBuf;
  int bufLen;
  int retVal;
  int i;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  if (argc == 3)
    writeLen =  (unsigned char)strtol(argv[2], NULL, 10);
  else if (argc == 2)
    writeLen = 64;
  else 
    return -1;

  writePage = (unsigned short)strtol(argv[1], NULL, 10);

  if ((writeLen > 64) || (writeLen <= 0))
    writeLen = 64;

  for(i=0; i<writeLen; i++)
    eePageBuffer[i] = (unsigned char)(i+1);

  retVal = ee24lc256WritePage(writePage, writeLen, eePageBuffer);

  if (retVal != 0) {
    sprintf(pOutBuf, "%s: ERROR writing [%u] bytes to page[%u]\n", argv[0], writeLen, writePage);
    }
  else {
    sprintf(pOutBuf, "%s: [%u] bytes written to page[%u]\n", argv[0], writeLen, writePage);
    }

  clicharSendOutputBuffer(pOutBuf, bufLen);

  return retVal;    
}

static int zeropageHandler(int argc, char * argv[], int flags)
{
  int i;
  for(i=0;i<64;i++)
    eePageBuffer[i] = 0x00;

  return 0;
}

#endif /* CONFIG_INCLUDE_EEPROM_24LC256 */

