#include "mbed.h"
#include "N5110.h"
#include "BMP180.h"
#include "PowerControl/PowerControl.h"
#include "PowerControl/EthernetPowerControl.h"

BMP180 bmp180(p28,p27);   // SDA, SCL
N5110 lcd(p7,p8,p9,p10,p11,p13,p26);
BusOut leds(LED4,LED3,LED2,LED1);
Serial serial(USBTX,USBRX);
InterruptIn button1(p16);
InterruptIn button2(p17);
InterruptIn button3 (p15);
InterruptIn button4 (p18);


Ticker timer; //Create ticker object for temperature and pressure readings
Ticker dataLoggerTimer ; // a ticker object for the data logger functions
Timeout powerSaverTimeout; // create a timeout object for the poweerSaver function

int setTimeFlag = 0; // flag for real time clock ISR
int timerFlag = 1; // Flag for tempesrature pressure reading ISR
int button1Flag = 0;
int button2Flag = 0 ;
int button3Flag = 0 ;
int button4Flag = 0 ;
int unitFlag = 1 ;
int powerSaverFlag = 0 ;
int lcdPowerFlag = 1 ; // Indicates if the LCD is switched on or powered off
int dataLoggerFlag = 0 ;
int saveToFileFlag = 0 ;


int temperatureC  ;
int temperatureF  ;
float pressureMB  ;
float pressureATM ;
int choice=1 ;
int length=14;
int UNIXdate;
int powerSaverTime = 60;
int dataLoggerTime = 2;
int avgTC ; // an integer to store the average value of the temperature in celsious
int avgTF ;// int to save the average value of the temperature in farenheit
int avgPB ; // int to store the average value of pressure in mBar
int avgPA ; // int to save the average pressure in atm
int maxTC ;
int minTC ;
int maxTF ;
int minTF ;
int maxPB ;
int minPB ;
int maxPA ;
int minPA ;
float arrayTC[83]= {5} ; // An array to store the values of temperature in c
float arrayTF[83]= {5} ; // An array to store the values of temperature in farenh
float arrayPB[83]= {5} ; // An array to store the values of pressure in dB
float arrayPA[83]= {5} ; // An array to store the values of pressure in atm



char rxString[16]; //buffer to store recieved string from serial
char bufferTime[14]; // buffer to store time
char bufferDate[14]; //buffer to store date
char bufferT[14]; //buffer to store temperature
char bufferP[14];  //buffer to store pressure, each character is 6 pixels wide, screen is 84 pixels (84/6 = 14)
// so can display a string of a maximum 14 characters in length
// or create formatted strings - ensure they aren't more than 14 characters long


char PowerSaverTime[2]; // string to store the powerSaverTime value to display on the LCD
char DataLoggerTime[2]; // string to store the time used for the logger to read data
char buffer0[14];
char buffer1[14];
char buffer2[14];
char buffer3[14];
char buffer4[14];
char buffer5[14];


int state=0; // used to navigate through the finite machines fsmA and fsmB
int stateplus1 ;
int stateplus2 ;
int stateminus1 ;
int stateminus2 ;



struct  State {
    int output;
    char title[14];
    int nextState[2];
};

typedef struct State STyp1; // for the start menu

STyp1 fsmA[5] = {
    {0,"Live Data",{1,4}},
    {1,"Saved Data",{2,0}},
    {2,"Forecast",{3,1}},
    {3,"Alarms",{4,2}},
    {4,"Settings",{0,3}},
};

typedef struct State STyp2; // for the settings menu

STyp2 fsmB[5] = {
    {0,"Date/Time",{1,4}},
    {1,"Units",{2,0}},
    {2,"Notification",{3,1}},
    {3,"Power",{4,2}},
    {4,"Logger",{0,3}},
};

typedef struct State STyp3; // for the time/date settings

STyp3 fsmC[5] = {
    {12,"Hour:",{1,4}},
    {0,"Min:",{2,0}},
    {21,"Day:",{3,1}},
    {4,"Month:",{4,2}},
    {2015,"Year:",{0,3}},
};


typedef struct State STyp4; // for the units settings

STyp3 fsmD[4] = {
    {1,"C/mb",{1,3}},
    {2,"C/atm",{2,0}},
    {3,"F/mb",{3,1}},
    {4,"F/atm",{0,2}},
};






