/* -*- C -*- ****************************************************************
 *
 *  System        : 
 *  Module        : 
 *  Object Name   : $RCSfile$
 *  Revision      : $Revision$
 *  Date          : $Date$
 *  Author        : $Author$
 *  Created By    : <unknown>
 *  Created       : Fri Jun 28 09:44:19 2019
 *  Last Modified : <230314.1251>
 *
 *  Description	
 *
 *  Notes
 *
 *  History
 *	
 ****************************************************************************
 *
 *  Copyright (c) 2019 <unknown>.
 * 
 *  All Rights Reserved.
 * 
 * This  document  may  not, in  whole  or in  part, be  copied,  photocopied,
 * reproduced,  translated,  or  reduced to any  electronic  medium or machine
 * readable form without prior written consent from <unknown>.
 *
 ****************************************************************************/

static const char rcsid[] = "@(#) : $Id$";


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <signal.h>
#include <time.h>
#include <errno.h>

FILE *fp = 0;

int debug = 1;
int haveSignal = 0;

#define SAVE_DATA 1
#define VALIDATE_DATA 2

#define IO_IDLE 0
#define IO_HAVE_PREFIX 1
#define IO_HAVE_MSB 2
#define IO_READ 3
#define IO_WRITE 4

void sigExitHandler(int sig, siginfo_t *sigInfo, void* voidP)
{
    haveSignal = 1;
}

void
setExit(void)
{
    sigset_t sigSet;
    struct sigaction sigAction, oldSigAction;
    
    haveSignal = 0;
    
    sigemptyset(&sigSet);
    sigaddset(&sigSet,SIGINT);
    sigaddset(&sigSet,SIGTERM);
    sigaddset(&sigSet,SIGQUIT);
    sigaddset(&sigSet,SIGKILL);
    sigaddset(&sigSet,SIGHUP);
    sigaddset(&sigSet,SIGPIPE);
    
    sigAction.sa_handler = 0;
    sigAction.sa_sigaction = &sigExitHandler;
    sigAction.sa_mask = sigSet;
    sigAction.sa_flags = SA_NODEFER;
    
#ifndef BSD
    sigAction.sa_restorer = 0;
#endif
    haveSignal = 0;
    
    sigaction(SIGINT,&sigAction,&oldSigAction);
    sigaction(SIGTERM,&sigAction,&oldSigAction);
    sigaction(SIGQUIT,&sigAction,&oldSigAction);
    sigaction(SIGKILL,&sigAction,&oldSigAction);
    sigaction(SIGHUP,&sigAction,&oldSigAction);
    sigaction(SIGPIPE,&sigAction,&oldSigAction);
}

#define CTIMEBUFFERS (256)
char ctimeBuf[CTIMEBUFFERS][64];
int ctimeBufIdx = 0;

/*
 * CTime returns a readable time string.
 * Result is stored in a round-robin
 * buffer, so that one can invoke this
 * couple times in a row, with each in-
 * vocation overwriting previous results.
 */

const char *
CTime(time_t theTime)
{
    char *result, *cp;
    char *timeString = ctime(&theTime);
    strcpy(result = ctimeBuf[ctimeBufIdx],timeString);
    if (cp = strchr(result,'\n'))
        *cp = 0;
    ctimeBufIdx = ++ctimeBufIdx & 255;
    return result;
}



void
setRaw(int fd, struct termios *options, int bitRate)
{
    /*
     * set port parameters, 9600 BPS */
    struct termios newOptions;
    tcgetattr(fd,options);
    tcgetattr(fd,&newOptions);
    cfsetospeed(&newOptions,bitRate);
    cfsetispeed(&newOptions,bitRate);
    
    /* configure raw mode (no interpretation of anything ever) */
    cfmakeraw(&newOptions);
    
    newOptions.c_cflag |= (CLOCAL | CREAD | CSTOPB | CS8);
    newOptions.c_iflag |= IGNPAR;
    newOptions.c_oflag |= (OPOST | ONLCR);
    
    /* set parameters immediately */
    tcsetattr(fd,TCSANOW,&newOptions);
}


void
prevSetRaw(int fd, struct termios *options)
{
    /*
     * set port parameters, 9600 BPS */
    struct termios newOptions;
    tcgetattr(fd,options);
    tcgetattr(fd,&newOptions);
    cfsetospeed(&newOptions,B9600);
    cfsetispeed(&newOptions,B9600);
    
    /* configure raw mode (no interpretation of anything ever) */
    cfmakeraw(&newOptions);
    
    newOptions.c_cflag |= (CLOCAL | CREAD | CSTOPB);
    newOptions.c_iflag |= IGNPAR;
    
    /* set parameters immediately */
    tcsetattr(fd,TCSANOW,&newOptions);
}



void
setCooked(int fd, struct termios *options)
{
    /* set parameters immediately */
    tcsetattr(fd,TCSANOW,options);
}

int
isPrefix(const char *haystack, const char *needle)
{
    if (!needle || !*needle)
        return 1;
    if (!haystack || !*haystack)
        return 0;
    return !strncmp(haystack,needle,strlen(needle));
}


