/*
    ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.
*/

#include "ch.h"
#include "hal.h"
#include "test.h"
#include "serial.h"
#include "gpt.h"
#include <string.h>
#include "keil/GPS_dekoduj.h"
#include <../../os/various/chprintf.h>
#include <chstreams.h.>

/*MAX delka prikazu, ktery uzivatel muze zadat*/
#define MAX_DELKA_PRIKAZU                               10

/*Velikost GPS bufferu*/
#define GPS_BUFFER                                                      500
#define PWM_PERIODA_NORMAL                      20000
#define PWM_SIRKA_NORMAL                                PWM_PERIODA_NORMAL/2

extern NMEA_GPGGA GPGGA_informace;
Thread *tp_odpal = NULL;
uint8_t uvitaci_zprava[] = "\r\n\r\n* * * * * * * * * * * * * * * * * * * * * * * * * *\r\nVita vas Automaticky Vypoustec Meteobalonu 1.1\r\nZapojeni vyvodu:\r\n\tGPIOB10 - ventil\r\n\tGPIOB11 - lis\r\n\tGPIOB12 - zataveni balonu\r\n\tGPIOB13 - otevreni krytu\r\nPrikazy:\r\n\t<odpal> zahajeni sekvence vypousteni\n\r\t<zrus> zruseni vypousteni\n\r\t<help> napoveda\r\n* * * * * * * * * * * * * * * * * * * * * * * * * *\r\n\r\n";

static PWMConfig pwmcfg = {
  10000,                                    /* 10kHz PWM clock frequency.   */
  PWM_PERIODA_NORMAL,                                    /* PWM period 1S (in ticks).    */
  NULL,
  {
    {PWM_OUTPUT_ACTIVE_HIGH, NULL},
    {PWM_OUTPUT_ACTIVE_HIGH, NULL},
    {PWM_OUTPUT_DISABLED, NULL},
    {PWM_OUTPUT_DISABLED, NULL}
  },
  /* HW dependent part.*/
  0
};

/*
 * Konfigurace USART2
 */

static const SerialConfig USART2_config =
{
        /*Speed*/
  9600,
        /*Initialization value for the CR1 register.*/
  0,
        /*Initialization value for the CR2 register.*/
  USART_CR2_STOP1_BITS | USART_CR2_LINEN,
        /*Initialization value for the CR3 register.*/
  0
};


 /*
        * GPT2 callback.
        */
static void gpt2cb(GPTDriver *gptp)
{
  (void)gptp;
         /* Wakes up the thread.*/
  chSysLockFromIsr();
  if (tp_odpal != NULL) {
    tp_odpal->p_u.rdymsg = (msg_t)50;     /* Znaci, ze se vlakno probouzi kvuli preruseni od GPT*/
    chSchReadyI(tp_odpal);
    tp_odpal = NULL;
  }
  chSysUnlockFromIsr();
}       


 /*
  *Konfigurace casovace 2
  */
        static const GPTConfig gpt2cfg = 
        {
                 1000, /*1000Hz f*/
                 gpt2cb /*callback fce*/
        };


