/**
 * Digital "Etch A Sketch"
 * 
 * Modified by Austin Lee, Andrew Bryan, and Jonya Chen
 * December 10, 2015
 */

////////////////////////////////////
// clock AND protoThreads configure!
// You MUST check this file!
#include "config.h"
// threading library
#include "pt_cornell_1_2.h"

#include <math.h>
#include <stdlib.h>
#include <plib.h>


// graphics libraries
#include "tft_master.h"
#include "tft_gfx.h"
#include "declarations.h"

#define	SYS_FREQ 40000000
#define PB_DIV 8
#define PRESCALE 256
#define T1_TICK (SYS_FREQ/PB_DIV/PRESCALE/100)


#define dt .01                                                                                                                                                                      
#define g 8192

// == Thresholds for finger positions ==============================
//thumb thresholds
#define thumbOpen 130
#define thumbClosed 125

//pointer finger thresholds
#define pointerOpen 400
#define pointerClosed 360

//middle finger thresholds
#define middleOpen 430
#define middleClosed 390

//ring finger thresholds
#define ringOpen 400
#define ringClosed 385

//pinky thresholds
#define pinkyOpen 400
#define pinkyClosed 390



// === thread structures ============================================
// semaphores for controlling two threads
// for guarding the UART and for allowing stread blink control
static struct pt_sem control_t1, control_t2, control_t6;
// thread control structs
static struct pt pt_timer, pt_adc, pt3, pt_stuff; 
// system 1 second interval tick
int sys_time_seconds;
static int thumb, pointerFinger, middleFinger, ringFinger, pinky;
unsigned static int draw; //1 is draw
int done;


// string buffer
char buffer[60];
char timer1[60];


void Zero_Sensors()
{
    float BUFFER_XANGLE = 0;
    float BUFFER_YANGLE = 0;
    float BUFFER_XOUT = 0;
    float BUFFER_YOUT = 0;
    float BUFFER_ZOUT = 0;
    int x = 0;
    for(x=0; x<500; x++)
    {
        Get_Accel_Values();
        Get_Accel_Angles();
        BUFFER_XANGLE += ACCEL_XANGLE;
        BUFFER_YANGLE += ACCEL_YANGLE;
        DelayMs(1);
        BUFFER_YOUT += (float)ACCEL_YOUT;
        BUFFER_XOUT += (float)ACCEL_XOUT;
        BUFFER_ZOUT += (float)ACCEL_ZOUT;
    }
    
    YOFFSET = BUFFER_YOUT/500.0;
    XOFFSET = BUFFER_XOUT/500.0;
    ZOFFSET = BUFFER_ZOUT/500.0;
    
    COMPLEMENTARY_XANGLE = BUFFER_XANGLE/500.0;
    COMPLEMENTARY_YANGLE = BUFFER_YANGLE/500.0;
    GYRO_XANGLE = BUFFER_XANGLE/500.0;
    GYRO_YANGLE = BUFFER_YANGLE/500.0;
}	
 




unsigned static short THUMBS_UP;
unsigned static short OPEN_HAND;
unsigned static short FIST, ERASE;
unsigned static short color, prevColor; //color state
unsigned static short red, green, white, blue; //set by flex sensors to send to state machine
unsigned static short debounce;

