/**********************************************************************************************//**
  @file       sys_utils.c
  @date       $Date$
  @revision   $Revision$

  @brief      This file contains system level utilities
**************************************************************************************************/

#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include "ComTypeDefs.h"
#include "hal_nv_ti.h"
#include "hal_uart_ti.h"
#include "hal_adc12.h"
#include "cavalier_board_cfg_rev2.h"
#include "sys_utils.h"
#include "Versions.h"
#include "analog_sensors.h"
#include "sys_tick.h"

#ifdef RELEASE

#pragma constseg="FW_MAJ"
__root const uint8_t FwMajor = FIRMWAREVERSION_MAJOR;

#pragma constseg="FW_MIN"
__root const uint8_t FwMinor = FIRMWAREVERSION_MINOR;

#pragma constseg="FW_REV"
__root const uint16_t FwRevision = FIRMWAREVERSION_REVISION;

#pragma constseg="FW_BUILD"
__root const uint16_t FwBuild = FIRMWAREVERSION_BUILD;

#else

const uint8_t FwMajor = FIRMWAREVERSION_MAJOR;
const uint8_t FwMinor = FIRMWAREVERSION_MINOR;
const uint16_t FwRevision = FIRMWAREVERSION_REVISION;
const uint16_t FwBuild = FIRMWAREVERSION_BUILD;

#endif

#define POWERUP_TIMEOUT             60      // seconds
#define POWERUP_VCC_THRESH          2800
#define LED_ALLOWED_TIME            3600   // turn off all LEDs after this number of seconds after power-on

extern bool_t DAFE_found;
extern bool_t FLOW_found;
extern bool_t PIXL_found;
extern bool_t AQUA_found;

/*!*************************************************************************************************
 * @brief  Wait until VCC reaches operational threshold
 * @param none
 **************************************************************************************************/
void _waitForPowerup(void)
{
    uint16_t     i;
    uint16_t     batv;

    for( i=0; i<POWERUP_TIMEOUT*4; i++ ) {
        batv = ana_readVdd();
        debug_printf("%8ld: PowerUp Attempt Number: %d: %dmV\n", (unsigned long) tick_GetUptime(), i, batv);
        if( batv >= POWERUP_VCC_THRESH ) {
            break;
        }
        milli_wait(250);
    }
}

bool_t get_cav_rev(void)
{
    uint16_t sample, mV;

    // for future board revs, we can have different voltage levels to indicate board revision
    // 3.3 can be below 0.25V
    // 3.2 can be above 2.5V
    // 3.4 can be between 0.25V and 0.5V
    // etc.

    ana_powerOn();
    milli_wait(100);
    adc12_readSingle( ADC_CHANNEL_ANA_PWR+ADC12SREF_1, &sample );
    mV = ana_conv_to_mV( sample, 1 );
    ana_powerOff();
    // empty any pending bytes (Rx transceiver off, so may not need)
    HalUARTRxFlush( PIXL_SERIAL_PORT );

    debug_printf("%8ld: Analog Sensor Power Level (mV) (will be above 1000 mV for v3.2): %d\n",
                 (unsigned long) tick_GetUptime(), mV);
    return (mV <= 1000) ? TRUE : FALSE;
}

void blink_led(uint8_t count, uint8_t time_on, uint8_t time_off)
{
    uint8_t i;

    for (i = count; i > 0; i--) {
        if (tick_GetUptime() < LED_ALLOWED_TIME) {
            HAL_TURN_ON_DBGLED1();
            milli_wait(time_on);
            HAL_TURN_OFF_DBGLED1();
            if (i > 1)
                milli_wait(time_off);
        } else {
            HAL_TURN_OFF_DBGLED1();
        }
    }
}

void blink_sensors(void)
{
    if (tick_GetUptime() < LED_ALLOWED_TIME) {  // turn off LED after an hour (3600 seconds) of operation
        if ((DAFE_found == 1) || (FLOW_found == 1)) {
            blink_led(2, 70, 150);         // DAFE or FLOW sensor : blink 2 times : 70 ms on, 150 ms off
        }
        if (PIXL_found || AQUA_found) {
            if (DAFE_found)
                milli_wait(600);            // some delay in the case there is a DAFE and a SOIL sensor
            blink_led(3, 70, 150);          // Soil Sensor : blink 3 times: 70 ms on, 150 ms off
        }
        if ( ! (PIXL_found || AQUA_found || FLOW_found || DAFE_found ) ) {
            blink_led(4, 70, 150);         // Repeater : blink 4 times : 70 ms on, 150 ms off
        }
    }
}

