/*
 * Copyright (c) 2013 - 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2017 NXP
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 


#include "main.h"
static void aslider_x_callback(const struct nt_control *control, 
                             enum nt_control_aslider_event event,
                             uint32_t position);


static void aslider_y_callback(const struct nt_control *control, 
                             enum nt_control_aslider_event event,
                             uint32_t position);

uint8_t nt_memory_pool[5000]; /* IAR EWARM compiler */


volatile struct hid_composite_t hid_report = {
  .moveUD = STOP,
  .moveLR = STOP,
  .Scrool = STOP,
  .mouse_button = STOP
};


const lpit_config_t my_pit_config = {
    .enableRunInDebug = false,     /*!< Timer doesn't run in debug mode*/
    .enableRunInDoze  = false,     /*!< Timer doesn't run in doze mode*/
};

/* Get source clock for LPTMR driver */
#define LPTMR_SOURCE_CLOCK CLOCK_GetFreq(kCLOCK_LpoClk)
/*LPTMR period */
#define LPTMR_USEC_COUNT 1000U /*in uS*/

/*DELAY Function */
uint32_t delay_double_tap_LED = 0; 
volatile uint32_t delay_click_indicate =200;

void PWT_LPTMR0_IRQHandler(void)
{
    LPTMR_ClearStatusFlags(LPTMR0, kLPTMR_TimerCompareFlag);

    tick_counter++; // non blocking delay utility tisk every 1mS

};  

int main(void)
{
   /* Init board hardware */
  BOARD_InitPins();

  BOARD_BootClockRUN();
   
  /*Indication led init */
  LED_INIT(LED_RED_GPIO,LED_RED_PIN,LOGIC_LED_OFF);
  LED_INIT(LED_GREEN_GPIO,LED_GREEN_PIN,LOGIC_LED_OFF);
  LED_INIT(RGB_RED_GPIO,RGB_RED_PIN,LOGIC_LED_OFF);
  LED_INIT(RGB_BLUE_GPIO,RGB_BLUE_PIN,LOGIC_LED_OFF);
  LED_INIT(RGB_GREEN_GPIO,RGB_GREEN_PIN,LOGIC_LED_OFF);
  
  BOARD_InitDebugConsole();
  //PRINTF("Hello I am: Touchpad KE15z! \r\n");

  /* FreeMASTER communicates over the default UART instance */
  init_freemaster_uart();
  
  /* FreeMASTER initialization */
  (void)FMSTR_Init();
    
  
  /*NTouch library inicialization*/
  NXPTouch_init();
  
    /* Configure LPTMR */
    /*
    * lptmrConfig.timerMode = kLPTMR_TimerModeTimeCounter;
    * lptmrConfig.pinSelect = kLPTMR_PinSelectInput_0;
    * lptmrConfig.pinPolarity = kLPTMR_PinPolarityActiveHigh;
    * lptmrConfig.enableFreeRunning = false;
    * lptmrConfig.bypassPrescaler = true;
    * lptmrConfig.prescalerClockSource = kLPTMR_PrescalerClock_1;
    * lptmrConfig.value = kLPTMR_Prescale_Glitch_0;
    */
    lptmr_config_t lptmrConfig;
    LPTMR_GetDefaultConfig(&lptmrConfig);

    /* Initialize the LPTMR */
    LPTMR_Init(LPTMR0, &lptmrConfig);

    /*
    * Set timer period.
    * Note : the parameter "ticks" of LPTMR_SetTimerPeriod should be equal or greater than 1.
    */
    LPTMR_SetTimerPeriod(LPTMR0, USEC_TO_COUNT(LPTMR_USEC_COUNT, LPTMR_SOURCE_CLOCK));

    /* Enable timer interrupt */
    LPTMR_EnableInterrupts(LPTMR0, kLPTMR_TimerInterruptEnable);

    /* Enable at the NVIC */
    EnableIRQ(PWT_LPTMR0_IRQn);
    /* Start counting */
    LPTMR_StartTimer(LPTMR0);
  
  pit_init();
//  delay_tap = tick_counter; /*Reset delay*/
  
  uint8_t result = 0;
  while(1)
  {
    result = nt_task();
    if(result == NT_SUCCESS)
    { // send report to KL25z every new data
      mouse_click_actions(PERIODIC);
  
      printf("?L%04dU%04dS%04dB%03dK%03d@", hid_report.moveLR,hid_report.moveUD,hid_report.Scrool,hid_report.mouse_button,hid_report.keyboard_modifier_button);
      hid_report.moveLR = STOP;
      hid_report.moveUD = STOP;
      hid_report.Scrool = STOP;
      hid_report.mouse_button = STOP;
      hid_report.keyboard_modifier_button= STOP;
    }
    
    if (is_time_now(true, &delay_double_tap_LED, delay_click_indicate))
    {
      LED_OFF(LED_GREEN_GPIO,LED_GREEN_PIN);
    }
    

    /* The FreeMASTER poll call must be called in the main application loop
    to handle the communication interface and protocol. 
       In the LONG_INTR FreeMASTER interrupt mode, all the processing is done 
    during the communication interrupt routine and the FMSTR_Poll() is 
    compiled empty. */
    FMSTR_Poll();
  }
}