static PT_THREAD(protothread_values(struct pt *pt)) {
    PT_BEGIN(pt);
    
    OPEN_HAND = 1;
    
    while(1) {
        PT_YIELD_TIME_msec(10);
        
        //Detect gesture thru finger readings
        if (thumb <= thumbClosed && pointerFinger <= pointerClosed && middleFinger <= middleClosed && ringFinger <= ringClosed && pinky <= pinkyClosed) {
            OPEN_HAND=0;
            FIST=1;
            THUMBS_UP=0;
            ERASE=0;
        }
        else if (thumb >= thumbOpen && pointerFinger >= pointerOpen && middleFinger >= middleOpen && ringFinger >= ringOpen && pinky >= pinkyOpen) {
            FIST=0;
            OPEN_HAND=1;
            THUMBS_UP=0;
            ERASE=0;
        }
        
        else if (thumb >= thumbOpen && pointerFinger <= pointerClosed && middleFinger <= middleClosed && ringFinger <= ringClosed && pinky <= pinkyClosed) {
            THUMBS_UP=1;
            FIST=0;
            OPEN_HAND=0; 
            ERASE=0;
        }
        else if (thumb >= thumbOpen && pointerFinger <= pointerClosed && middleFinger <= middleClosed && ringFinger <= ringClosed && pinky >= pinkyClosed) {
            ERASE=1;
            THUMBS_UP=0;
            FIST=0;
            OPEN_HAND=1;
        }
        
        
        //Detect color mode thru finger readings
        if (draw==0){
            if (thumb >= thumbOpen && pointerFinger >= pointerOpen && middleFinger >= middleOpen && ringFinger >= ringOpen && pinky >= pinkyOpen) {
                debounce=1;
                ERASE=0;
            }
            else if (thumb <= thumbClosed && pointerFinger >= pointerOpen && middleFinger <= middleClosed && ringFinger <= ringClosed && pinky <= pinkyClosed) {
                red=1;
                green=0;
                blue=0;
                white=0;
                ERASE=0;
            }
            else if (thumb <= thumbClosed && pointerFinger >= pointerOpen && middleFinger >= middleOpen && ringFinger <= ringClosed && pinky <= pinkyClosed) {
                red=0;
                green=1;
                blue=0;
                white=0;
                ERASE=0;
            }
            else if (thumb <= thumbClosed && pointerFinger >= pointerOpen && middleFinger >= middleOpen && ringFinger >= ringOpen && pinky <= pinkyClosed) {
                red=0;
                green=0;
                blue=1;
                white=0;
                ERASE=0;
            }
            else if (thumb <= thumbClosed && pointerFinger >= pointerOpen && middleFinger >= middleOpen && ringFinger >= ringOpen && pinky >= pinkyOpen) {
                red=0;
                green=0;
                blue=0;
                white=1;
                ERASE=0;
            }
        }

    }
    PT_END(pt);
}


// === Thread 3 ======================================================

