#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <libgen.h>
#include <unistd.h>
#include <termios.h>


// 31+NULL
#define SSCMD_BYTES 32
#define SSCMD_SOF    '!'
#define SSCMD_ASEP   ':'
#define SSCMD_AREAD  '?'
#define SSCMD_AWRITE '='
#define SSCMD_AINC   '+'
#define SSCMD_ADEC   '-'
#define SSCMD_DSEP   '*'
#define SSCMD_EOF "\x0A\x0D"
#define SSCMD_DATA_BYTES 16
#define MAX_PRESETS 16
#define BCAST_ADDR 0

// Supported terminal speeds
typedef enum {BAUD9600, BAUD19200, BAUD57600} PortSpeed_t;

// SeaSense command IDs
// CAUTION: index into string table
typedef enum {ADDR=0,ADGR,BAUD,BOOT,CURV,INFO,LOUT,PSET,RSET,STAT,STBY,TEMP} SSCommandID_t;

// SeaSense Access Modes
typedef enum {READ,WRITE,INC,DEC,IMMEDIATE} SSAccessID_t;

typedef struct SSCommandDef_s{
    SSCommandID_t id;
    const char *token;
}SSCommandDef_t;

typedef struct SSAccessDef_s{
    SSAccessID_t id;
    const char *token;
}SSAccessDef_t;

typedef enum {SOF,STN,AS,CMD,DATA} SSCommandFields_t;

typedef struct SSCommand_s{
    char address;
    SSCommandID_t command;
    SSAccessID_t access;
    char data[SSCMD_DATA_BYTES];
    uint8_t checksum;
}SSCommand_t;

typedef struct SSConfig_s{
    struct termios *oterm;
    struct termios *term;
    const char *device;
    PortSpeed_t baud;
    
}SSConfig_t;

static char* SSCommands[] =
{
	"addr",
    "adgr",
    "baud",
    "boot",
    "curv",
    "info",
    "lout",
    "pset",
    "rset",
    "stat",
    "stby",
    "temp"
};

#define id2str(i) (SSCommands[i])

uint8_t ucCalculateChecksum( const char *pcCommandString );

// returns new string if dest==null
// caller owns, must release
char *cmd2str(SSCommand_t *src, char *dest)
{
    char *retval=dest;
    if (NULL == dest) {
        retval= (char *)malloc(SSCMD_BYTES*sizeof(uint8_t));
    }
    sprintf(retval,"%c%03u%c%s%c",SSCMD_SOF,src->address,SSCMD_ASEP,id2str(src->command),src->access);
    char *ip=retval+strlen(retval);
    if (NULL != src->data) {
        sprintf(ip,"%*s",(int)strlen(src->data),src->data);
        ip=retval+strlen(retval);
    }
    sprintf(ip,"%c",SSCMD_DSEP);
    ip=retval+strlen(retval);
    src->checksum = ucCalculateChecksum(retval);
    sprintf(ip,"%02X%s",src->checksum,SSCMD_EOF);
    return retval;
}

uint8_t ucCalculateChecksum( const char *pcCommandString )
{
  uint32_t uiRunningTotal = 0; 
  uint32_t i;

  /*Calculate checksum*/
  for( i = 0; i < strlen( pcCommandString ); i++) {

    // Add character to running total
    uiRunningTotal += ( uint32_t ) pcCommandString[i]; }

    // Mask the lower byte and return the value to the caller
    return (uint8_t) ( uiRunningTotal & 0xFF);
}

void printHelp(const char *pname)
{
    printf("\n");
    printf("Usage : %s '<dspl_cmd>'\n",pname);
    printf("-a : all (command)\n");
    printf("-c : checksum only\n");
    printf("-h : help\n");
    printf("\n");
}
void parseArgs(int argc, char **argv, char **cmd)
{
    int opt;
    while ((opt = getopt(argc, argv, "h")) != -1) {
        switch (opt) {
                case 'h':
            default: /* '?' */
                printHelp(argv[0]);
                exit(EXIT_FAILURE);
                break;
        }
    }
    if (optind >= argc) {
        printf("\n");
        fprintf(stderr, "Expected argument after options\n");
        printHelp(argv[0]);
        exit(EXIT_FAILURE);
    }
    *cmd=argv[optind];
    return;
}

const char *mkCmd()
{
    return NULL;
}

void showMenu()
{
    
/*
 help
 lout <addr>[,<0-100>]
 info <baddr>
 stat <addr>
 temp <addr>
 
 set lout
 inc lout
 dec lout
 get info
 get stat
 
 exec <cmd_str>

 cmd <cmd> [<addr>] [<acc>]
 addr <addr>
 acc <acc>
 data <val>[,<val>...]
 show
 send
 del
 [hist  // show previous]
 [!send // send previous]
 [queue // queue]
 
 // set address for cmd sequence
batch <addr>
 
 read    : addr, adgr, baud, boot, curv, lout, info, pset, rset, stat, stby, temp
 write   : addr, baud, boot, curv, lout, pset, rset, stby
 inc/dec : adgr, curv, lout,  pset, stby

 data: 
 curv {YMIN,YMAX,A,B,C,D,E}
 YMIN 0-100
 YMAX 0-100 YMAX>YMIN
 A 0-100 A<=B
 B 0-100 A<=B<=C
 C 0-100 B<=C<=D
 D 0-100 C<=D
 E 1-6

 lout {VALUE}
 VALUE 0-100
 
 pset {STN, FAM, MODEL, S/N, PCBVER, FWVER, LED_COLOR, BEAM_PATTERN, DIMMING }
 */
}

void getInput()
{

}

int processCmd()
{
    return -1;
}

int main(int argc, char **argv)
{
  int retval=-1;
    SSCommand_t cmd;
    cmd.address=10;
    cmd.command=ADDR;
    cmd.access=SSCMD_AREAD;
    memset(&cmd.data,0,SSCMD_DATA_BYTES);
    char cmdstr[SSCMD_BYTES]={0};
    printf("%s",cmd2str(&cmd,cmdstr));
    
    return 0;
}