/*
* Vlakno pro ovladani odpalovaci sekvence
*/
        static WORKING_AREA(waThread_odpal, 128);
        static msg_t Thread_odpal(void *arg) {
        uint8_t stav = 0; // rika, ve ktere fazi je odpalovani
        uint8_t odpal_povolen = 0;
        uint8_t odpal_pomocna = 0;
        uint8_t pocet_opakovani = 1;
        uint16_t perioda_casovace = 500;
        (void)arg;
  chRegSetThreadName("Odpal_vlakno");
                
                
        while (TRUE)
        {
    msg_t msg;

    /* Waiting for the IRQ to happen.*/
    chSysLock();
    tp_odpal = chThdSelf();
    chSchGoSleepS(THD_STATE_SUSPENDED);
    msg = chThdSelf()->p_u.rdymsg;  /* Retrieving the message, optional.*/
    chSysUnlock();
    /* Perform processing here.*/
                if(msg == 1)
                {
                                odpal_povolen = 1;
                                odpal_pomocna++;
                                stav = 0;
                                pocet_opakovani = 1;
                }
                else if (msg == 2) //Pokud se ma odpal zrusit v prubehu vypousteni
                {
                        /*
                         * Pro jistotu se vypnou vsechny vystupy pri zruseni odpalu
                         */
                        palClearPad(GPIOB, GPIOB_PIN10);
                        palClearPad(GPIOB, GPIOB_PIN11);
                        palClearPad(GPIOB, GPIOB_PIN12);
                        palClearPad(GPIOB, GPIOB_PIN13);
                        
                        if(odpal_povolen == 1)
                                chprintf((BaseSequentialStream *)&SD1,"\r\nOdpal zrusen uzivatelem.\r\n");
                        else
                                chprintf((BaseSequentialStream *)&SD1,"Odpal nebyl aktivovan.\r\n");
                        
                        odpal_povolen = 0;
                        odpal_pomocna = 0;
                        stav = 0;
                        pocet_opakovani = 1;
                }
                else if (msg == 50 && odpal_povolen == 1) // preruseni od GPT
                {
                        odpal_pomocna = 1;
                }
                if (odpal_povolen == 1 && odpal_pomocna <= 1) 
                {
                        odpal_pomocna = 2; // aby nepretelkla tato promenna
                        switch (stav)
                        {
                                case 0:
                                        if (pocet_opakovani == 1)
                                                chprintf((BaseSequentialStream *)&SD1,"Vypousteni zahajeno!\r\n");
                                        
                                        if (pocet_opakovani <= 20)
                                        {       
                                                //sdWrite(&SD1,,sizeof("1/4)\tVentil otevren -> nafukovani balonu\r\n")/sizeof(char));
                                                chprintf((BaseSequentialStream *)&SD1,"(1/4)\tVentil otevren -> nafukovani balonu %d%%\r",pocet_opakovani*5);
                                                palSetPad(GPIOB, GPIOB_PIN10);
                                                gptStartOneShot(&GPTD2,perioda_casovace);
                                                pocet_opakovani++;
                                                if (pocet_opakovani == 21)
                                                {
                                                        chprintf((BaseSequentialStream *)&SD1,"\r\n");
                                                        stav++;
                                                        pocet_opakovani = 1;
                                                }
                                        }
                                break;                  
                                case 1:
                                        if (pocet_opakovani <= 10)
                                        {       
                                                palClearPad(GPIOB, GPIOB_PIN10);
                                                palSetPad(GPIOB, GPIOB_PIN11);
                                                chprintf((BaseSequentialStream *)&SD1,"(2/4)\tPrepalovani lisu... %d%%\r",pocet_opakovani*10);
                                                gptStartOneShot(&GPTD2,perioda_casovace);
                                                pocet_opakovani++;
                                                if (pocet_opakovani == 11)
                                                {
                                                        chprintf((BaseSequentialStream *)&SD1,"\r\n");
                                                        stav++;
                                                        pocet_opakovani = 1;
                                                }
                                        }
                                break;
                                case 2: 
                                        if (pocet_opakovani <= 10)
                                        {                                               
                                                palClearPad(GPIOB, GPIOB_PIN11);
                                                palSetPad(GPIOB, GPIOB_PIN12);
                                                chprintf((BaseSequentialStream *)&SD1,"(3/4)\tZatavovani balonu... %d%%\r",pocet_opakovani*10);
                                                gptStartOneShot(&GPTD2,perioda_casovace);
                                                pocet_opakovani++;
                                                if (pocet_opakovani == 11)
                                                {
                                                        chprintf((BaseSequentialStream *)&SD1,"\r\n");
                                                        stav++;
                                                        pocet_opakovani = 1;
                                                }
                                        }
                                break;
                                case 3:
                                        if (pocet_opakovani <= 20)
                                        {                                               
                                                palClearPad(GPIOB, GPIOB_PIN12);
                                                palSetPad(GPIOB, GPIOB_PIN13);
                                                chprintf((BaseSequentialStream *)&SD1,"(4/4)\tOtevirani vika... %d%%\r",pocet_opakovani*5);
                                                gptStartOneShot(&GPTD2,perioda_casovace);
                                                pocet_opakovani++;
                                                if (pocet_opakovani == 21)
                                                {
                                                        chprintf((BaseSequentialStream *)&SD1,"\r\n");
                                                        stav++;
                                                        pocet_opakovani = 1;
                                                }
                                        }
                                break;
                                case 4: 
                                        palClearPad(GPIOB, GPIOB_PIN13);
                                        chprintf((BaseSequentialStream *)&SD1,"Vypousteni ukonceno!\r\n");
                                        odpal_povolen = 0;
                                        odpal_pomocna = 0;
                                        stav = 0;
                                break;
                                default: 
                                break;
                        }
                }       
        }
}


/*
 * Vlakno pro obsluhu GPS prijimace
 */