static PT_THREAD(protothread3(struct pt *pt)) {
    PT_BEGIN(pt);

    unsigned static int state = 1;
    unsigned static int n;
    
    while (1) {
        PT_YIELD_TIME_msec(50);

        //FSM CODE
        switch (state) {
            case 1: //Color
                if (THUMBS_UP==1) state = 13; //MaybeFinish
                else if (FIST==1) state = 12; //MaybeDraw
                else if (ERASE==1){
                    PT_YIELD_TIME_msec(1000);  //Debounce Erase
                    if (ERASE==1) {
                        tft_fillScreen(ILI9340_BLACK); //Clear screen
                        tft_fillRoundRect(0, 18, 240, 1, 0, ILI9340_WHITE); // x,y,w,h,radius,color
                        tft_fillRoundRect(0, 319, 240, 1, 0, ILI9340_WHITE); 
                        tft_fillRoundRect(0, 18, 1, 302, 0, ILI9340_WHITE); 
                        tft_fillRoundRect(239, 18, 1, 302, 0, ILI9340_WHITE); 
                    }
                    ERASE = 0;
                }
                draw = 0;
                break;

            case 2: //Draw
                if (THUMBS_UP==1) state = 13;
                else if (OPEN_HAND==1) state = 11; //MaybeColor
                draw = 1;
                break;

            case 3: //Finish
                done = 1;
                tft_fillRoundRect(180, 0, 60, 15, 0, ILI9340_BLACK);
                tft_setCursor(180, 0);
                tft_setTextColor(ILI9340_BLUE);
                tft_writeString("DONE");
                PT_YIELD_TIME_msec(1000);
                while(1); //Stop the program
                break;

            case 11: //MaybeColor
                PT_YIELD_TIME_msec(200);
                if (OPEN_HAND==1) {
                    state = 1; //Color
                } else state = 2; //Draw
                break;

            case 12: //MaybeDraw
                PT_YIELD_TIME_msec(200);
                if (FIST==1) {
                    state = 2; //Draw
                } else state = 1;
                break;

            case 13: //MaybeFinish
                n=0;
                while(n<20 && state != 1){
                    n++;
                    PT_YIELD_TIME_msec(50);
                    if (THUMBS_UP==1) state = 3; //Finish
                    else {
                        state = 1; //Color
                        break;
                    }
                }              
                break;

        }
        
            //Color switching FSM 
            switch(color){
                case 0: //white
                    if (red) color=11; //maybe red
                    else if (green) color=12; //maybe green
                    else if (blue) color=13; //maybe blue
                    prevColor = 0;
                    break;
                case 1: //red
                    if (white) color=10; //maybe white
                    else if (green) color=12; //maybe green
                    else if (blue) color=13; //maybe blue
                    prevColor = 1;
                    break;
                case 2: //green
                    if (red) color=11; //maybe red
                    else if (white) color=10; //maybe white
                    else if (blue) color=13; //maybe blue
                    prevColor = 2;
                    break;
                case 3: //blue
                    if (red) color=11; //maybe red
                    else if (green) color=12; //maybe green
                    else if (white) color=10; //maybe white
                    prevColor = 3;
                    break;
                case 10: //maybe white
                    PT_YIELD_TIME_msec(400);
                    if (debounce) {
                        color=prevColor;
                        debounce= 0;
                    }
                    else if (white) color=0;
                    else color=prevColor;
                    break;
                case 11: //maybe red
                    PT_YIELD_TIME_msec(400);
                    if (debounce) {
                        color=prevColor;
                        debounce= 0;
                    }
                    else if (red) color=1;
                    else color=prevColor;
                    break;
                case 12: //maybe green
                    PT_YIELD_TIME_msec(400);
                    if (debounce) {
                        color=prevColor;
                        debounce= 0;
                    }                    
                    else if (green) color=2;
                    else color=prevColor;
                    break;
                case 13: //maybe blue
                    PT_YIELD_TIME_msec(400);
                    if (debounce) {
                        color=prevColor;
                        debounce= 0;
                    }
                    else if (blue) color=3;
                    else color=prevColor;
                    break;
            
            }
            
        tft_setTextSize(2);
        tft_fillRoundRect(0, 0, 240, 15, 0, ILI9340_BLACK);
        tft_setCursor(180, 0);
        //Print current draw mode
        if(draw == 1){ 
            tft_setTextColor(ILI9340_GREEN);
            tft_writeString("DRAW");
        }
        else if(draw == 0){
            tft_setTextColor(ILI9340_RED);
            tft_writeString("STOP");
        }
        
        //Print current color mode
        tft_setCursor(0, 0);
        if (color==0) {
            tft_setTextColor(ILI9340_WHITE);
            tft_writeString("WHITE");
        }
        else if (color==1){
            tft_setTextColor(ILI9340_RED);
            tft_writeString("RED");
        }
        else if (color==2) {
            tft_setTextColor(ILI9340_GREEN);
            tft_writeString("GREEN");
        }
        else if (color==3) {
            tft_setTextColor(ILI9340_BLUE);
            tft_writeString("BLUE");
        }

        // never exit while



    } // END WHILE(1)
    PT_END(pt);
} // thread 3



// === ADC Thread =============================================