void blink_rev(void)
{
    uint8_t i;

    debug_printf("%8ld:\tBlinking minor code version (%d), three times separated by 600 ms\n",
                 (unsigned long) tick_GetUptime(), FIRMWAREVERSION_MINOR-7);
    for (i = 0; i < 3; i++) {
        blink_led(FIRMWAREVERSION_MINOR-7, 150, 50);  // blink LED number of release, with 150 ms on, 50 ms off
        milli_wait(600);  // do it three times, with 600 ms pause between blinking
    }
}

#if 0
/**********************************************************************************************//**
 * @fn      SysUtils_Reset
 *
 * @brief   System Reset
 *
 * @param   None
 *
 * @return  None
 ***************************************************************************************************/
void utils_Reset (void)
{
	// TODO: Update app reboot counter

	SystemReset();
}
#endif

#if 0
/**********************************************************************************************//**
 * @fn      SysUtils_GetFwInfo
 *
 * @brief   Get the Firmware information
 *
 * @param   maj - Firmware Major number
 * @param   min - Firmware Minor number
 * @param   maj - Firmware Revision number
 * @param   build - Firmware Build number
 *
 * @return  None
 ***************************************************************************************************/
void utils_GetFwInfo (uint8_t *maj, uint8_t *min, uint16_t *rev, uint16_t *build)
{
    if ( !maj || !min || !rev || !build ) {
        return;
    }

    *maj = FwMajor;
    *min = FwMinor;
    *rev = FwRevision;
    *build = FwBuild;

    return;
}
#endif

/**********************************************************************************************//***
* @brief   Delay milli second wait
*
* @param   timeout - time to wait (in ms)
*
* @return  none
 ***************************************************************************************************/
void milli_wait( uint16_t timeout )
{
    while( timeout-- ) {
    	DELAY_USEC(1000);
    }
}

#if 0
/**********************************************************************************************//***
* @brief   Convert single digit ASCII to hex binary 
*
* @param   digit - ASCII digit: '0'..'9', 'A'..'F'
*
* @return  hex value: 0x0..0xF
 ***************************************************************************************************/
uint8_t char2hex( uint8_t digit )
{
  if( isxdigit(digit) )
  {
    return( digit - (digit < 'A' ? '0' : ('A' -10)) );
  }
  return(255);
}

/**********************************************************************************************//***
* @brief   Convert a 4 char ASCII string hex number to uint16_t
*
* @param   *str - pointer to input string (max 4 chars or NULL terminated)
*
* @return  uint16_t - 2 byte binary hex.  returns -1 if error
 ***************************************************************************************************/
uint16_t hex2int( uint8_t *str )
{
  uint16_t value = 0;
  int8_t digit;
  uint8_t len = 0;
  
  if( !isxdigit(*str) )   //Must start with a digit
  {
    return(65535);
  }
  
  while( (*str != NULL) && (len < 4) )
  {
    if( value > 0x0FFF )  //Overflow?    
    {
      return(65535);
    }
    
    if( (digit = char2hex(*str++)) == -1 )
    {
      return(65535);
    }
    
    value = (value << 4) + digit;
    len++;
  }    
  
  return( value );    
} 

/**********************************************************************************************//***
* @brief   Reverse string (null terminated)
*
* @param   *str - pointer to input string (max 4 chars or NULL terminated)
*
* @return  uint16_t - 2 byte binary hex.  returns -1 if error
 ***************************************************************************************************/
char_t *strrev( char_t *str )
{
    char_t *p1, *p2;

    if (!str || !*str)
    {
        return str;
    }

    for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
    {
        *p1 ^= *p2;
        *p2 ^= *p1;
        *p1 ^= *p2;
    }

    return str;
}

/**********************************************************************************************//***
* @brief   Convert uint16_t to string
*
* @param   *str - pointer to input string (max 4 chars or NULL terminated)
*
* @return  pointer to str
 ***************************************************************************************************/
char_t * itoa( uint16_t value, char_t *str, uint8_t base )
{
    static char_t digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
    uint8_t i = 0;
    //uint8_t sign;

    //if( (sign = value) < 0 )
    //{
    //    value = -value;
    //}

    do
    {
        str[i++] = digits[value % base];
    } while( (value /= base) > 0 );

    //if (sign < 0)
    //{
    //    s[i++] = '-';
    //}
    str[i] = '\0';

    return( strrev(str) );
}
#endif

#if 0
/***************************************************************************************************
 * @brief   32 bit integer square root
 *
 * @param   x - 32b operand
 *
 * @return  32b square-root( x )
 ***************************************************************************************************/
uint32_t isqrt( uint32_t a )
{
    uint32_t    rem = 0;
    uint32_t    root = 0;
    int         i;

    for(i=0; i<16; i++)
    {
        root <<= 1;
        rem = ((rem << 2) + (a >> 30));
        a <<= 2;
        root++;
        if(root <= rem)
        {
            rem -= root;
            root++;
        }
        else
        {
            root--;
        }
    }
    return( root >> 1 );
}
#endif
