// include files
#include "mcc_generated_files/mcc.h"
#include "application.h"
#include "macros.h"


uint8_t motorSpeed;
bool motorState = HALTED;

extern volatile uint8_t timer0overflowFlag;// flag is set and incremented in timer0.c file at timer0 overflow event
extern volatile bool clc1FirstRisingEdge;// flag is set at initialization of CLC1 in CLC1.c
extern volatile bool clc1risingEdgeFlag;// flag is set in CLC1.c file when there is rising edge on CLC1 output 
extern volatile bool clc5RisingEdgeFlag;// flag is set in CLC5.c file when there is rising edge on CLC5 output 
bool sendingCommandFlag=false;//flag is set whenever there is button press or slider change event for sending command
                               // when command is being sent any touch events to the button or slider are ignored 

key_t touch;

static uint8_t Reverse(uint8_t byteVar);
static e_commands PrepareNewCMD (e_key swNum);
static uint32_t Prepare_IR_NEC_Data(uint8_t devAddress, uint8_t cmd);    
static e_status SendIR_Frame (uint32_t IR_Data, uint8_t numOfBitsToSend); 
static e_key_state GetEvent(void);
static void ClearEvent(void);
static e_key GetButton(void);
static void ClearButton(void);
static e_commands GetMotorSpeed(uint16_t adcVal);

/**
 * @Param
    none
 * @Returns
    none
 * @Description
     main application function being executed in while loop
 * check for button touch or slider change event, prepare command according to the event,
 * prepare 32 bit data as per NEC IR protocol, send IR frame for the command using DSM
 * @Example
    ApplicationTask();
 */
void ApplicationTask(void) 
{
    static e_app_state state=GET_EVENT;
    static e_key button;
    static uint32_t data;
    static e_commands command;   
    static uint8_t rxAddress; 
    uint8_t retStatus = 0;
    
    switch(state) {
    case GET_EVENT:
        if(GetEvent())//check if there is button touch or slider change event
        {
            button = GetButton();   
            if(button < MAX_KEYS)
            {
                state = PREPARE_CMD;//prepare command for button press or slider change event
            }
            ClearButton();
            ClearEvent();
            sendingCommandFlag=true;
        }
        else
        {
            LED_D5_SetLow();
            LED_D2_SetLow();
            LED_D3_SetLow();
            sendingCommandFlag=false;
        }
        break;
    case PREPARE_CMD:
        command = PrepareNewCMD(button);
        if(command != 0)
        {
            //Reverse the bits in the command to be transmitted from LSb to MSb
            //as per NEC protocol LSb is transmitted first
            command = Reverse (command);
            state = PREPARE_IR_NEC_DATA;
        }
        else
        {
            state = GET_EVENT;
        }
        button = NO_KEY;
    break;
    case PREPARE_IR_NEC_DATA:
        //Reverse the bits in the Receiver address to be transmitted from LSb to MSb
        //as per NEC protocol LSb is transmitted first
        rxAddress=Reverse((uint8_t)IR_RX_ADDRESS);
        data = Prepare_IR_NEC_Data(rxAddress,command);
        if(data != 0)
        {
            state = SEND_DATA_OVER_IR;
        }
        else
        {
            state = GET_EVENT;
        }       
    break;
    case SEND_DATA_OVER_IR:
        retStatus = SendIR_Frame(data,MAXCOMMANDBITS_1);
        if(retStatus == ERROR)
        {
            state = COMM_ERR;
        }
        else if(retStatus == SUCCESS)
        {
            state = GET_EVENT;
        }
    break;
    case COMM_ERR:
        // error event can be added
        state = GET_EVENT;
    break;
    default:
        state = GET_EVENT;
        break;
    }
}

/**
   @Param
    button number
   @Returns
    none
   @Description
    callback function for button press 
 *  Currently it does nothing the code can be added if required
   @Example 
    ProcessButtonTouch(button1);   
 */
void ProcessButtonTouch(enum mtouch_button_names button)
{
    switch(button)
    {
        case Button1:break;
        case Button2:break;
        default: break;
    }
}

/**
   @Param
    none
   @Returns
    button number
   @Description
    callback function for button release
    if button 1 is released then motor start command is sent and button LED will be turned ON
    if button 2 is released then motor stop command is sent and button LED will be turned ON
   @Example
    ProcessButtonRelease(button1);
 */