char lineInBuf[32768], lineIn[1024], lineFrag[256], lineOut[1024], spaces[1200], hexSent[256];

int
main(int argc, char *argv[], char *envp[])
{
    int fd0, i, k, nc, areWeThereYet, inputReady;
    int lineOutLen, lineInBufLen, lineInBufAvail, fd;
    struct termios sioOptions, termOptions;
    fd_set readfds;
    struct timeval timeout;
    int lockCount, sendOk;
    const char *devName;
    int btCount, dskIo, prevDskIo,lineCt;
    unsigned int blkNum;
    time_t startTime, endTime;
    char fname[64], oneLine[65], *cp;
    struct stat statBuf;
    char dskBuf[1024];
    int bitRate = B9600;
    
    for (i=0;i<sizeof(spaces);i++)
        spaces[i] = ' ';
    spaces[sizeof(spaces)-1]=0;
    
    /* setExit(); */
    haveSignal = 0;
    
    prevDskIo = dskIo = IO_IDLE;
    
    devName = "/dev/cuaa0";
    if (argc > 1)
    {
        if (isPrefix(argv[1],"/dev/"))
            devName=argv[1];
        else if (argv[1][0] == '2')
            bitRate = B2400;
    }
    if (argc > 2 && argv[2][0] == '2')
        bitRate = B2400;
    
    fd0 = open(devName, O_RDWR | O_EXCL | O_NOCTTY | O_NDELAY);
    if (fd0 < 0)
    {
        fprintf(stderr,"Error: %s cannot open %s, errno = %d / %s\n",
                argv[0],devName,errno,strerror(errno));
        exit(1);
    }
    
    lockCount = 0;
    errno = 0;
    while (flock(fd0,LOCK_EX | LOCK_NB) && errno == EINTR)
    {
        if (lockCount++ > 12)
        {
            fprintf(stderr,"%s @ %d, cannot aquire lock on /dev/ttyACM0, exiting\n",
                    __FILE__,__LINE__);
            close(fd0);
            exit(1);
        }
        fprintf(stderr,"%s @ %d, /dev/ttyACM0 is in use by another process, retrying in 5\n",
                __FILE__,__LINE__);
        sleep(5);
        errno = 0;
    }
    
    setRaw(fd0, &sioOptions, bitRate);
    setRaw(0,   &termOptions, bitRate);
    
    areWeThereYet = 0;
    
    /* serial setup done, now for the fancy graphics */
    
    fp = fopen("sioloop.log","w");
    
    lineInBuf[0] = 0;
    
    while (!areWeThereYet)
    {
        FD_ZERO(&readfds);
        FD_SET (fd0,&readfds);
        FD_SET (0,  &readfds);
        
        timeout.tv_sec = 1;
        timeout.tv_usec = 0;
        
        if (select(fd0+1,&readfds,0,0,&timeout) > 0)
        {
            lineIn[0] = 0;
            if (FD_ISSET(fd0,&readfds))
            {
                nc = read(fd0,&lineInBuf[0],sizeof(lineInBuf));
                /* fprintf(stderr,"%s @ %d, have %d on fd0\r\n",__FILE__,__LINE__,nc); */
                if (nc > 0)
                {
                    for (i=0;i<nc;i++)
                    {
                        prevDskIo = dskIo;
                        switch (dskIo)
                        {
                        case IO_IDLE:
                            if (lineInBuf[i] == 0 || lineInBuf[i] == 3)
                            {
                                dskIo = IO_HAVE_PREFIX;
                                if (debug)
                                    fprintf(stderr,"\r\n%s @ %d in %s, esc: %02x, dskIo=%d\r\n",
                                            __FILE__,__LINE__,__FUNCTION__,lineInBuf[i],dskIo);
                            }
                            else
                                write(1,&lineInBuf[i],1);
                            break;
                        case IO_HAVE_PREFIX:
                            blkNum = lineInBuf[i] << 8;
                            dskIo = IO_HAVE_MSB;
                            if (debug)
                                fprintf(stderr,"%s @ %d in %s, dskIo=%d,blkNum=%d\r\n",
                                        __FILE__,__LINE__,__FUNCTION__,dskIo,blkNum);
                            break;
                        case IO_HAVE_MSB:
                            blkNum |= lineInBuf[i];
                            btCount = 0;
                            if (blkNum & 0x8000)
                                dskIo = IO_WRITE;
                            else
                                dskIo = IO_READ;
                            blkNum &= 0x7fff;
                            if (debug)
                                fprintf(stderr,"%s @ %d in %s, dskIo=%d, blkNum=%d\r\n",
                                        __FILE__,__LINE__,__FUNCTION__,dskIo,blkNum);
                            if (dskIo  == IO_WRITE)
                                break;
                        case IO_READ:
                            startTime = endTime = time(0);
                            /* if (debug) */
                            printf("\r\n%s @ %d in %s, starting to send blk#%d\r\n",
                                   __FILE__,__LINE__,__FUNCTION__,blkNum);
                            
                            sendOk = 0;
                            
                            sprintf(fname,"%05d.blk",blkNum);
                            if (!stat(fname,&statBuf))
                            {
                                if (statBuf.st_size == 1024)
                                {
                                    fd = open(fname,O_RDONLY);
                                    if (fd >= 0 && read(fd,&dskBuf[0],1024) == 1024)
                                    {
                                        write(fd0,&dskBuf[0],1024);
                                        sendOk = 1;
                                        close(fd);
                                    }
                                }
                            }
                            
                            if (!sendOk)
                            {
                                sprintf(fname,"%d.4th",blkNum);
                                fp = fopen(fname,"r");
                                fprintf(stderr,"\r\%s @ %d in %s: fname=%s\r\n",
                                        __FILE__,__LINE__,__FUNCTION__,fname);
                                if (fp)
                                {
                                    lineCt = 0;
                                    while (fgets(oneLine,64,fp))
                                    {
                                        oneLine[64] = 0;
                                        cp = strchr(oneLine,'\r');
                                        if (cp)
                                            *cp = 0;
                                        cp = strchr(oneLine,'\n');
                                        if (cp)
                                            *cp = 0;
                                        if (oneLine[0])
                                        {
                                            fprintf(stderr,"\r\n%s @ %d in %s, sending %s\r\n",
                                                    __FILE__,__LINE__,__FUNCTION__,oneLine);
                                            write(fd0,oneLine,strlen(oneLine));
                                            if (strlen(oneLine) < 64)
                                                write(fd0,spaces,64 - strlen(oneLine));
                                            if (++lineCt >= 16)
                                                break;
                                        }
                                    }
                                    fclose(fp);
                                    for (;lineCt < 16;lineCt++)
                                        write(fd0,spaces,64);
                                    sendOk = 1;
                                }
                            }
                            
#if 0
                            for (k=0;k<256;k++)
                            {
                                write(fd0,"    ",2);
                                usleep(500);
                            }
#endif
                            if (!sendOk)
                                write(fd0,&spaces[0],1024);
                            
                            write(fd0,"\r",1);
#if 0
                            for (k=0;k<1024;k++)
                            {
                                write(fd0,&spaces[k],1);
                                /* if (!(k&255)) usleep(1000); */
                            }
#endif
                            endTime = time(0);
                            if (debug)
                                printf("\r\n%s @ %d in %s, done sending blk#%d, took %d seconds\r\n",
                                       __FILE__,__LINE__,__FUNCTION__,blkNum,endTime - startTime);
                            dskIo = IO_IDLE;
                            if (debug)
                                fprintf(stderr,"%s @ %d in %s, dskIo=%d\r\n",
                                        __FILE__,__LINE__,__FUNCTION__,dskIo);
                            break;
                        case IO_WRITE:
                            if (!btCount)
                            {
                                if (debug)
                                    fprintf(stderr,"%s @ %d in %s, dskIo=%d, blk#%d, char: %02x\r\n",
                                            __FILE__,__LINE__,__FUNCTION__,dskIo,blkNum,lineInBuf[i]);
                                sprintf(fname,"%05d.blk",blkNum);
                                fd = open(fname,O_WRONLY | O_CREAT | O_TRUNC,0644);
                            }
                            if (debug && btCount > 1020)
                                fprintf(stderr,"%s @ %d in %s, dskIo=%d, blk#%d, btCount: %d\r\n",
                                        __FILE__,__LINE__,__FUNCTION__,dskIo,blkNum,btCount);
                            if (fd >= 0)
                            {
                                write(fd,&lineInBuf[i],1);
                                usleep(100);
                            }
                            if (++btCount >= 1024)
                            {
                                usleep(50000);
                                write(fd0," ",1);
                                dskIo = IO_IDLE;
                                if (debug)
                                    fprintf(stderr,"\r\n%s @ %d in %s, done with IO_WRITE\r\n",
                                            __FILE__,__LINE__,__FUNCTION__);
                            }
                            break;
                        default:
                            fprintf(stderr,"\r\n%s @ %d in %s, unknown mode %d\r\n",
                                    __FILE__,__LINE__,__FUNCTION__,dskIo);
                            break;
                        }
                    }
                }
            }
            
            if ((FD_ISSET(0,&readfds)))
            {
                nc = read(0,&lineFrag[0],sizeof(lineFrag));
                
                if (nc > 0)
                {
                    /* fprintf(stderr,"%s @ %d, have %d on 0\r\n",__FILE__,__LINE__,nc); */
                    for (i=0;i<nc;i++)
                        if (lineFrag[i] == 27)
                            areWeThereYet=1;
                    if (!areWeThereYet)
                    {
                        /* nc = write(fd0,&lineFrag[0],nc); */
                        for (k=0;k<nc;k++)
                        {
                            write(fd0,&lineFrag[k],1);
                            usleep(200);
                        }
                        /* fprintf(stderr,"%s @ %d, sent %d to fd0\r\n",__FILE__,__LINE__,nc); */
                    }
                }
            }
        }
    }
    
    setCooked(fd0, &sioOptions);
    setCooked(0,   &termOptions);
    
    close(fd0);
    return 0;
}