void LPIT0_IRQHandler(void)
{
  int32_t result = 0;
  /* Clear the interrupt flag.*/
  LPIT_ClearStatusFlags(LPIT0, LPIT_MSR_TIF0_MASK);
   result = nt_trigger();
  if ( result ==NT_FAILURE)
  {
  //LED_TOGGLE(LED_RED_GPIO,LED_RED_PIN);
   } 
    
}

void TSI_IRQHandler(void)
{
  TSI_DRV_IRQHandler(0);
}

static void pit_init(void)
{
  /* Set the source for the LPIT module */
  CLOCK_SetIpSrc(kCLOCK_Lpit0, kCLOCK_IpSrcSircAsync);

  /* Init PIT */
  LPIT_Init(LPIT0, &my_pit_config);

  /* Set PIT0 period 5ms */
  LPIT_SetTimerPeriod(LPIT0, kLPIT_Chnl_0, 35*CLOCK_GetFreq(kCLOCK_ScgSircAsyncDiv2Clk)/1000);

    /* Enable the PIT interrupt */
  LPIT_EnableInterrupts(LPIT0, LPIT_MIER_TIE0_MASK);
  EnableIRQ(LPIT0_IRQn);

  /* Start the PIT timer */
  LPIT_StartTimer(LPIT0, kLPIT_Chnl_0);
}
#define DELTA 50
#define MULTIPLIER 2