void ProcessButtonRelease(enum mtouch_button_names button)
{
    switch(button){
        case Button1: 
            touch.key = SW1;
            touch.keyStatus = KEY_RELEASED;
            break;
        case Button2:
            touch.key = SW2;
            touch.keyStatus = KEY_RELEASED;
            break;
        default: break;
    }
}

/**
   @Param
    slider number
   @Returns
    none 
   @Description
    callback function for slider change
    slider is used to increment or decrement the motor speed
   @Example    
 */
void ProcessSliderChange(enum mtouch_slider_names slider)
{
    touch.key = SLIDER;
    touch.keyStatus = KEY_PRESSED;
    touch.sliderADC_Val = MTOUCH_Slider_Position_Get(Slider1);
}

/**
 * @Param
    1 byte value
 * @Returns
    reverse of the byte with LSb as MSb
 * @Description
    In NEC protocol LSb is transmitted first. 
    This function is used to reverse the bits in the data byte and decode the byte received with MSb first.
 * @Example
    Reverse();
 */
static uint8_t Reverse(uint8_t byteVar) 
{
   byteVar = (byteVar & UPPER_NIBBLE) >> 4 | (byteVar & LOWER_NIBBLE) << 4;
   byteVar = (byteVar & BIT7632) >> 2 | (byteVar & BIT5410) << 2;
   byteVar = (byteVar & BIT7531) >> 1 | (byteVar & BIT6420) << 1;
   return byteVar;
}

/**
 * @Param
   e_key swNum: value of the switch pressed 
 * @Returns
   8 bit command
 * @Description
    This function is used to prepare an 8 bit command corresponding to the button press event.
 * @Example
    command= PrepareNewCMD(SW1);
 */
static e_commands PrepareNewCMD (e_key swNum)
{
   e_commands newCommand = NONE;
   
    if (swNum == SW1)
    { 
        newCommand=MOTOR_START;//if button 1 is pressed then command is transmitted to start the motor
        if (motorState == HALTED )//if motor is in running state then keep the LEDs on Slider as is related to the motor speed
        {
            TurnOnSliderLED1();
            motorSpeed = SPEED_1;
        }
        BLED1_SetLow();
        BLED2_SetHigh();
        LED_D5_SetHigh();
        motorState = RUNNING;
    }
    else if (swNum == SW2)
    {
        newCommand = MOTOR_STOP;//if button 2 is pressed then command is transmitted to stop the motor
        motorState = HALTED;
        motorSpeed=0;
        TurnOffSliderLEDs();
        BLED1_SetHigh();
        BLED2_SetLow();
        LED_D2_SetHigh();            
    }
    else if (swNum == SLIDER)
    {
        if (motorState == RUNNING)
        {
            newCommand=GetMotorSpeed(touch.sliderADC_Val);
            LED_D3_SetHigh();
        }
        else 
        {
            newCommand=NONE;
        }
    }
    return newCommand;
}

/**
 * @Param
   8 bit IR Receiver Address , 8 bit command
 * @Returns
   None
 * @Description
    This function is used to prepare 32 bits command to be transmitted after button press event. 
 * 32 bit IR Data as per NEC protocol for 8 bit command transmission: 
    address byte, inverse of address byte, command byte, inverse of command byte
 * @Example
    IR_DATA=Prepare_IR_NEC_Data(RxAddress, command);
 */
static uint32_t Prepare_IR_NEC_Data(uint8_t devAddress, uint8_t cmd)
{
    uint32_t retData = 0;
    
    retData = devAddress;// receiver address
    retData <<= 8;
    retData |= (uint8_t)~devAddress;//inverse of receiver address
    retData <<= 8;
    retData |= (uint8_t)cmd;//command
    retData <<= 8;
    retData |= (uint8_t)~cmd;//inverse of command
    return retData;   
}
/**
 * @Param
   IR_Data to be transmitted , numOfBitsToSend
 * @Returns
    status=1 if frame transmitted successfully
 *  status=0xFF if error in transmission
 * @Description
    This function is used to send command using NEC IR protocol. 
 *  NEC protocol
    //Logical '0' ? a 562.5?s pulse burst followed by a 562.5?s space, with a total transmit time of 1.125ms
    //Logical '1' ? a 562.5?s pulse burst followed by a 1.6875ms space, with a total transmit time of 2.25ms
    //Complete IR frame for 1byte command transmission
    //9ms leading pulse burst (16 times the pulse burst length used for a logical data bit)
    //4.5ms space
    //the 8-bit address for the receiving device
    //the 8-bit logical inverse of the address
    //the 8-bit command
    //the 8-bit logical inverse of the command
    //a final 562.5?s pulse burst to signify the end of message transmission.
 * @Example
    frameSentFlag= SendIR_Frame(IR_Data, numOfBitsToSend);
 */
