/*
    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"

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

/*Velikost GPS bufferu*/
#define GPS_BUFFER                                                      500

extern NMEA_GPGGA GPGGA_informace;
Thread *tp_odpal = NULL;

/*
 * 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;
                palTogglePad(GPIOB, GPIOB_LED4);
         /* 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 msg_zrusen[] = "Odpal zrusen uzivatelem.\r\n";
        uint8_t odpal_pomocna = 0;
        (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++;
                }
                else if (msg == 2) //Pokud se ma odpal zrusit v prubehu
                {
                        odpal_povolen = 0;
                        odpal_pomocna = 0;
                        stav = 0;
                        sdWrite(&SD1, msg_zrusen, sizeof(msg_zrusen)/sizeof(uint8_t));
                }
                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:
                                        sdWrite(&SD1,"0\r\n",3);
                                        gptStartOneShot(&GPTD2,1000);
                                break;                  
                                case 1:
                                        sdWrite(&SD1,"1\r\n",3);
                                        gptStartOneShot(&GPTD2,1500);
                                break;
                                case 2: 
                                        sdWrite(&SD1,"2\r\n",3);
                                        gptStartOneShot(&GPTD2,2000);
                                break;
                                case 3: 
                                        sdWrite(&SD1,"3\r\n",3);
                                        gptStartOneShot(&GPTD2,3000);
                                break;
                                case 4: 
                                        sdWrite(&SD1,"4\r\n",3);
                                        gptStartOneShot(&GPTD2,5000);
                                break;
                                default: 
                                        sdWrite(&SD1,"konec\r\n",7); //posledni krok
                                odpal_povolen = 0;
                                odpal_pomocna = 0;
                                stav = 0;
                                break;
                        }
                        stav++;
                }       
        }
}


/*
 * 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();           
                palTogglePad(GPIOB, GPIOB_LED4);
        }
        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 
        {
                uint8_t zp_neplatny[] = "Neplatny prikaz, spravny format *<prikaz>\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_LED4, PAL_MODE_OUTPUT_PUSHPULL);
        palSetPadMode(GPIOB, GPIOB_LED3, PAL_MODE_OUTPUT_PUSHPULL);
        
        /*
         * Porty pro vypousteci sekvenci
         */
        palSetPadMode(GPIOB, 10, PAL_MODE_OUTPUT_PUSHPULL);
        palSetPadMode(GPIOB, 11, PAL_MODE_OUTPUT_PUSHPULL);     
        palSetPadMode(GPIOB, 12, PAL_MODE_OUTPUT_PUSHPULL);
        palSetPadMode(GPIOB, 13, PAL_MODE_OUTPUT_PUSHPULL);
 /*
        * Aktivuje timer2 a 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);
        
  /*
   * 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];
                        }
  }
}