/*
 * relay.c
 *
 *  Created on: Jul 16, 2010
 *      Author: epics
 */

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#include "api.h"
#include "api_define.h"
#include "api_global.h"
#include "adc.h"
#include "one_wire_temperature.h"

#include "relay.h"

static const char relayThresholdCommandKeyword00[] PROGMEM = "thr_high";
static const char relayThresholdCommandKeyword01[] PROGMEM = "thr_low";
static const char relayThresholdCommandKeyword02[] PROGMEM = "enable";
static const char relayThresholdCommandKeyword03[] PROGMEM = "input_channel";
static const char relayThresholdCommandKeyword04[] PROGMEM = "output_pin";
static const char relayThresholdCommandKeyword05[] PROGMEM = "led_pin";

const char* relayThresholdCommandKeywords[] PROGMEM = {
         relayThresholdCommandKeyword00,
         relayThresholdCommandKeyword01,
         relayThresholdCommandKeyword02,
         relayThresholdCommandKeyword03,
         relayThresholdCommandKeyword04,
         relayThresholdCommandKeyword05
};

void relayThresholdInit(void)
{
   uint32_t currentValue = 0xFFFFFFFF;

   currentValue = relayThresholdsGetCurrentValue(); // returns value + error information on top (16 MSB)

   if ( 0 != (currentValue >> 16))
   {
      // error
      return;
   }
   else
   {
      // to be sure, remove back packer info;
      currentValue = (currentValue & 0xFFFF);
   }

   /* checks passed */

   PORTA = PORTA | ( ( 0x1 << relayThresholdLedPin ) ); // turn on lamp

   if ( currentValue < relayThresholdLow )
   {
      relayThresholdCurrentState = relayThresholdState_BELOW_LOW_THRESHOLD;
      PORTA = PORTA & ( ~( 0x1 << relayThresholdOutputPin) ); // turn off relay
   }
   else if ( currentValue > relayThresholdHigh )
   {
      relayThresholdCurrentState = relayThresholdState_ABOVE_HIGH_THRESHOLD;
      PORTA = PORTA & ( ~( 0x1 << relayThresholdOutputPin) ); // turn off relay
   }
   else
   {
      relayThresholdCurrentState = relayThresholdState_INBETWEEN;
      PORTA = PORTA | ( ( 0x1 << relayThresholdOutputPin) ); // turn on relay
   }

   if (TRUE != relayThresholdReportState_flag)
   {
      /* report current state and value */
   }
}

void relayThresholdDetermineStateAndTriggerRelay(void)
{
   uint32_t currentValue = 0xFFFFFFFF;

   currentValue = relayThresholdsGetCurrentValue(); // returns value + error information on top (16 MSB)

   if ( 0 != (currentValue >> 16))
   {
      // error
      return;
   }
   else
   {
      // to be sure, remove back packer info;
      currentValue = (currentValue & 0xFFFF);
   }

   /* checks passed */

   switch ( relayThresholdCurrentState )
   {
      case relayThresholdState_INBETWEEN:
         {
            if ( currentValue > relayThresholdHigh )
            {
               /*turn off relays*/
               PORTA = PORTA & ( ~( 0x1 << relayThresholdOutputPin) );
               relayThresholdCurrentState = relayThresholdState_ABOVE_HIGH_THRESHOLD;
            }
            if ( currentValue < relayThresholdLow )
            {
               /*turn off relays*/
               PORTA = PORTA & ( ~( 0x1 << relayThresholdOutputPin) );
               relayThresholdCurrentState = relayThresholdState_BELOW_LOW_THRESHOLD;
            }
         }
         break;
      case relayThresholdState_BELOW_LOW_THRESHOLD:
         {
            _delay_ms(100);
            _delay_ms(100);
            PINA = ( 0x1 << relayThresholdLedPin ); // toggle LED

            if ( currentValue > relayThresholdLow + 2 )
            {
               /*turn on relays*/
               PORTA = PORTA | ( 0x3 );
               relayThresholdCurrentState = relayThresholdState_INBETWEEN;
            }
         }
         break;
      case relayThresholdState_ABOVE_HIGH_THRESHOLD:
         {

            _delay_ms(100);
            _delay_ms(100);
            PINA = ( 0x1 << relayThresholdLedPin ); // toggle LED

            if ( currentValue < relayThresholdHigh - 2 )
            {
               /* turn on relays and lamp*/
               PORTA = PORTA | ( ( 0x1 << relayThresholdLedPin ) | ( 0x1 << relayThresholdOutputPin) );
               relayThresholdCurrentState = relayThresholdState_INBETWEEN;
            }
         }
         break;
      default:
         {
            relayThresholdCurrentState = relayThresholdState_BELOW_LOW_THRESHOLD;
         }
   }

#endif
}