static PT_THREAD(protothread_adc(struct pt *pt)) {
    PT_BEGIN(pt);
    
    int y;
    int z;
    unsigned static int cursorX=120;
    unsigned static int cursorY=160;
    
    unsigned int angle=1;
    
    while (1) {
        PT_YIELD_TIME_msec(100);
        
        // read the ADC outputs for each finger
        SetChanADC10(ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN5); 
        PT_YIELD_TIME_msec(2);
        ringFinger = ReadADC10(0); 
        PT_YIELD_TIME_msec(2);
        SetChanADC10(ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN11);
        PT_YIELD_TIME_msec(2);
        pointerFinger = ReadADC10(0);
        PT_YIELD_TIME_msec(2);
        SetChanADC10(ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN0); 
        PT_YIELD_TIME_msec(2);
        pinky = ReadADC10(0);
        PT_YIELD_TIME_msec(2);
        SetChanADC10(ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN3); 
        PT_YIELD_TIME_msec(2);
        thumb = ReadADC10(0);
        PT_YIELD_TIME_msec(2);
        SetChanADC10(ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN9); 
        PT_YIELD_TIME_msec(2);
        middleFinger = ReadADC10(0);   
        PT_YIELD_TIME_msec(2);
 
            
        Get_Accel_Values();
        
        y= (ACCEL_YOUT+8192)/2000;
        z= ACCEL_ZOUT/4000;
        
        //Set angle
        if(z<0) angle=7;  //if ACCEL_ZOUT is negative, move cursor DOWN
        else if(y==0) angle=7;
        else if(y==1) angle=6;
        else if(y==2) angle=4;
        else if(y==3) angle=1;
        else if(y==4) angle=2;
        else if(y==5) angle=3;
        else if(y==6) angle=5;
        else if(y==7) angle=8;
        else if(y==8 || y==9) angle=7;
        
        if(draw == 1){ //DRAW
         
            //Make cursor adjustments, based on angle
         switch (angle) {
            case 1:    
                cursorX=cursorX-2;
                cursorY=cursorY-2;
                break;
            case 2: 
                cursorX=cursorX;
                cursorY=cursorY-2;
                break;
            case 3: 
                cursorX=cursorX+2;
                cursorY=cursorY-2;
                break;
            case 4: 
                cursorX=cursorX-2;
                cursorY=cursorY;
                break;
            case 5: 
                cursorX=cursorX+2;
                cursorY=cursorY;
                break;
            case 6: 
                cursorX=cursorX-2;
                cursorY=cursorY+2;
                break;
            case 7: 
                cursorX=cursorX;
                cursorY=cursorY+2;
                break;
            case 8: 
                cursorX=cursorX+2;
                cursorY=cursorY+2;
                break;  
         }
         
         //Stop drawing further if cursor goes past borders
         if(cursorX>237) cursorX=237;
         else if(cursorX<2) cursorX=2;
         if(cursorY<20) cursorY=20;
         else if(cursorY>317) cursorY=317;
         
         
        //Draw corresponding color... yellow for testing
         if(color==1) tft_fillCircle(cursorX,cursorY,1,ILI9340_RED);
         else if(color==2) tft_fillCircle(cursorX,cursorY,1,ILI9340_GREEN);
         else if(color==3) tft_fillCircle(cursorX,cursorY,1,ILI9340_BLUE);
         else if(color==0) tft_fillCircle(cursorX,cursorY,1,ILI9340_WHITE);
         else  tft_fillCircle(cursorX,cursorY,1,ILI9340_YELLOW);
         
        }
        // NEVER exit while
    } // END WHILE(1)
    PT_END(pt);
} //



// === Main  ======================================================
// set up UART, timer2, threads
// then schedule them as fast as possible

