/*
 * picoph.c
 * Library for interface with picopH
 *
 *  Created on: Jul 28, 2022
 *      Author: Irene Hu
 */

#include "picoph.h"
#include "main.h"

//#define CTRL_X 24 // as in system.h

struct picophDriver picoph;   // struct defd in picoph.h

extern unsigned char uart3_prn_buf[];

//******************************************************************************
//* Initialize and UART wrapper
//******************************************************************************

void picoph_init(void)
// similar to other devices, init function now only sets up the uart port
// Uses UART3 (same as Optode) on J7 MFET or MPHOX
{
    uart3_init();

    uart3_setup(PICO_BAUD, 16000000);       //defd in picoph.h

    uart3_echo_set(0);
}

void picoph_open(void)
// applies power and initializes driver variables
{
    // on MFET, dual rs232 level translator is enabled for Optode J7 and Console J6 by console_enable/shutdown

#if BOARD_MFET >= 2  || BOARD_MPHOX >= 1  || BOARD_MSC == 2
       // turn on 5v supply on J7 MFET rev C, MPHOX rev A
       MAP_GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_2, 0xff);    // PL2, pin 106 is 5vON
#endif

   //picoph.dataCnt = 1;
   picoph.state = 0;
   picoph.bufIndx = 0;

}

void picoph_close(void)
{

#if BOARD_MFET >= 2  || BOARD_MPHOX >= 1  || BOARD_MSC == 2
    // turn off 5v supply on J7 MFET rev C, MPHOX rev A
    MAP_GPIOPinWrite(GPIO_PORTL_BASE, GPIO_PIN_2, 0x00);    // PL2, pin 106 is 5vON
#endif

}

unsigned char picoph_getc(void)
{
    return(uart3_rx_byte());
}

void picoph_putc(unsigned char c)
{
    uart3_tx_byte(c);
}

int picoph_rxBytesAvail(void)
{
    return(uart3_rx_bytes_avail());
}

void picoph_flushRx(void)
{
    uart3_rx_bufr_flush();
}

void picoph_flushTx(bool bDiscard)
{
    uart3_tx_bufr_flush(bDiscard);
}

int picoph_write(const char *pcBuf, uint32_t ui32Len)
{
    return(uart3_write(pcBuf, ui32Len));
}

void picoph_tx_str(void)
{
    uprintf("%s", uart3_prn_buf);
}

int picoph_rx_buf_size(void)
{
    return(UART3_RX_BUFFER_SIZE);
}

int picoph_tx_buf_size(void)
{
    return(UART3_TX_BUFFER_SIZE);
}

int picoph_buf_write(unsigned char inChar)
{
    uart3_prn_buf[picoph.bufIndx] = inChar;
    picoph.bufIndx++;
    if(picoph.bufIndx >= picoph_tx_buf_size())
    {
        uprintf("\r\nDEBUG: error in picoph driver, bufIndx exceeds buf size. picoph.c\r\n");

        picoph.bufIndx = picoph_tx_buf_size() - 1;
        if(sys_data.diag > 0) uprintf("\r\nDEBUG: error in picoph driver, bufIndx exceeds buf size. picoph.c\r\n");
        wait_consoleTx();
        return(0);      // return of zero is buffer overflow error
    }
    return(1);
}

//******************************************************************************
//* User methods
//******************************************************************************