void button1Pressed()  //ISR to subtract 1 from the value of choice when the first button is pressed
{
    state=fsmA[state].nextState[1];
//    state=fsmB[state].nextState[1];
//    state=fsmC[state].nextState[1];
//    state=fsmD[state].nextState[1];
}



void button2Pressed() //ISR to add 1 to the value of the choice when the second button is pressed
{
    state=fsmA[state].nextState[0];
//    state=fsmB[state].nextState[0];
//    state=fsmC[state].nextState[0];
//    state=fsmD[state].nextState[0];
}



void button3Pressed()
{
    button3Flag=1;
}



void button4Pressed()
{
    button4Flag = 1 ;
}




void serialISR()
{
    //reads rx string into buffer when a serial interrupt occurs
    serial.gets(rxString,16);
    //set flag
    setTimeFlag=1;
}


void timerExpired() // ISR which sets the timer flag (flag used to display the buffers on LCD)
{
    timerFlag=1;
}



void dataLoggerTimerExpired ()
{
    leds = 0;
    dataLoggerFlag = 1 ;

}



void setTime() // sets the time every second
{
//print time for debugging
    //serial.printf("set_time -%s ",rxString);
//atoi() converts a string to an integer
    int time = atoi(rxString);
//update the time
    set_time(time);
}



void updateTime()
{
    time_t seconds = time(NULL); // get current time
// format time into a string (time and date)
    strftime(bufferTime, 14 , "%I:%M", localtime(&seconds));
    strftime(bufferDate, 14 , "%d/%m/%y", localtime(&seconds));

    if (setTimeFlag) {
        setTimeFlag=0;
        setTime();
    }
}

void display()
{
    lcd.clear();
    lcd.printString(buffer0,0,0);
    lcd.printString(buffer1,0,1);
    lcd.printString(buffer2,0,2);
    lcd.printString(buffer3,0,3);
    lcd.printString(buffer4,0,4);
    lcd.printString(buffer5,0,5);

}



void readData() //Imports the data and saves them to the bufferss
{

    Measurement measurement;  // measurement structure declared in BMP180 class

    // read values (T in Celsius and P in mb) and print over serial port
    measurement = bmp180.readValues();
    // serial.printf("T = %.2f C P = %.2f mb\n",measurement.temperature,measurement.pressure);

    temperatureC = measurement.temperature;
    temperatureF = (temperatureC*(9/5))+32;

    if (unitFlag==1 || unitFlag==2 ) {
        length = sprintf(bufferT,"T = %0.1d C",temperatureC); // print formatted data to buffer
        // it is important the format specifier ensures the length will fit in the buffer
    }
    if (unitFlag==3 || unitFlag==4 ) {
        length = sprintf(bufferT,"T = %0.1d F",temperatureF); // print formatted data to buffer
        // it is important the format specifier ensures the length will fit in the buffer
    }

    pressureMB = measurement.pressure;  // same idea with floats
    pressureATM = pressureMB*0.0009869;


    if (unitFlag==1 || unitFlag==3 ) {
        length = sprintf(bufferP,"P = %.2f mb",pressureMB); // print formatted data to buffer
        // it is important the format specifier ensures the length will fit in the buffer
    }
    if (unitFlag==2 || unitFlag==4 ) {
        length = sprintf(bufferP,"P = %.2f atm",pressureATM); // print formatted data to buffer
        // it is important the format specifier ensures the length will fit in the buffer
    }

}







void savePower()   // turns off the LCD to save power
{

    lcd.turnOff () ; //turns off the lcd
    lcdPowerFlag = 0 ;  // A flag is set to indicate the state of the LCD

}







void powerSaverCheck()   // checks if the powersaverFlag is set
{

    if (powerSaverFlag == 1 ) {

        powerSaverTimeout.attach(&savePower, powerSaverTime); // setup a timeiut to call the savePower function

    }

    else {
        powerSaverTimeout.detach();

    }
}