int main(void) {
    ANSELA = 0;
    ANSELB = 0;
    CM1CON = 0;
    CM2CON = 0;

    // === config the uart, DMA, vref, timer5 ISR =============
    PT_setup();


    // the ADC ///////////////////////////////////////
    // configure and enable the ADC
    CloseADC10(); // ensure the ADC is off before setting the configuration

    // define setup parameters for OpenADC10
    // Turn module on | ouput in integer | trigger mode auto | enable autosample
    // ADC_CLK_AUTO -- Internal counter ends sampling and starts conversion (Auto convert)
    // ADC_AUTO_SAMPLING_ON -- Sampling begins immediately after last conversion completes; SAMP bit is automatically set
    // ADC_AUTO_SAMPLING_OFF -- Sampling begins with AcquireADC10();
#define PARAM1  ADC_FORMAT_INTG16 | ADC_CLK_AUTO | ADC_AUTO_SAMPLING_ON //

    // define setup parameters for OpenADC10
    // ADC ref external  | disable offset test | disable scan mode | do 1 sample | use single buf | alternate mode off
#define PARAM2  ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_1 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_OFF
    //
    // Define setup parameters for OpenADC10
    // use peripherial bus clock | set sample time | set ADC clock divider
    // ADC_CONV_CLK_Tcy2 means divide CLK_PB by 2 (max speed)
    // ADC_SAMPLE_TIME_5 seems to work with a source resistance < 1kohm
#define PARAM3 ADC_CONV_CLK_PB | ADC_SAMPLE_TIME_5 | ADC_CONV_CLK_Tcy2 //ADC_SAMPLE_TIME_15| ADC_CONV_CLK_Tcy2

    // define setup parameters for OpenADC10
    // set AN4 and  as analog inputs
#define PARAM4	ENABLE_AN9_ANA | ENABLE_AN11_ANA | ENABLE_AN0_ANA | ENABLE_AN5_ANA | ENABLE_AN3_ANA

    // define setup parameters for OpenADC10
    // do not assign channels to scan
#define PARAM5	SKIP_SCAN_ALL

    SetChanADC10(ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN9); // configure to sample AN4 
    OpenADC10(PARAM1, PARAM2, PARAM3, PARAM4, PARAM5); // configure ADC using the parameters defined above

    EnableADC10(); // Enable the ADC
    ///////////////////////////////////////////////////////

    // init the display
    tft_init_hw();
    tft_begin();
    tft_fillScreen(ILI9340_BLACK);
    tft_fillRoundRect(0, 18, 240, 1, 0, ILI9340_WHITE); // x,y,w,h,radius,color
    tft_fillRoundRect(0, 319, 240, 1, 0, ILI9340_WHITE); 
    tft_fillRoundRect(0, 18, 1, 302, 0, ILI9340_WHITE); 
    tft_fillRoundRect(239, 18, 1, 302, 0, ILI9340_WHITE); 


    //240x320 vertical display
    tft_setRotation(0); // Use tft_setRotation(1) for 320x240

    //I2C SETUP CODE
    int error = 1;
    Setup_I2C();
    tft_fillRoundRect(120, 160, 15, 15, 0, ILI9340_GREEN); // x,y,w,h,radius,color
    do {
        Setup_MPU6050();
        MPU6050_Test_I2C();
        error = MPU6050_Check_Registers();
    } while (error == 1);  //Green square displayed if stuck in loop

    Zero_Sensors();
    
    tft_fillRoundRect(120, 160, 15, 15, 0, ILI9340_BLACK); // x,y,w,h,radius,color
    
    // === setup system wide interrupts  ====================
    INTEnableSystemMultiVectoredInt();
    
    // === now the threads ====================
    // init  the thread control semaphores
    PT_SEM_INIT(&control_t1, 0); // start blocked
    PT_SEM_INIT(&control_t2, 1); // start unblocked
    PT_SEM_INIT(&control_t6, 0); // start blocked

    // init the threads

    PT_INIT(&pt_adc);
    PT_INIT(&pt3);
    PT_INIT(&pt_stuff);

    // schedule the threads
    while (1) {
        // round robin

        PT_SCHEDULE(protothread3(&pt3));
        PT_SCHEDULE(protothread_values(&pt_stuff));        
        PT_SCHEDULE(protothread_adc(&pt_adc));
    }
} // main