/*
 * Copyright (c) 2013 - 2016, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * 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 Freescale Semiconductor, Inc. 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 "Touchpad_KL25z_aslider.h"


static void init_freemaster_uart(void);

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);
#if USE_MATRIX
static void matrix_callback(const struct nt_control *control, 
                            enum nt_control_matrix_event event,
                            nt_coordinates16_xy *position,
                            uint32_t index);
#endif
const pit_config_t my_pit_config = {
    .enableRunInDebug = false,     /*!< Timer runs in debug mode*/
};




/*
 * This list describes all TSA tables which should be exported to the 
 * FreeMASTER application.
 */
#ifndef FMSTR_PE_USED
  FMSTR_TSA_TABLE_LIST_BEGIN()
      FMSTR_TSA_TABLE(nt_frmstr_tsa_table)
  FMSTR_TSA_TABLE_LIST_END()
#endif
    
#define nt_printf(...) /* do nothing - the debug lines is used by freemaster */    

#define JUMPER_GPIO GPIOD
#define JUMPER_PIN  2

 /* 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 */
volatile uint32_t delay_double_tap_LED = 0; 
volatile uint32_t delay_click_indicate =200;

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

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

};  

void PIT_IRQHandler(void)
{
  int32_t result = 0;
  /* Clear interrupt flag.*/
  PIT_ClearStatusFlags(PIT, kPIT_Chnl_0, 1);
  result = nt_trigger();
  
//  if ( result ==NT_FAILURE)
//  {
//     LED_TOGGLE(LED_GREEN_GPIO,LED_GREEN_PIN);
//  } else
//  {
//   //LED_OFF(LED_GREEN_GPIO,LED_GREEN_PIN);
//  }

}

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

/* ******************** MAIN FUNCTION ****************************************/
int main(void)
{
 volatile uint8_t jumper_on;
  /* Init board hardware. */
  BOARD_InitPins();
  BOARD_BootClockRUN();
  USB_DeviceApplicationInit();  

  /*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);


      /*Debug jumper detect */
  if(GPIO_ReadPinInput(JUMPER_GPIO, JUMPER_PIN) )
  {
    jumper_on = DEBUG_DISABLE; 
    BOARD_InitPins_DEBUG(); // Switch DEBUG pins to TSI!        
    
  } else {
  jumper_on =  DEBUG_ENABLE;
  }
  set_sebug(jumper_on);
  
  
  /* FreeMASTER will communicate over default UART instance */
  init_freemaster_uart();
  
  /* FreeMASTER initialization */
  (void)FMSTR_Init();
  
  /*NTouch library inicialization*/
  NXPTouch_init(jumper_on);
 
  
    /* 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(LPTMR0_IRQn);
    
  /* Init PIT */
  PIT_Init(PIT, &my_pit_config);
  /* Set PIT0 period 5ms */
  PIT_SetTimerPeriod(PIT, kPIT_Chnl_0, 5*CLOCK_GetBusClkFreq()/1000);
  
  /* Enable PIT interrupt */
  PIT_EnableInterrupts(PIT, kPIT_Chnl_0, PIT_TCTRL_TIE_MASK);
  EnableIRQ(PIT_IRQn);
  /* Start the PIT timer */
  PIT_StartTimer(PIT, kPIT_Chnl_0);
  /* Start counting */
  LPTMR_StartTimer(LPTMR0);
  
    /*START delay measurement for mouse click indication*/
    delay_double_tap_LED= tick_counter;  
    int8_t result = 0; 
  while(1)
  {
    result = nt_task();
    if (result == NT_SUCCESS)
    { /*If new data prepared, call mouse click function*/
     mouse_click_actions(PERIODIC);
     hid_report.mouse_complete = 1;
     hid_report.keyboard_complete = 1;
      
    }
    /*Delay for mouse click indication*/
    if (is_time_now(true, &delay_double_tap_LED, delay_click_indicate))
    {
        LED_OFF(LED_GREEN_GPIO,LED_GREEN_PIN);
    }
    
   FMSTR_Poll();
  }
}

#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 > 10) || (temp < -10)))
         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;
  }
}

#if USE_MATRIX
static void matrix_callback(const struct nt_control *control, 
                            enum nt_control_matrix_event event,
                            nt_coordinates16_xy *position,
                            uint32_t index)
{
  nt_coordinates16_xy * finger_position;
  finger_position = position;
  
  uint32_t dX,dY, c2;
  static uint32_t c2_old =0;
  
  /* according to event switch to: */
  switch(event)
  {
    //    case NT_MATRIX_GET_CONTROL_GESTURE: /* get control gesture */ 
    //      {
    //      switch ((enum nt_control_matrix_control_gestures)index)
    //        {
    //
    //        case NT_MATRIX_DOUBLE_TAP: 
    //            {  
    //
    //            }   
    //        break;
    //        default:
    //        break;
    //        }
    //      }
    //      break;
    
  case NT_MATRIX_MOVEMENT :
    
    if ( finger_position[1].x)
    { // rectangular triangle
      // c2 = dX * dx + dY*dY;
      dX = finger_position[0].x - finger_position[1].x;
      dY = finger_position[0].y - finger_position[1].y;
      c2 = dY*dY + dX*dX;  
      
      if (c_old != -1)
      {
        LED_TOGGLE(RGB_GREEN_GPIO,RGB_GREEN_PIN);
        hid_report.Scrool = (((int32_t)c2)-c2_old);
        hid_report.keyboard_modifier_button = MODIFERKEYS_RIGHT_CTRL;
      }   
      c2_old = c2;
    }
    break;
  case NT_MATRIX_RELEASE :
    c2_old = -1;
    break;
  default:
    break;
  }
}

#endif

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++)<6)
        {
         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)
{
    uint32_t lpsciSourceClock;
    lpsci_config_t uartConfig;
    
    uartConfig.baudRate_Bps = BOARD_DEBUG_UART_BAUDRATE;
    uartConfig.parityMode = kLPSCI_ParityDisabled;
    uartConfig.stopBitCount = kLPSCI_OneStopBit;
     
    uartConfig.enableTx = true;
    uartConfig.enableRx = true;
    
    /* Select different clock source for LPSCI. */
    CLOCK_SetLpsci0Clock(1u);
    lpsciSourceClock = CLOCK_GetFreq(kCLOCK_PllFllSelClk);
    /* Init UART */
    LPSCI_Init((UART0_Type *)BOARD_DEBUG_UART_BASEADDR, &uartConfig, lpsciSourceClock);
  
    /* Enable UART interrupt on NVIC level. */
    EnableIRQ(BOARD_UART_IRQ);
    
    /* Base address is configured dynamically in this application, making slightly 
       bigger/slower code but more generic. See freemaster_cfg.h for more details */
    FMSTR_SetSciBaseAddress((FMSTR_ADDR)(UART_Type *)BOARD_DEBUG_UART_BASEADDR);
}

/*!
 * @brief Example of direct interrupt vector installation - overriding the weak
 * symbol which is put in 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 a value of BOARD_DEBUG_UART_INSTANCE
 * macro defined in board.h)
 * 
 * This 'direct' approach may be used instead of OSA_InstallIntHandler
 * above, for example when ROM-based interrupt table is used.
 */
 void BOARD_UART_IRQ_HANDLER(void)
 {
     FMSTR_Isr();
 }

void NXPTouch_init(uint8_t debug_on)
{
  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);

  if (debug_on == DEBUG_DISABLE) 
  {
   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);
#if USE_MATRIX   
  nt_control_enable(&matrix_0);
  nt_control_matrix_register_callback(&matrix_0, &matrix_callback); 
#endif  
  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);

}