void wakeUp ()  //Turns the LCD on after the mbed is interrupted
{

    if (button1Flag || button2Flag || button3Flag || button4Flag ) {


        if (lcdPowerFlag == 0) { // if the LCD is powered off
            lcdPowerFlag = 1 ;
            lcd.init();  // turns the LCD on
            powerSaverTimeout.attach(&savePower, powerSaverTime); // setup a timeiut to call the savePower function
            button1Flag = 0 ;
            button2Flag = 0 ;
            button3Flag = 0 ;
            button4Flag = 0 ;
        }
    }

}

void setLoggerTimer()
{
    switch (dataLoggerTime) {
        case 1:
            dataLoggerTimer.attach(&dataLoggerTimerExpired , 300);
            break;
        case 2:
            dataLoggerTimer.attach(&dataLoggerTimerExpired , 600);
            break;
        case 3:
            dataLoggerTimer.attach(&dataLoggerTimerExpired , 900);
            break;
        case 4:
            dataLoggerTimer.attach(&dataLoggerTimerExpired , 1200);
            break;
        case 5:
            dataLoggerTimer.attach(&dataLoggerTimerExpired , 1500);
            break;
        case 6:
            dataLoggerTimer.attach(&dataLoggerTimerExpired , 1800);
            break;
        default:
            break;
    }
}


void liveData() //
{
    while (1) {
        wakeUp();
        if (timerFlag) {
            timerFlag=0;
            readData();
            // lcd.printString("hey",1,1);
            strncpy(buffer1, bufferTime, 14);
            strncpy(buffer2, bufferDate, 14);
            strncpy(buffer3, bufferT, 14);
            strncpy(buffer4, bufferP, 14);
            int dispaly = sprintf (buffer5, "");//convert integer to buffer str
            int dispaly1 = sprintf (buffer0, "");//convert integer to buffer str
            display();
        }
        if (button3Flag) {
            button3Flag = 0;
            timer.detach();
            break;
        }
    }
}



void updateLoggerData() // If the data logger ISR is set then it reads the data and stores to suitable arrays (the arrays are used to plot the desired graphs)
{
    if (dataLoggerFlag) {
        dataLoggerFlag=0;
        lcd.clear();

        int i = 0 ;
        int k ;
        int sumTC = 0;
        int sumTF = 0;
        int sumPB = 0;
        int sumPA = 0;
        int maxTC = 0 ;
        minTC = 0 ;
        maxTF = 0 ;
        minTF = 0 ;
        maxPB = 0 ;
        minPB = 0 ;
        maxPA = 0 ;
        minPA = 0 ;
        i++;

//write the data to the arrays (arrays used to plot graphs)
        //read the data from sensor
        readData();
        //Store the values in the array
        arrayTC[i] = temperatureC;
        arrayTF[i] = temperatureF;
        arrayPB[i] = pressureMB;
        arrayPA[i] = pressureATM;

// calculate the average value of the arrays and save them to an integer

        for (k = 0 ; k<i ; k++) {
            sumTC += arrayTC[k]; // calculates the sum of the stored values
            avgTC = sumTC/k ; //calculates the average value
            sumTF += arrayTF[k]; // calculates the sum of the stored values
            avgTF = sumTF/k ; //calculates the average value
            sumPB += arrayPB[k]; // calculates the sum of the stored values
            avgPB = sumTC/k ; //calculates the average value
            sumPA += arrayPA[k]; // calculates the sum of the stored values
            avgPA = sumPA/k ; //calculates the average value

            if(arrayTC[i]>maxTC) { // finds the biggest value of the array
                maxTC=arrayTC[i];
            }
            if(arrayTC[i]<minTC) { // finds the smallest value of the array
                minTC=arrayTC[i];
            }
            if(arrayTF[i]>maxTF) {
                maxTF=arrayTF[i];
            }
            if(arrayTF[i]<minTF) {
                minTF=arrayTF[i];
            }
            if(arrayPB[i]>maxPB) {
                maxPB=arrayPB[i];
            }
            if(arrayPB[i]<minPB) {
                minPB=arrayPB[i];
            }
            if(arrayPA[i]>maxPA) {
                maxPA=arrayPA[i];
            }
            if(arrayPA[i]<minPA) {
                minPA=arrayPA[i];
            }



        }

        if (i>83) {
            lcd.clear();
            i=0;
        }

    }
}