uint8_t relayThresholdsSetHighThreshold(uint16_t value)
{
   uint8_t status = 0;

   relayThresholdHigh = value;

   relayThresholds_valid[relayThreshold_HIGH] = TRUE;

   return status;
}

uint8_t relayThresholdsSetLowThreshold(uint16_t value)
{
   uint8_t status = 0;

   relayThresholdLow = value;

   relayThresholds_valid[relayThreshold_LOW] = TRUE;

   return status;
}

uint8_t relayThresholdsSetEnableFlag(uint8_t value)
{
   uint8_t status = 0;
   relayThresholdEnable_flag = ( 0 != value);
   return status;
}

uint8_t relayThresholdsSetInputChannel(uint8_t value)
{
   uint8_t status = 0;
   if ( value <= 7)
   {
      relayThresholdInputChannel = value;
   }
   else
   {
      CommunicationError(ERRA, -1, 0, PSTR("invalid argument"), -1);
      status = -1;
   }
   return status;
}

uint8_t relayThresholdsSetOutputPin(uint8_t value)
{
   uint8_t status = 0;
   if ( value <= 7)
   {
      relayThresholdOutputPin = value;
   }
   else
   {
      CommunicationError(ERRA, -1, 0, PSTR("invalid argument"), -1);
      status = -1;
   }
   return status;
}

uint8_t relayThresholdsSetLedPin(uint8_t value)
{
   uint8_t status = 0;
   if ( value <= 7)
   {
      relayThresholdLedPin = value;
   }
   else
   {
      CommunicationError(ERRA, -1, 0, PSTR("invalid argument"), -1);
      status = -1;
   }
   return status;
}

void relayThresholdMiscSubCommands(struct uartStruct *ptr_uartStruct )
{
   uint8_t status = 0;
   uint32_t value = 0;
   uint16_t index = 0;
   // find matching temperature command keyword
   while (index < relayThresholdCommandKeyNumber_MAXIMUM_NUMBER)
   {
      if ( 0 == strncmp_P(setParameter[1], (const char*) (pgm_read_word( &(relayThresholdCommandKeywords[index]))), MAX_LENGTH_PARAMETER) )
      {
         break;
      }
      index++;
   }

   switch(ptr_uartStruct->number_of_arguments - 1 /* arguments of argument */)
   {
      case 0:
         /* printout status*/

         /* generate message */
         createExtendedSubCommandReceiveResponseHeader(ptr_uartStruct, -1,
                                                       owiTemperatureCommandKeyNumber_FORCE_PARASITIC_MODE,
                                                       owiTemperatureCommandKeywords);
         switch (index)
         {
            case relayThresholdCommandKeyNumber_THR_HIGH:
               value = relayThresholdsGetHighThreshold();
               snprintf_P(uart_message_string,BUFFER_SIZE -1, "%s %x",value);
               break;
            case relayThresholdCommandKeyNumber_THR_LOW:
               value = relayThresholdsGetLowThreshold();
               snprintf_P(uart_message_string,BUFFER_SIZE -1, "%s %x",value);
               break;
            case relayThresholdCommandKeyNumber_ENABLE:
               value = relayThresholdsGetEnableFlag();
               strncat_P(uart_message_string, ( FALSE != value) ? PSTR(" TRUE"):PSTR(" FALSE"),BUFFER_SIZE -1);
               break;
            case relayThresholdCommandKeyNumber_INPUT_CHANNEL:
               value = relayThresholdsGetInputChannel();
               snprintf_P(uart_message_string,BUFFER_SIZE -1, "%s %x",value);
               break;
            case relayThresholdCommandKeyNumber_OUTPUT_PIN:
               value = relayThresholdsGetOutputPin();
               snprintf_P(uart_message_string,BUFFER_SIZE -1, "%s %x",value);
               break;
            case relayThresholdCommandKeyNumber_LED_PIN:
               value = relayThresholdsGetLedPin();
               snprintf_P(uart_message_string,BUFFER_SIZE -1, "%s %x",value);
               break;
            default:
               clearString(uart_message_string, BUFFER_SIZE);
               CommunicationError(ERRA, -1, 0, PSTR("invalid argument"), -1);
               return;
               break;
         }

         /*send the data*/
         UART0_Send_Message_String(uart_message_string, BUFFER_SIZE - 1);
         /*clear strings*/
         clearString(uart_message_string, BUFFER_SIZE);
         break;
      case 1:
         /* set status*/

         if (0 < getNumericLength(setParameter[2],MAX_LENGTH_PARAMETER))
         {
            value = strtoul(setParameter[2], &ptr_setParameter[2], 16);
         }
         else
         {
            CommunicationError(ERRA, -1, 0, PSTR("invalid argument"), -1);
            return;
         }

         switch (index)
         {
            case relayThresholdCommandKeyNumber_THR_HIGH:
               status = relayThresholdsSetHighThreshold(value & 0xFFFF);
               break;
            case relayThresholdCommandKeyNumber_THR_LOW:
               status = relayThresholdsSetLowThreshold(value & 0xFFFF);
               break;
            case relayThresholdCommandKeyNumber_ENABLE:
               status = relayThresholdsSetEnableFlag(0 != value );
               break;
            case relayThresholdCommandKeyNumber_INPUT_CHANNEL:
               status = relayThresholdsSetInputChannel(value & 0xF);
               break;
            case relayThresholdCommandKeyNumber_OUTPUT_PIN:
               status = relayThresholdsSetOutputPin(value & 0xF);
               break;
            case relayThresholdCommandKeyNumber_LED_PIN:
               status = relayThresholdsSetLedPin(value & 0xF);
               break;
            default:
               CommunicationError(ERRA, -1, 0, PSTR("invalid argument"), -1);
               return;
               break;
         }

         /*recursive call to show change*/
         if (0 == status)
         {
            ptr_uartStruct->number_of_arguments=1;
            relayThresholdMiscSubCommands(ptr_uartStruct);
            ptr_uartStruct->number_of_arguments=2;
         }
         break;
      default:
         general_errorCode = CommunicationError(ERRG, -1001, TRUE, PSTR("invalid number of arguments"), 101);
         break;
   }

   if (FALSE != relayThresholdEnable_flag && FALSE != relayThresholds_valid[relayThreshold_LOW]
                                                                   && FALSE != relayThresholds_valid[relayThreshold_LOW] )
   {
      relayThresholdInit();
   }

   return;
}