static e_status SendIR_Frame (uint32_t IR_Data, uint8_t numOfBitsToSend)
{
    static e_IrTxState logicState = START_TMR0;
    static uint8_t bitCounter = 0;
    static uint32_t tempData = 0;
    
    e_status retValue;
    
    switch (logicState)
    {
    case START_TMR0:
        TIMER0_PR_4_5ms();//Set the Timer overflow to 4.5 ms for start of transmission
        TMR0_WriteTimer(ZERO);
        timer0overflowFlag = 0;
        EnableTMR0Intr();//enable timer 0 overflow interrupt
        TMR0_StartTimer();//start timer 0
        DSM_ManualModulationSet();//set DSM modulation source to transmit MARK in the start of IR frame
        DSM_ModulationStart();
        logicState = START_BIT_MARK;
        break;
    case START_BIT_MARK: //send 9ms leading pulse burst (MARK)   
        if (timer0overflowFlag == MARK_DURATION)//check if timer has overflowed 2 times for 4.5*2=9 ms duration
        {    
            DSM_ManualModulationClear();
            timer0overflowFlag = 0;
            logicState = START_BIT_SPACE;
        }      
        break; 
    case START_BIT_SPACE://send 4.5ms SPACE//9ms MARK + 4.5ms SPACE = Start sequence in NEC IR Frame
        if(timer0overflowFlag)
        {
            tempData = IR_Data;
            bitCounter = numOfBitsToSend;
            if(tempData & BIT31_HIGH) 
            {       
                //logic '1'transmit "1000" a 562.5µs pulse burst followed by a 1.6875ms space
                CLC_logic_1_Initialize();      
                 //DSM source is CLC1 output
                DSM_SRC_Set(SRC_CLC1_OUT);
                logicState = LOGIC_1;
            }
            else 
            { 
                //logic '0'transmit "10"  a 562.5µs pulse burst followed by a 562.5µs space  
                CLC_logic_0_Initialize();  
                //DSM source is CLC5 output
                DSM_SRC_Set(SRC_CLC5_OUT);
                logicState = LOGIC_0;
            }
            tempData <<= 1;            
            TIMER0_PR_562_5us();//change the timer period value to transmit 32 bit data in IR frame at 562.5 us period 
                                //for transmitting Logic '0' or Logic '1'
            timer0overflowFlag = 0;
            // Disabling TMR0 interrupt. All interrupts are handled in CLC1 and CLC5 ISR
            DisableTMR0Intr();
        }
        break;
    case LOGIC_1:
       if(clc1risingEdgeFlag)
        {    
            if(bitCounter == 0) //check if all bits are transmitted
            {  
                DSM_ManualModulationSet();//for 562.5 us stop bit
                DSM_SRC_Set(SRC_SW_BIT);// DSM source is software bit;
                logicState = STOP_BIT;
                CLC_logic_1_Stop();
                clc1FirstRisingEdge = false;
                EnableTMR0Intr();// enabling TMR0 interrupt to service stop bit
            } 
            else 
            {        
                if(!(tempData & BIT31_HIGH)) //check if the next data bit to be transmitted is logic 0
                {
                    //enable logic 0 CLC                  
                    CLC_logic_0_Initialize(); 
                    //Set DSM source to CLC5 output
                    DSM_SRC_Set(SRC_CLC5_OUT);//switch to logic 0
                    CLC_logic_1_Stop();  
                    clc1FirstRisingEdge = false;
                    logicState = LOGIC_0;
                }
                bitCounter--;
                tempData <<= 1;
            }
            clc1risingEdgeFlag = 0;
        }
    break;
    case LOGIC_0:
        if(clc5RisingEdgeFlag)
        {    
            if(bitCounter == 0)//check if all bits are transmitted 
            {  
                DSM_ManualModulationSet();//for 562.5 us stop bit
                DSM_SRC_Set(SRC_SW_BIT);// DSM source is software bit;   
                logicState = STOP_BIT;
                CLC_logic_0_Stop();
                EnableTMR0Intr();// enabling TMR0 interrupt to service stop bit 
            } 
            else 
            {        
                if(tempData & BIT31_HIGH)//check if the next data bit to be transmitted is logic 1
                {
                    //enable logic 1 CLCs 
                    CLC_logic_1_Initialize();
                    //Set DSM source to CLC1 output
                    DSM_SRC_Set(SRC_CLC1_OUT);//switch to logic 1
                    CLC_logic_0_Stop();           
                    logicState = LOGIC_1;
                }
                bitCounter--;
                tempData <<= 1;
            }
            clc5RisingEdgeFlag = 0;
        }
    break;
    case STOP_BIT:// All the bits are transmitted, transmit the end sequence a pulse burst of 562.5 us
        if(timer0overflowFlag)
        {
            logicState = STOP_FRAME;
            timer0overflowFlag = 0;
        }        
        break;
    case STOP_FRAME:  // IR frame is transmitted reinitialize get ready for next transmission  
        if(timer0overflowFlag)
        {
            TMR0_StopTimer();
            TIMER0_PR_4_5ms();//reload timer for overflow at every 4.5 ms for start of transmission
            DSM_ManualModulationClear();
            DSM_ModulationStop();//Stop DSM for disabling the modulation
            logicState = START_TMR0;
            timer0overflowFlag = 0;
            DisableTMR0Intr();
            retValue = SUCCESS;
        }
    break;
    default://error state
        retValue = ERROR;
        logicState = START_TMR0;
        break;  
    }
    return retValue;
    
}