static void aslider_y_callback(const struct nt_control *control, 
                             enum nt_control_aslider_event event,
                             uint32_t position)
{
  static int32_t y_old = -1;/*Old Y position*/
  int32_t temp ;

    switch(event)
  {
  case NT_ASLIDER_INITIAL_TOUCH:
   asm("nop");
   
    break;
  
  case NT_ASLIDER_MOVEMENT:
    if(!(y_old <0))
    {
        temp = position - y_old;
      
        if (temp > DELTA)
            y_old = position -5;
         
        if (temp< -DELTA)
            y_old = position + 5;
        
         if(nt_control_aslider_get_invalid_position(&aslider_x)) /*Scrool enable*/
          {
           hid_report.Scrool = -(((int32_t)position)-y_old)/4;
            LED_ON(RGB_GREEN_GPIO,RGB_GREEN_PIN);
            LED_ON(RGB_RED_GPIO,RGB_RED_PIN);
            LED_OFF(RGB_BLUE_GPIO,RGB_BLUE_PIN);
          } else{
              LED_TOGGLE(RGB_RED_GPIO,RGB_RED_PIN);
              hid_report.moveUD = (((int32_t)position)-y_old)*MULTIPLIER;
              }       
     }
      
     y_old = position; 
     break;
  case NT_ASLIDER_ALL_RELEASE:
    LED_OFF(RGB_RED_GPIO,RGB_RED_PIN);
    LED_OFF(RGB_GREEN_GPIO,RGB_GREEN_PIN);
    y_old = -1; /*REset last position*/
    break;
  }
}
static void aslider_x_callback(const struct nt_control *control, 
                             enum nt_control_aslider_event event,
                             uint32_t position)
{
  
  static int32_t x_old = -1; /*OLD X position*/
  static int8_t zoom_enable = 0; /* ZOOM gescure recognized */
  int32_t temp;
    
   /*Get status of Y electrode*/
  int32_t last_touched_electrode = 0; 
  last_touched_electrode = nt_control_get_touch_button(&aslider_y, last_touched_electrode);
     
  switch(event)
  {
  case NT_ASLIDER_INITIAL_TOUCH:
    LED_ON(LED_RED_GPIO,LED_RED_PIN);
    mouse_click_actions(event); /* Start sequence for mouse click detection*/
     if (last_touched_electrode >=5)/*If first touch is to ZOMM ZONE */
         zoom_enable = 1;        
     else 
         zoom_enable = 0;  
       
    break;
  case NT_ASLIDER_MOVEMENT:
   
    if(!(x_old < 0)) /*Prevent dump movement*/
    {
      temp = position - x_old;
      if (!((temp > 15) || (temp < -15)))
         mouse_click_actions(event);/*MOVEMENT more than limit! STOP TOUCH DETECTION */
   
     if (temp > DELTA)
          x_old = position -5;
       
     if (temp< -DELTA)
          x_old = position + 5;
      
      if((last_touched_electrode >= 5)&& zoom_enable)
      {/*If are currently touched Yslider electrode 5 or 6 = > ZOOM*/
        LED_TOGGLE(RGB_GREEN_GPIO,RGB_GREEN_PIN);
        hid_report.Scrool = (((int32_t)position)-x_old)/6;
        hid_report.keyboard_modifier_button = MODIFERKEYS_RIGHT_CTRL;
      }else{
        LED_TOGGLE(RGB_BLUE_GPIO,RGB_BLUE_PIN);
        hid_report.moveLR = (((int32_t)position)-x_old)*MULTIPLIER;
         /*STOP zoom function*/
         zoom_enable = 0; /*If finger leaves ZOOM area, turn zoom off until next first touch*/
        }
      }
    x_old = position;
    break;
  case NT_ASLIDER_ALL_RELEASE:
    LED_OFF(LED_RED_GPIO,LED_RED_PIN);
    LED_OFF(RGB_BLUE_GPIO,RGB_BLUE_PIN);
    x_old = -1;
    
    /*STOP zoom function*/
   zoom_enable = 0;
    break;
  }
}

void mouse_click_actions(uint8_t action)
{ /*call from main loop if new data is OK, or from callback. 
  *     SOME RECURSION call in state SAMPLING and RESULT*/
  static uint8_t state = WAIT_FOR_TOUCH;
  static uint16_t shift_register = 0;
  static uint8_t call_cnt = 0;
   
  if (action == NT_ASLIDER_MOVEMENT)
      state = RESET;
       
   switch (state)
    {
    case WAIT_FOR_TOUCH :
      if(action == NT_ASLIDER_INITIAL_TOUCH)
      {
       shift_register = 0; //first touch
       state = SAMPLING;
      }
      break;
    case SAMPLING:
        if ((call_cnt++)<10)
        {
         shift_register = shift_register<<1;
         if (nt_control_aslider_is_touched(&aslider_x))
            shift_register++; // if touch, add 1 to shif register
        }else{
          state = RESULT;
          mouse_click_actions(PERIODIC); /*RECURSION For immediate jump to "RESULT" state.*/
            }
      break;
    case RESULT :
      if ((!( (shift_register)& 0x0003 ) )&& shift_register > 32)
      {
          hid_report.mouse_button = LEFT;
          LED_TOGGLE(LED_GREEN_GPIO,LED_GREEN_PIN);
          reset_delay(&delay_double_tap_LED);
      }
      state = RESET;
      mouse_click_actions(PERIODIC); /*RECURSION For immediate jump to "RESET" state.*/
      break;
    case RESET :
        shift_register = 0;
        call_cnt = 0;
        state = WAIT_FOR_TOUCH;
      break;
  }    
}
/*!
 * @brief UART Module initialization
 */
