/*
 * Copyright (C) 2003  Pascal Brisset, Antoine Drouin
 * Copyright (C) 2014  Gautier Hattenberger <gautier.hattenberger@enac.fr>
 *
 * This file is part of paparazzi.
 *
 * paparazzi is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * paparazzi is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with paparazzi; see the file COPYING.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 */

/**
 * @file subsystems/datalink/pprz_transport.h
 *
 * Building and parsing Paparazzi frames.
 *
 * Pprz frame:
 *
 * |STX|length|... payload=(length-4) bytes ...|Checksum A|Checksum B|
 *
 * where checksum is computed over length and payload:
 * @code
 * ck_A = ck_B = length
 * for each byte b in payload
 *     ck_A += b;
 *     ck_b += ck_A;
 * @endcode
 */

#ifndef PPRZ_TRANSPORT_H
#define PPRZ_TRANSPORT_H

#include <inttypes.h>
#include "std.h"
#ifndef PPRZ_DATALINK_EXPORT
#include "subsystems/datalink/datalink.h"
#include "subsystems/datalink/transport.h"
#else /* PPRZ_DATALINK_EXPORT defined */
#include "datalink.h"
#include "transport.h"
#endif

/* PPRZ Transport
 */

#define STX  0x99

// PPRZ parsing state machine
#define UNINIT      0
#define GOT_STX     1
#define GOT_LENGTH  2
#define GOT_PAYLOAD 3
#define GOT_CRC1    4

struct pprz_transport {
  // generic reception interface
  struct transport_rx trans_rx;
  // specific pprz transport_rx variables
  uint8_t status;
  uint8_t payload_idx;
  uint8_t ck_a_rx, ck_b_rx;
  // generic transmission interface
  struct transport_tx trans_tx;
  // specific pprz transport_tx variables
  uint8_t ck_a_tx, ck_b_tx;
};

extern struct pprz_transport pprz_tp;

// Init function
extern void pprz_transport_init(struct pprz_transport *t);

static inline void parse_pprz(struct pprz_transport *t, uint8_t c)
{
  switch (t->status) {
    case UNINIT:
      if (c == STX) {
        t->status++;
      }
      break;
    case GOT_STX:
      if (t->trans_rx.msg_received) {
        t->trans_rx.ovrn++;
        goto error;
      }
      t->trans_rx.payload_len = c - 4; /* Counting STX, LENGTH and CRC1 and CRC2 */
      t->ck_a_rx = t->ck_b_rx = c;
      t->status++;
      t->payload_idx = 0;
      break;
    case GOT_LENGTH:
      t->trans_rx.payload[t->payload_idx] = c;
      t->ck_a_rx += c; t->ck_b_rx += t->ck_a_rx;
      t->payload_idx++;
      if (t->payload_idx == t->trans_rx.payload_len) {
        t->status++;
      }
      break;
    case GOT_PAYLOAD:
      if (c != t->ck_a_rx) {
        goto error;
      }
      t->status++;
      break;
    case GOT_CRC1:
      if (c != t->ck_b_rx) {
        goto error;
      }
      t->trans_rx.msg_received = TRUE;
      goto restart;
    default:
      goto error;
  }
  return;
error:
  t->trans_rx.error++;
restart:
  t->status = UNINIT;
  return;
}

static inline void pprz_parse_payload(struct pprz_transport *t)
{
  uint8_t i;
  for (i = 0; i < t->trans_rx.payload_len; i++) {
    dl_buffer[i] = t->trans_rx.payload[i];
  }
  dl_msg_available = TRUE;
}


#define PprzBuffer(_dev) TransportLink(_dev,ChAvailable())

#define ReadPprzBuffer(_dev,_trans) { \
    while (TransportLink(_dev,ChAvailable()) && !(_trans.trans_rx.msg_received)) { \
      parse_pprz(&(_trans),TransportLink(_dev,Getch()));  \
    } \
  }

#define PprzCheckAndParse(_dev,_trans) {  \
    if (PprzBuffer(_dev)) {                 \
      ReadPprzBuffer(_dev,_trans);          \
      if (_trans.trans_rx.msg_received) {      \
        pprz_parse_payload(&(_trans));      \
        _trans.trans_rx.msg_received = FALSE;  \
      }                                     \
    }                                       \
  }


#endif /* PPRZ_TRANSPORT_H */