/**
 * @Param
    None
 * @Returns
    Status of any switch press event
 * @Description
    This function is used to get information whether there is any event for generating command.
 * @Example
    event_status =GetEvent();
 */
static e_key_state GetEvent(void)
{
    return touch.keyStatus;
}

/**
 * @Param
    None
 * @Returns
    None
 * @Description
    This function is used to clear previous switch press event after 
 *  corresponding  action is taken for the event.
 * @Example
    ClearEvent();
 */
static void ClearEvent(void)
{
    touch.keyStatus = KEY_NO_ACTION;
}

/**
 * @Param
    None
 * @Returns
    Which switch is pressed
 * @Description
    This function is used to get information about which switch is pressed for generating command.
 * @Example
    button=GetButton();
 */
static e_key GetButton(void)
{
    return touch.key;
}

/**
 * @Param
    None
 * @Returns
    None
 * @Description
    This function is used to clear previous switch button presses after 
 *  corresponding  action is taken for the button press event.
 * @Example
    ClearButton();
 */
static void ClearButton(void)
{
    touch.key = NO_KEY;
}
/**
   @Param
    ADC value
   @Returns
    speed change command 
   @Description
    function for getting the motor speed change command corresponding to the slider position 
   @Example 
    command= GetMotorSpeed(adcVal);   
 */

static e_commands GetMotorSpeed(uint16_t adcVal)
{
    e_commands retVal = NONE;

    if (adcVal < SLIDER_COUNT_MIN)
    {//turn off all the slider LEDs
        TurnOffSliderLEDs();
        retVal = SPEED_0;
    }
    else if ((adcVal > (SLIDER_COUNT_MIN - 1) ) && (adcVal < SLIDER_COUNT_1))
    {//turn on slider LED 1
        TurnOnSliderLED1();
        retVal = SPEED_1;
    }
    else if ((adcVal > (SLIDER_COUNT_1 - 1)) && (adcVal < SLIDER_COUNT_2))
    {//turn on slider LED 1 and 2
        TurnOnSliderLED2();
        retVal = SPEED_2;
    }
    else if ((adcVal > (SLIDER_COUNT_2 - 1)) && (adcVal < SLIDER_COUNT_3))
    {//turn on slider LED 1, 2 and 3
        TurnOnSliderLED3();
        retVal = SPEED_3;
    }
    else if ((adcVal > (SLIDER_COUNT_3 - 1)) && (adcVal < SLIDER_COUNT_4))
    {//turn on slider LED 1, 2, 3 and 4
        TurnOnSliderLED4();
        retVal = SPEED_4;
    }
    else if ((adcVal > (SLIDER_COUNT_4 - 1)) && (adcVal < SLIDER_COUNT_MAX))
    {//turn on slider LED 1, 2, 3, 4 and 5
        TurnOnSliderLED5();
        retVal = SPEED_5;
    }
    else if (adcVal > (SLIDER_COUNT_MAX - 1) )
    {//turn on all the slider LEDs 1, 2, 3, 4, 5 and 6
        TurnOnSliderLED6();
        retVal = SPEED_6;
    }
    return retVal;
}