void dataLogger()  // shows a summery of data and the graphs
{
    leds = 0;
    choice = 1;
    char Max[2] = {0};
    char Min[2] = {0};
    char Avg[2] = {0};
    while (1) {
        updateLoggerData();
        int min = sprintf (Min, "Min = %d", minTC);//convert integer to buffer str
        int max = sprintf (Max, "Max = %d", maxTC);//convert integer to buffer str
        int avg = sprintf (Avg, "Avg = %d", avgTC);//convert integer to buffer str
        //int mn = sprintf (Min, "Min = %d", minPB);//convert integer to buffer str
        //int mx = sprintf (Max, "Max = %d", maxPB);//convert integer to buffer str
        //int ag = sprintf (Avg, "Avg = %d", avgPB);//convert integer to buffer str

        if (choice < 1) {
            choice =4 ;
        }
        if (choice > 4 ) {
            choice = 1;
        }

        switch (choice) {
            case 1 : {
                lcd.clear();
                leds = 1 ;
                lcd.printString(bufferTime,48,0);
                lcd.printString("Temperature",36,1);
                lcd.printString(Min,0,2);
                lcd.printString(Avg,0,3);
                lcd.printString(Max,0,4);
                lcd.printString("Next",40,5);


                break;
            }
            case 2 :
                lcd.clear();
                leds = 2 ;
                break;
            case 3 :
                lcd.clear();
                leds = 4 ;
                lcd.printString(bufferTime,48,0);
                lcd.printString("Temperature",36,1);
                lcd.printString(Min,0,2);
                lcd.printString(Avg,0,3);
                lcd.printString(Max,0,4);
                lcd.printString("Next",40,5);

                break;
            case 4 :
                leds = 8 ;
                lcd.clear();
                break;
            default :

                break;
        }
    }
}

void dataLoggerSetting()
{

    lcd.clear();
    choice = dataLoggerFlag ;
    powerSaverCheck();



    while (1) {
        wakeUp();

        if (choice < 1) {
            choice = 2;
        }
        if (choice > 2) {
            choice = 1;
        }

        switch (choice) {
            case 1:

                lcd.clear();
                lcd.printString(bufferTime,48,0);
                lcd.printString("Data Logger :",0,1);
                lcd.printString("ON",0,2);
                lcd.printString("Save on RAM :",0,3);
                lcd.printString(DataLoggerTime,0,4);
                lcd.printString("Back      Save",0,5);

                if (button4Flag) {
                    button4Flag=0;
                    saveToFileFlag ++;
                    dataLoggerFlag = 1 ;
                }


                break;
            case 2:
                lcd.clear();
                lcd.printString(bufferTime,48,0);
                lcd.printString("Data Logger : ",0,1);
                lcd.printString("OFF",0,2);
                lcd.printString("Back      Save",0,5);
                if (button4Flag) {
                    button4Flag=0;
                    dataLoggerFlag = 0 ;
                    dataLoggerTimer.detach();

                }

            default:
                break;

        }

        wait (0.1);
        if (button3Flag) {
            button3Flag=0;
            break;
        }
    }



}












void powerSaverSetting()
{


    lcd.clear();
    choice = powerSaverFlag ;

    while (1) {
        wakeUp();
        int time = sprintf (PowerSaverTime, "%d min", powerSaverTime/60);//convert integer to buffer str

        if (choice < 1) {
            choice = 2;
        }
        if (choice > 2) {
            choice = 1;
        }
        if (powerSaverTime > 300) {
            powerSaverTime = 60 ;
        }


        switch (choice) {
            case 1:

                lcd.clear();
                lcd.printString(bufferTime,48,0);
                lcd.printString("Power Saver :",0,1);
                lcd.printString("ON",0,2);
                lcd.printString("Time Out :",0,3);
                lcd.printString(PowerSaverTime,0,4);
                lcd.printString("Back      Save",0,5);

                if (button4Flag) {
                    button4Flag=0;
                    powerSaverFlag = 1 ;
                    powerSaverTime += 60 ;
                    dataLoggerTimer.attach(&dataLoggerTimerExpired , 5);

                }


                break;
            case 2:
                lcd.clear();
                lcd.printString(bufferTime,48,0);
                lcd.printString("Power Saver : ",0,1);
                lcd.printString("OFF",0,2);
                lcd.printString("Back      Save",0,5);
                if (button4Flag) {
                    button4Flag=0;
                    powerSaverFlag = 0 ;

                }

            default:
                break;

        }

        wait (0.1);
        powerSaverCheck();
        if (button3Flag) {
            button3Flag=0;
            break;
        }
    }
}