int picoph_stateMachine(int picoState)
// pass in state=1 to start it, check for finish when returned state value is 0
// based on optode and ocr state machines
// more closely follows ocr, which has separate states for transitions (eg sending command)
// rather than optode which has more back and forth with the instrument
{
    //int nextState = 0;
    //unsigned int indx;
    //unsigned int tick1, tick2;
    //unsigned int wait_ms;
    unsigned char inChar;


    // example usage:
    //picoState = 1;
    //picoState = picoph_statemachine(picoState);     //pass in 1 to start the state machine, hereafter pass retVal
    //call statemachine repeatedly until picoState == 0

    // turn on (picoph_open)
    // wait some ms (defined in picoph.h)
    // send poll cmd string (terminate with cr)
    // look for output terminated by cr and # within 2000msec
    // parse
    // close
    // look at retVal, if retVal=-1, that's a timeout, then terminate statemachine by sending it a 0 as arg

    // pico open (turn on power, wait 1msec, flush rx and tx bufs

#define ST_NADA         0
#define ST_INIT         1
#define ST_PWRON_DELAY  2
#define ST_REQ_DATA     3
#define ST_RX_START     4
#define ST_RX_DATA      5
//#define ST_REPEAT       6
#define ST_CLOSE        7

    picoph.state = picoState;    // for debugging, picoph.state not used at this time.

    switch(picoState)
    {
        case ST_NADA:
            // do nothing, caller needs to pass in state=1 to start things
            return(ST_NADA);

        case ST_INIT:
            picoph_init();         // moved from init()
            picoph_open();         // power on

            MAP_SysCtlDelay(MILLISECOND*1);  // power stable for 1mec (TODO, figure 'right' delay)


            picoph.old_tick10ms = Timer_10ms();

            //MAP_SysCtlDelay(MILLISECOND*750);

            picoph.state = ST_PWRON_DELAY;
            return(ST_PWRON_DELAY);

        case ST_PWRON_DELAY:  // number defined in picoph.h
            picoph.new_tick10ms = Timer_10ms();
            if((picoph.new_tick10ms - picoph.old_tick10ms) >= PICO_STARTUP_MS/10)
            {
                picoph_flushRx(); // bytes are appearing - flush after startup
                picoph_flushTx(true);   // true says discard

                picoph.old_tick10ms = picoph.new_tick10ms;
                picoph.state = ST_REQ_DATA;
                return(ST_REQ_DATA);
            }
            picoph.state = ST_PWRON_DELAY;
            return(ST_PWRON_DELAY);

        case ST_REQ_DATA:
            //send MEA command to trigger measurement

            // debug
            //if( picoph_rxBytesAvail() )
            //    uprintf("Pico bytes are available\r\n");

            picoph_write(PICO_GETMEASCMD,strlen(PICO_GETMEASCMD));

            picoph.state = ST_RX_START;
            return(ST_RX_START);

        case ST_RX_START:
            // receive:   MEA 1 blahbalh
            // timeout defined in picoph.h
            if( picoph_rxBytesAvail() )
            {
                // debug
                //uprintf("Pico bytes are available\r\n");

                inChar = picoph_getc();
                //uprintf( "[%#02x]", inChar );   // DEBUG
                if(inChar == 'M')
                {
                    picoph.old_tick10ms = Timer_10ms();

                    picoph.bufIndx = 0;
                    uart3_prn_buf[picoph.bufIndx++] = inChar;

                    picoph.state = ST_RX_DATA;
                    return(ST_RX_DATA);
                }
            }

            // statemachine resilience - call as slow as once per 1.5sec in fast_sample
            picoph.new_tick10ms = Timer_10ms();
            if((picoph.new_tick10ms - picoph.old_tick10ms) >= PICO_TIMEOUT_MS/10)
            {
                if(sys_data.diag > 0) uprintf("\r\nPico-pH did not send expected response to measure command in %d msec - check connection or setup\r\n",PICO_TIMEOUT_MS);
                sprintf((char *)&uart3_prn_buf[0], "Pico-pH not responding to MEAS command");
                wait_consoleTx();
                picoph_close();
                picoph.state = ST_NADA;
                return(ST_NADA);
            }
            picoph.state = ST_RX_START;
            return(ST_RX_START);        // return to same state

        case ST_RX_DATA:
            // receive the data string from the pico, terminated by CR
            if( picoph_rxBytesAvail() )
            {
                inChar = picoph_getc();
                //uprintf( "(%02x)", inChar );

                // check for terminating character
                if(inChar == 0x0d)
                {
                    // finished receiving data from pico
                    uart3_prn_buf[picoph.bufIndx] = 0x00;
                    picoph.old_tick10ms = Timer_10ms();
                    picoph.state = ST_CLOSE;
                    return(ST_CLOSE);
                }

                // replace space delimiter by tab delim
                if (inChar == 0x20)
                    inChar = '\t';

                if(!picoph_buf_write(inChar))
                {
                    picoph_close();  // Close NanoFET, power off
                    picoph.state = ST_NADA;     //-1;
                    return(ST_NADA);
                }

#ifdef OLDCODE
                uart3_prn_buf[picoph.bufIndx++] = inChar;

                if(picoph.bufIndx >= picoph_buf_size())        //PICO_BUFFER_SIZE)
                {
                    picoph.bufIndx = picoph_buf_size() - 1;
                    if(sys_data.diag > 0) uprintf("\r\nDEBUG: error in pico-pH driver bufIndx exceeds buf size, picoph.c\r\n");
                    wait_consoleTx();
                    picoph_close();  // Close pico, power off
                    picoph.state = ST_NADA;
                    return(ST_NADA);
                }
#endif

            }

            picoph.new_tick10ms = Timer_10ms();
            if((picoph.new_tick10ms - picoph.old_tick10ms) >= PICO_TIMEOUT_MS/10)
            {
                if(sys_data.diag > 0) uprintf("\r\nERROR: pico-pH did not finish transmitting data, timeout\r\n");
                sprintf((char *)&uart3_prn_buf[0], "pico-pH did not finish transmit data");
                wait_consoleTx();
                picoph_close();  // Close, power off
                picoph.state = ST_NADA;
                return(ST_NADA);
            }
            picoph.state = ST_RX_DATA;
            return(ST_RX_DATA);     // return to same state

        /*
        case ST_REPEAT:
            if(picoph.dataCnt > 0)
            {
                picoph.dataCnt--;
                if(picoph.dataCnt > 0)
                {
                    picoph.state = ST_REQ_DATA;
                    return(ST_REQ_DATA);
                }
            }

            picoph_close();  // Close Optode, power off
            picoph.state = ST_LOSE;
            return(ST_CLOSE);
        */

        case ST_CLOSE:
            picoph_close();  // Close Optode, power off
            picoph.state = ST_NADA;
            return(ST_NADA);

        default:
            uprintf("\r\nERROR: Pico-pH statemachine error - should not get to default\r\n");
            picoph_close();
            picoph.state = ST_NADA;
            return(ST_NADA);



    }


}