static void init_freemaster_uart(void)
{

    lpuart_config_t lpuartConfig;
    
    CLOCK_SetIpSrc(BOARD_FREEMASTER_UART_CLK, BOARD_FREEMASTER_UART_CLKSRC);
    uint32_t uartClkSrcFreq = CLOCK_GetIpFreq(BOARD_FREEMASTER_UART_CLK);
    
    /* UART configuration structure */
    LPUART_GetDefaultConfig(&lpuartConfig);
    lpuartConfig.baudRate_Bps     = BOARD_FREEMASTER_UART_BAUDRATE;
    lpuartConfig.enableTx         = true;
    lpuartConfig.enableRx         = true;
    
    /* Init UART */
    LPUART_Init((LPUART_Type *)BOARD_FREEMASTER_UART_BASEADDR, &lpuartConfig, uartClkSrcFreq);
    
    /* Enable UART interrupt */
    EnableIRQ(BOARD_FREEMASTER_UART_IRQ);
    
    /* The base address is configured dynamically in this application, making the code slightly 
       bigger/slower but more generic. See freemaster_cfg.h for more details */
    /*   The base address is offset by 0x10 due to some new regs in the KE15Z LPUART. */
    FMSTR_SetSciBaseAddress((FMSTR_ADDR)(LPUART_Type *)(BOARD_FREEMASTER_UART_BASEADDR + 0x10));
}

/*!
 * @brief An example of a direct interrupt vector installation - overriding the weak
 * symbol which is put into the vector table. Note that the function name
 * contains the UART instance number. Replace the UART1 in the name with the
 * proper instance number (for example, the value of the BOARD_DEBUG_UART_INSTANCE
 * macro defined in board.h)
 * 
 * This 'direct' approach may be used instead of OSA_InstallIntHandler
 * (above), for example, when the ROM-based interrupt table is used.
 */
 
 void BOARD_FREEMASTER_UART_IRQ_HANDLER(void)
 {
     FMSTR_Isr();
 }
void NXPTouch_init(void)
{
  int32_t result;
  if ((result = nt_init(&system_0, nt_memory_pool, sizeof(nt_memory_pool))) != NT_SUCCESS)
  {
    switch(result)
    {
    case NT_FAILURE:
      nt_printf("\nCannot initialize NXP Touch due to non specific error.\n");
      break;
    case NT_OUT_OF_MEMORY:
      nt_printf("\nCannot initialize NXP Touch due to not enough memory.\n");
      break;
    }
    while(1); /* add code to handle this error */
  }
  
  nt_printf("\nThe NXP Touch has been successfully initialized.\n");

  nt_printf("Unused memory: %d bytes, you can make the memory pool smaller without affecting the functionality.\n", nt_mem_get_free_size());

  nt_electrode_enable(&electrode_X0, 0);
  nt_electrode_enable(&electrode_X1, 0);
  nt_electrode_enable(&electrode_X2, 0);
  nt_electrode_enable(&electrode_X3, 0);
  nt_electrode_enable(&electrode_X4, 0);
  nt_electrode_enable(&electrode_X5, 0);
  nt_electrode_enable(&electrode_X6, 0);
  nt_electrode_enable(&electrode_X7, 0);
  nt_electrode_enable(&electrode_X8, 0);
  
  nt_electrode_enable(&electrode_Y0, 0);  
  nt_electrode_enable(&electrode_Y1, 0);
  nt_electrode_enable(&electrode_Y2, 0);
  nt_electrode_enable(&electrode_Y3, 0);
  nt_electrode_enable(&electrode_Y4, 0);
  nt_electrode_enable(&electrode_Y5, 0);
  nt_electrode_enable(&electrode_Y6, 0);
  nt_electrode_enable(&electrode_Y7, 0);
//  nt_control_enable(&matrix_0);
//  nt_control_matrix_register_callback(&matrix_0, &matrix_callback); 
  nt_control_enable(&aslider_x);
  nt_control_aslider_register_callback(&aslider_x, &aslider_x_callback);
  nt_control_enable(&aslider_y);
  nt_control_aslider_register_callback(&aslider_y, &aslider_y_callback);

}