void calculateUNIXTime()
{
    time_t rawtime;
    struct tm * timeinfo;

    /* get current timeinfo: */
    time ( &rawtime ); //or: rawtime = time(0);
    /* convert to struct: */
    timeinfo = localtime ( &rawtime );

    /* now modify the timeinfo to the given date: */
    timeinfo->tm_year   = (fsmC[4].output) - 1900;
    timeinfo->tm_mon    = (fsmC[3].output) - 1;    //months since January - [0,11]
    timeinfo->tm_mday   = (fsmC[2].output) ;        //day of the month - [1,31]
    timeinfo->tm_hour   = (fsmC[0].output) ;        //hours since midnight - [0,23]
    timeinfo->tm_min    = (fsmC[1].output) ;         //minutes after the hour - [0,59]
    timeinfo->tm_sec    = 0 ;          //seconds after the minute - [0,59]

    /* call mktime: create unix time stamp from timeinfo struct */
    UNIXdate = mktime ( timeinfo );

}


void brightnessSetting ()
{
    while(1) {
        wakeUp();
        powerSaverCheck();


        if (button4Flag) {
            button4Flag=0;
            lcd.

        }

        if (button3Flag) {
            button3Flag=0;
            break;
        }
    }
}



void unitsSetting()
{
    state = 0;
    while (1) {
        wakeUp();
        powerSaverCheck();
        updateTime();
        stateminus1=fsmD[state].nextState[0];
        stateminus2=fsmD[stateminus1].nextState[0];
        stateplus1=fsmD[state].nextState[1];
        stateplus2=fsmD[stateplus1].nextState[1];




        int display0 = sprintf (buffer0, " Back    Save");//convert integer to buffer str
        int display1 = sprintf (buffer1, "%s", fsmD[stateminus2].title);//convert integer to buffer str
        int display2 = sprintf (buffer2, "%s", fsmD[stateminus1].title);//convert integer to buffer str
        int display3 = sprintf (buffer3, ">>%s", fsmD[state].title);//convert integer to buffer str
        int display4 = sprintf (buffer4, "%s", fsmD[stateplus1].title);//convert integer to buffer str
        int dispaly5 = sprintf (buffer5, "");//convert integer to buffer str


        unitFlag = fsmD[state].output;

        display();

        if (button3Flag) {
            button3Flag=0;
            break;
        }

        wait (0.1);
    }
}







void timeDateSetting()
{

    state = 0;
    while (1) {
        wakeUp();
        powerSaverCheck();
        updateTime();
        stateminus1=fsmC[state].nextState[0];
        stateminus2=fsmC[stateminus1].nextState[0];
        stateplus1=fsmC[state].nextState[1];
        stateplus2=fsmC[stateplus1].nextState[1];




        int display0 = sprintf (buffer0, " Back    Save");//convert integer to buffer str
        int display1 = sprintf (buffer1, "%s %d", fsmC[stateminus2].title,fsmC[stateminus2].output);//convert integer to buffer str
        int display2 = sprintf (buffer2, "%s %d", fsmC[stateminus1].title,fsmC[stateminus1].output);//convert integer to buffer str
        int display3 = sprintf (buffer3, ">>%s %d", fsmC[state].title,fsmC[state].output);//convert integer to buffer str
        int display4 = sprintf (buffer4, "%s %d", fsmC[stateplus1].title,fsmC[stateplus1].output);//convert integer to buffer str
        int display5 = sprintf (buffer5, "%s %d", fsmC[stateplus2].title,fsmC[stateplus2].output);//convert integer to buffer str



        display();



        if (button4Flag) {
            button4Flag= 0;
            fsmC[state].output ++; 
            calculateUNIXTime();
            set_time(UNIXdate); // initialise time from the calculated UNIX time entered by the user
        }


        //checks the limits of the values
        if (fsmC[0].output >23) { //hour
            fsmC[0].output = 0 ;
        }
        if (fsmC[1].output>59) { // minute
            fsmC[1].output = 0 ;
        }
        if (fsmC[3].output>11) { //month
            fsmC[3].output = 0 ;
        }
        if (fsmC[2].output>31) { //day
            fsmC[2].output = 1 ;
        }
        if (fsmC[4].output>2030) { //year
            fsmC[4].output = 2015 ;
        }



        if (button3Flag) {
            button3Flag=0;
            break;
        }
        wait (0.1);
    }
}