static WORKING_AREA(waThread_GPS, 768);
static msg_t Thread_GPS(void *arg) {
        /*
        * Nacita se jen nekolik NMEA zprav, aby se neplytvalo pameti na ulozeni kompletniho
        * setu s tím rizikem, ze se nekdy nenacte aktualni informace o poloze.
        */
  uint8_t inBuffer[GPS_BUFFER];
        char *zacatek_retezce;
        char *konec_retezce;
        uint8_t pocet_znaku;
        uint8_t NMEA_zprava[100];

  (void)arg;
  chRegSetThreadName("GPS_NMEA");       

  while (TRUE) {
    chThdSleepMilliseconds(1000); //neni potreba data vycitat rychleji
                sdRead(&SD2,inBuffer,GPS_BUFFER);
                
        /*
         *Nejprve se vycte cast NMEA dat, pote se vyhleda retezec GPGGA zpravy, ta se vyparsuje a pomoci fce
         *dekoduj_zpravu_GPS, ktera vyparsuje data o poloze a jine, a ulozi je do struktury GPGGA_informace.
         */

                if ((zacatek_retezce = strstr((char *)inBuffer,"$GPGGA")) != NULL)
                {
                        if ((konec_retezce = strstr(zacatek_retezce,"*")) != NULL)
                        {       
                                pocet_znaku = (konec_retezce-zacatek_retezce)/sizeof(char);
                                if (pocet_znaku > 100)
                                {
                                        pocet_znaku = 100;
                                }
                                strncpy((char *)NMEA_zprava,zacatek_retezce,pocet_znaku);
                                dekoduj_zpravu_GPS(&NMEA_zprava[0],pocet_znaku);
                                sdWrite(&SD2,"Latitude: ",sizeof("Latitude: ")/sizeof(char));
                                sdWrite(&SD2,GPGGA_informace.Latitude,sizeof(GPGGA_informace.Latitude)/sizeof(uint8_t));
                                sdWrite(&SD2,"\r\n",2);
                                sdWrite(&SD2,"Longitude: ",sizeof("Longitude: ")/sizeof(char));
                                sdWrite(&SD2,GPGGA_informace.Longitude,sizeof(GPGGA_informace.Longitude)/sizeof(uint8_t));
                                sdWrite(&SD2,"\r\n",2);
                                sdWrite(&SD2,"Altitude: ",sizeof("Altitude: ")/sizeof(char));                   
                                sdWrite(&SD2,GPGGA_informace.Altitude,sizeof(GPGGA_informace.Altitude)/sizeof(uint8_t));
                                sdWrite(&SD2,"\r\n",2);                 
                                sdWrite(&SD2,"Status: ",sizeof("Status: ")/sizeof(char));                       
                                sdWrite(&SD2,&GPGGA_informace.Status_GPS,sizeof(GPGGA_informace.Status_GPS)/sizeof(uint8_t));
                                sdWrite(&SD2,"\r\n",2); 
                                sdWrite(&SD2,NMEA_zprava,pocet_znaku);
                                sdWrite(&SD2,"\r\n",2);
                        }
                        else
                        {                       
                                sdWrite(&SD2,"\r\n",2);
                                sdWrite(&SD2,"Nenalezen ukoncovaci znak NMEA zpravy *\r\n",sizeof("Nenalezen ukoncovaci znak NMEA zpravy *\r\n")/sizeof(char));
                                sdWrite(&SD2,inBuffer,GPS_BUFFER);
                        }
                }
                else
                        sdWrite(&SD2,"Nenalezen zacatek GPGGA zpravy\r\n",sizeof("Nenalezen zacatek GPGGA zpravy\r\n")/sizeof(char));
                
  }
}

void dekodujPrikaz(char *prikaz)
{
        if(strcmp(prikaz,"odpal") == 0)
        {
                /* Wakes up the thread.*/
                chSysLockFromIsr();
                if (tp_odpal != NULL) {
                        tp_odpal->p_u.rdymsg = (msg_t)1;     /* odpal povolen*/
                        chSchReadyI(tp_odpal);
                        tp_odpal = NULL;
                }
                chSysUnlockFromIsr();

        }
        else if (strcmp(prikaz,"zrus") == 0)
        {
                        /* Wakes up the thread.*/
                chSysLockFromIsr();
                if (tp_odpal != NULL) {
                        tp_odpal->p_u.rdymsg = (msg_t)2;     /* zakazano pokracovat v odpalovaci sekvenci*/
                        chSchReadyI(tp_odpal);
                        tp_odpal = NULL;
                }
                chSysUnlockFromIsr();
        }
        else if (strcmp(prikaz,"help") == 0)
        {
                sdWrite(&SD1, uvitaci_zprava, sizeof(uvitaci_zprava)/sizeof(uint8_t));
        }
        else 
        {
                uint8_t zp_neplatny[] = "Neplatny prikaz!\r\n\t<odpal> pro zahajeni sekvence\n\r\t<zrus> pro zruseni vypousteni\n\r";
                sdWrite(&SD1,zp_neplatny,sizeof(zp_neplatny)/sizeof(uint8_t));
                palTogglePad(GPIOB, GPIOB_LED3);
        }
}