void relayThreshold(struct uartStruct *ptr_uartStruct)
{
   relayThresholdMiscSubCommands(ptr_uartStruct);
}

uint16_t relayThresholdsGetHighThreshold(void)
{
   return relayThresholdHigh;
}

uint16_t relayThresholdsGetLowThreshold(void)
{
   return relayThresholdLow;
}

uint8_t relayThresholdsGetEnableFlag(void)
{
   return relayThresholdEnable_flag;
}

uint8_t relayThresholdsGetInputChannel(void)
{
   return relayThresholdInputChannel;
}

uint8_t relayThresholdsGetOutputPin(void)
{
   return relayThresholdOutputPin;
}

uint8_t relayThresholdsGetLedPin(void)
{
   return relayThresholdLedPin;
}

uint8_t relayThresholdsGetCurrentState(void)
{
   return relayThresholdCurrentState;
}

uint32_t relayThresholdsGetCurrentValue(void)
{
   uint8_t status = eNoError;

   /*pressure is checked only for channel 0*/
   status = atmelReadSingleADCChannelVoltage(relayThresholdInputChannel);

   /* set global status*/
   switch (relayThresholdInputChannel)
   {
      case 0:
         res0 = status;
         break;
      case 1:
         res1 = status;
         break;
      case 2:
         res2 = status;
         break;
      case 3:
         res3 = status;
         break;
      case 4:
         res4 = status;
         break;
      case 5:
         res5 = status;
         break;
      case 6:
         res6 = status;
         break;
      case 7:
         res7 = status;
         break;
      default:
         snprintf_P(message, BUFFER_SIZE - 1,
                    PSTR("error accessing ADC wrong channel: %i"), ptr_uartStruct->Uart_Message_ID);
         CommunicationError(ERRG, -100, TRUE, NULL,
                            COMMUNICATION_ERROR_USE_GLOBAL_MESSAGE_STRING_INDEX_THRESHOLD - 100 );
         return 0x2L << 16;
         break;
   }

   /* check status return*/
   if ( status != eNoError )
   {
      snprintf_P(message, BUFFER_SIZE - 1,
                 PSTR("error accessing ADC channel %i"), ptr_uartStruct->Uart_Message_ID);
      CommunicationError(ERRG, -100, TRUE, PSTR("error accessing ADCs"),
                         COMMUNICATION_ERROR_USE_GLOBAL_MESSAGE_STRING_INDEX_THRESHOLD - 100 );

      return 0x1L << 16;
   }

   return Voltages[relayThresholdInputChannel];
}