void settingsMenu()
{
    state = 0;
    while(1) {

        stateminus1=fsmB[state].nextState[0];
        stateminus2=fsmB[stateminus1].nextState[0];
        stateplus1=fsmB[state].nextState[1];
        stateplus2=fsmB[stateplus1].nextState[1];


        wakeUp();
        powerSaverCheck();
        updateTime();
        strncpy(buffer0, bufferTime, 14);
        int dispaly = sprintf (buffer1, "%s", fsmB[stateminus2].title);//convert integer to buffer str
        int dispaly1 = sprintf (buffer2, "%s", fsmB[stateminus1].title);//convert integer to buffer str
        int dispaly2 = sprintf (buffer3, ">>%s", fsmB[state].title);//convert integer to buffer str
        int dispaly3 = sprintf (buffer4, "%s", fsmB[stateplus1].title);//convert integer to buffer str
        int dispaly4 = sprintf (buffer5, "%s", fsmB[stateplus2].title);//convert integer to buffer str

        display();


        switch (state) {
            case 0:
                if (button4Flag) {
                    button4Flag=0;
                    timeDateSetting();
                }


                break;
            case 1:


                if (button4Flag) {
                    button4Flag=0;
                    unitsSetting();
                }

                break;
            case 2:

                if (button4Flag) {
                    button4Flag = 0;
                    brightnessSetting();
                }
                break;
            case 3:
                if (button4Flag) {
                    button4Flag = 0;
                    powerSaverSetting();
                }
                break;
            case 4:
                if (button4Flag) {
                    button4Flag = 0;
                    dataLoggerSetting();
                }
                break;
            default:
                break;
        }
        if (button3Flag) {
            button3Flag=0;
            break;
        }
        wait (0.1);
    }
}


void startMenu()   //The menu displayed at the beginning
{
    //wakeUp();
    //powerSaverCheck();

    stateminus1=fsmA[state].nextState[1];
    stateminus2=fsmA[stateminus1].nextState[1];
    stateplus1=fsmA[state].nextState[0];
    stateplus2=fsmA[stateplus1].nextState[0];


    strncpy(buffer0, bufferTime, 14);
    int dispaly = sprintf (buffer1, "%s", fsmA[stateminus2].title);//convert integer to buffer str
    int dispaly1 = sprintf (buffer2, "%s", fsmA[stateminus1].title);//convert integer to buffer str
    int dispaly2 = sprintf (buffer3, ">>%s", fsmA[state].title);//convert integer to buffer str
    int dispaly3 = sprintf (buffer4, "%s", fsmA[stateplus1].title);//convert integer to buffer str
    int dispaly4 = sprintf (buffer5, "%s", fsmA[stateplus2].title);//convert integer to buffer str



    switch (state) {
        case 0:
            if (button4Flag) {
                button4Flag=0;
                timerFlag=1;
                timer.attach(&timerExpired,1.0);
                liveData();
            }
            break;
        case 1:
            if (button4Flag) {
                button4Flag=0;
                dataLogger();
            }
            break;
        case 2:
            break;
        case 3:
            break;
        case 4:
            if (button4Flag) {
                button4Flag=0;
                settingsMenu();
            }
            break;
        default:
            break;
    }
}




int main()
{

    // initiliase barometer
    bmp180.init();
    lcd.init();
    serial.attach(&serialISR);
    button1.rise(&button1Pressed); // call ISR on rising edge (button1pressed)
    button2.rise(&button2Pressed); // call ISR on rising edge (button2pressed)
    button3.rise(&button3Pressed);
    button4.rise(&button4Pressed);
    dataLoggerTimer.attach(&dataLoggerTimerExpired , dataLoggerTime);
    set_time(UNIXdate); // initialise time from the calculated UNIX time entered by the user


    while(1) {
        updateTime();
        startMenu();
        display();
        wait (0.1);
    }
}