/*
 * Application entry point.
 */
int main(void) {
        
        uint8_t znaky[20];
        char prikaz[MAX_DELKA_PRIKAZU + 1];
        uint8_t pocet_znaku = 0;

        /*
   * System initializations.
   * - HAL initialization, this also initializes the configured device drivers
   *   and performs the board-specific initializations.
   * - Kernel initialization, the main() function becomes a thread and the
   *   RTOS is active.
   */
  halInit();
  chSysInit();  

        /*
   * Activates the serial driver 1 using the driver default configuration.
   * PA9 and PA10 are routed to USART1.
         * Komunikace s uzivatelem
   */
  sdStart(&SD1, NULL);
  palSetPadMode(GPIOA, 9, PAL_MODE_ALTERNATE(7));       //TX
  palSetPadMode(GPIOA, 10, PAL_MODE_ALTERNATE(7)); //RX

  /*
   * Activates the serial driver 2 using the driver default configuration.
   * PA2 and PA3 are routed to USART2.
         *GPS
   */
  sdStart(&SD2, &USART2_config);
  palSetPadMode(GPIOA, 2, PAL_MODE_ALTERNATE(7));       //TX
  palSetPadMode(GPIOA, 3, PAL_MODE_ALTERNATE(7));       //RX
        
        /*
         * LED na vyvojove desce
         */
        palSetPadMode(GPIOB, GPIOB_LED3, PAL_MODE_OUTPUT_PUSHPULL);
        
  /*
   * Initializes the PWM driver 4, routes the TIM4 outputs to the board LEDs.
   */
  pwmStart(&PWMD4, &pwmcfg);
  palSetPadMode(GPIOB, GPIOB_LED4, PAL_MODE_ALTERNATE(2));
        pwmEnableChannel(&PWMD4,0,PWM_SIRKA_NORMAL);

        
 /*
        * Porty pro vypousteci sekvenci
        */
        palSetPadMode(GPIOB, GPIOB_PIN10, PAL_MODE_OUTPUT_PUSHPULL);
        palClearPad(GPIOB, GPIOB_PIN10);
        palSetPadMode(GPIOB, GPIOB_PIN11, PAL_MODE_OUTPUT_PUSHPULL);    
        palClearPad(GPIOB, GPIOB_PIN11);
        palSetPadMode(GPIOB, GPIOB_PIN12, PAL_MODE_OUTPUT_PUSHPULL);
        palClearPad(GPIOB, GPIOB_PIN12);
        palSetPadMode(GPIOB, GPIOB_PIN13, PAL_MODE_OUTPUT_PUSHPULL);
        palClearPad(GPIOB, GPIOB_PIN13);
 /*
        * Aktivuje timer2 a timer3 prejde tak do aktivniho stavu
        */
        gptStart(&GPTD2,&gpt2cfg);      
        /*
   * Vytvori vlakno pro prijem dat z GPS modulu
   */
  chThdCreateStatic(waThread_GPS, sizeof(waThread_GPS), NORMALPRIO, Thread_GPS, NULL);
        
 /*
        * Vytvori vlakno pro odpalovaci sekvenci
        */
        chThdCreateStatic (waThread_odpal, sizeof(waThread_odpal), NORMALPRIO, Thread_odpal, NULL);
        
        sdWrite(&SD1, uvitaci_zprava, sizeof(uvitaci_zprava)/sizeof(uint8_t));
  /*
   * Normal main() thread activity, in this demo it does nothing except
   * sleeping in a loop and check the button state, when the button is
   * pressed the test procedure is launched with output on the serial
   * driver 1.
   */

  while (TRUE) {

                        sdRead(&SD1,znaky,1);
                /*Kdyz uzivatel stiskne enter -> dekoduj a vykonej prikaz, nebo pokud je prikaz delsi, nez by mel byt,
                 *prestane ukladat a upozorni uzivatele
                 */
                        if (znaky[0] == '\r' || pocet_znaku >= MAX_DELKA_PRIKAZU)
                        {
                                pocet_znaku = 0;
                                dekodujPrikaz(prikaz);
                                prikaz[0] = 0;
                        }
                        /*Uklada prikaz*/
                        else
                        {
                                prikaz[pocet_znaku + 1] = 0;
                                prikaz[pocet_znaku++] = znaky[0];
                        }
  }
}