uint32_t picoph_qa_test(void)
{

    int picophState;

    //start the picoph driver by passing 1, when complete, the statemachine returns 0
    picophState = 1;
    picophState = picoph_stateMachine(picophState);     //pass in 1 to start the state machine, hereafter pass retVal


    while(1)
    {
        if(picophState == 0)            // makes sure picoph statemachine is finished
        {
            if(picoph.state == -1)
                return(0);              // fail
            else
                return(1);              // pass
#ifdef WARNING_ABATEMENT_UNREACHABLE
            break;
#endif
        }
        picophState = picoph_stateMachine(picophState);

    }

#ifdef WARNING_ABATEMENT_UNREACHABLE
    return(0);   // fail, never gets here
#endif
}

void picoph_comm_test(void)
// largely borrowed from optode comm test
// just a passthrough serial terminal
{
    unsigned char inChar;

    prn_nl(); prn_nl(); uprintf("*Press CTRL-X to stop communicating with the Pico-pH.\r\n");
    prn_nl(); uprintf(" Sample commands:"); prn_nl(); 
    uprintf("    #VERS            [Get device info: device ID, # channels (1), firmware version, available sensor types]\r\n");
    uprintf("    #IDNR            [ID number]\r\n");
    uprintf("    #LOGO            [Flash status LED 4 times]\r\n");
    uprintf("    #PDWN / #PWUP    [Power down / up sensor circuits]\r\n");
    uprintf("    RMR 1 0 0 14     [Retrieve all settings registers]\r\n");
    uprintf("    MEA 1 47         [Trigger measurement (sensor types set by 47)]\r\n");

    prn_nl(); uprintf("MEA should return output:  MEA 1 47 0 27845 0 0 0 22737 22530 200043 4133 1012867 54193 108753 0 0 7777 622733 0 0"); prn_nl(); prn_nl(); 

    picoph_init();      // moved from init()
    picoph_open();               // power on
    MAP_SysCtlDelay(MILLISECOND * PICO_STARTUP_MS);

    picoph_flushRx();
    picoph_flushTx(true);   // true says discard

    while(1)
    {
        // if picopH sent anything, print it out on UART1 (the desktop)
        if( picoph_rxBytesAvail() )
        {
            inChar = picoph_getc();
            if (inChar == '\r')
                prn_nl();
            else
                uprintf( "%c", inChar );
        }

        // if user types anything, send it to the picopH--exit on CTRL-X
        if(UARTRxBytesAvail()) {
            inChar = UARTgetc();
            if(inChar == CTRL_X) {
                uprintf("Ctrl-x received, exiting.\n");
                break;
            }
            picoph_putc(inChar);
        }
    }

    picoph_close();
    return;

}


void picoph_oneMeas(void)
// use state machine to take one measurement
// for now, this directly prints out return string from picopH
{
    //start the driver by passing 1, when complete, the statemachine returns 0
    int picoState = 1;

    while(picoState > 0) { // 0 when done
        picoState = picoph_stateMachine(picoState);
    }

    // output the return
    UARTwrite((char *)uart3_prn_buf, strlen((char *)uart3_prn_buf));
}
