/**
 * ======================================================================
 * GT-720F device driver (Version 0.0.1)
 *
 * Reference documents:
 * Application Note AN0003
 * Binary Messages Of SkyTraq Venus 5 GPS Receiver
 * Ver 0.3 Feb. 1, 2007
 * SkyTraq Technology, Inc.
 * ======================================================================
 * Copyright (c) 2011 Shinichiro Nakamura (CuBeatSystems)
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files
 * (the "Software"), to deal in the Software without restriction,
 * including without limitation the rights to use, copy, modify, merge,
 * publish, distribute, sublicense, and/or sell copies of the Software,
 * and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ======================================================================
 */

#include "gt_720f.h"

#define STSEQ_BYTE0 (0xA0)  /**< Start of Sequence Byte 0 */
#define STSEQ_BYTE1 (0xA1)  /**< Start of Sequence Byte 1 */

#define ENSEQ_BYTE0 (0x0D)  /**< End of Sequence Byte 0 */
#define ENSEQ_BYTE1 (0x0A)  /**< End of Sequence Byte 1 */

/**
 * @brief Packet Attribute.
 */
typedef enum {
    AttrInput,  /**< Attribute is Input. */
    AttrOutput, /**< Attribute is Output. */
} Attr;

/**
 * @brief System Message.
 */
typedef struct {
    unsigned char id;
    Attr attribute;
    char *name;
    char *descriptions;
} system_message_t;

/**
 * @brief System Message List.
 */
static const system_message_t sysmsg[] = {
    /*
     * Input System Messages
     */
    { 0x01, AttrInput, "System Restart", "Force System to restart" },
    { 0x02, AttrInput, "Query Software version", "Query revision information of software" },
    { 0x03, AttrInput, "Query Software CRC", "Query the CRC of the software" },
    { 0x04, AttrInput, "Set Factory Defaults", "Set system to factory default values" },
    { 0x05, AttrInput, "Configure Serial Port", "Set up serial port COM, baud rate, data bits, stop bits and parity" },
    { 0x06, AttrInput, "Reserved", "Reserved" },
    { 0x07, AttrInput, "Reserved", "Reserved" },
    { 0x08, AttrInput, "Configure NMEA", "Configure NMEA output message" },
    { 0x09, AttrInput, "Configure Output Message Format", "Configure the output message format from GPS receiver" },
    /*
     * Input GPS Messages
     */
    { 0x30, AttrInput, "Get ephemeris", "Retrieve ephemeris data of the GPS receiver" },
    { 0x31, AttrInput, "Set ephemeris", "Set ephemeris data to the GPS receiver" },
    /*
     * Output System Messages
     */
    { 0x80, AttrOutput, "Software version", "Software revision of the receiver" },
    { 0x81, AttrOutput, "Software CRC", "Software CRC of the receiver" },
    { 0x82, AttrOutput, "Reserved", "Reserved" },
    { 0x83, AttrOutput, "ACK", "ACK to a successful input message" },
    { 0x84, AttrOutput, "NACK", "Response to an unsuccessful input message" },
    /*
     * Output GPS Messages
     */
    { 0xB1, AttrOutput, "GPS Ephemeris Data", "Ephemeris data of the GPS receiver" },
};

/**
 * @brief データを送信する。
 * @details
 * シーケンス開始マーカーと終了マーカー、
 * ペイロード長とチェックサムは自動的に付加される。
 *
 * @param p ハンドラ。
 * @param buf 転送対象バッファ。
 * @param siz 転送対象バイト数。
 *
 * @return 送信したペイロードバイト数。
 */
static int transmit(
        gt_720f_t *p,
        unsigned char *buf,
        unsigned int siz)
{
    /*
     * 2   : Start of Sequence
     * 2   : Payload Length
     * siz : Message ID + Message Body
     * 1   : Checksum
     * 2   : End of Sequence
     */
    unsigned char sbuf[2 + 2 + siz + 1 + 2];
    unsigned char cs = 0;
    int i;

    sbuf[0] = STSEQ_BYTE0;
    sbuf[1] = STSEQ_BYTE1;
    sbuf[2] = (siz >> 8) & 0xFF;
    sbuf[3] = (siz >> 0) & 0xFF;
    for (i = 0; i < siz; i++) {
        sbuf[4 + i] = buf[i];
        cs ^= buf[i];
    }
    sbuf[4 + siz] = cs;
    sbuf[4 + siz + 1] = ENSEQ_BYTE0;
    sbuf[4 + siz + 2] = ENSEQ_BYTE1;

    if (p->func_write(sbuf, 2 + 2 + siz + 1 + 2) == 0) {
        return siz;
    } else {
        return 0;
    }
}

/**
 * @brief データを受信する。
 * @details
 * シーケンス開始マーカーと終了マーカー、
 * ペイロード長とチェックサムは自動的に除去される。
 *
 * @param p ハンドラ。
 * @param buf 転送対象バッファ。
 * @param siz 転送可能最大バイト数。
 *
 * @return 受信したペイロードバイト数。
 */
static int receive(
        gt_720f_t *p,
        unsigned char *buf,
        unsigned int siz)
{
    unsigned char st[2];    /**< Start of Sequence */
    unsigned char pl[2];    /**< Payload Length */
    unsigned char cs;       /**< Checksum */
    unsigned char en[2];    /**< End of Sequence */
    unsigned int payload_size;

    /*
     * Start of Sequence
     */
    p->func_read(st, sizeof(st));

    /*
     * Payload Length
     */
    p->func_read(pl, sizeof(pl));

    /*
     * Check the payload length
     */
    payload_size = (unsigned int)(pl[0] << 8) | (unsigned int)(pl[1] << 0);
    if (siz < payload_size) {
        return 0;
    }

    /*
     * Payload
     */
    p->func_read(buf, siz);

    /*
     * Checksum
     */
    p->func_read(&cs, 1);

    /*
     * End of Sequence
     */
    p->func_read(en, sizeof(en));

    return payload_size;
}

/**
 * @brief SYSTEM RESTART - Force system to restart (0x1)
 *
 * @details
 * This is a request message which will reset and restart
 * the GPS receiver. This command is issued from the host to GPS
 * receiver and GPS receiver should respond with an ACK or NACK.
 * The payload length is 15 bytes.
 */
int gt_720f_system_restart(
        gt_720f_t *p,
        Gt720fStartMode start_mode,
        unsigned short year,
        unsigned char month,
        unsigned char day,
        unsigned char hour,
        unsigned char minute,
        unsigned char second,
        short latitude,
        short longitude,
        short altitude)
{
}

/**
 * @brief NMEAメッセージ間隔を設定する。
 * @details
 *
 * @param p ハンドラ。
 * @param GGA GGAメッセージの通知間隔。単位は秒。
 * @param GSA GSAメッセージの通知間隔。単位は秒。
 * @param GSV GSVメッセージの通知間隔。単位は秒。
 * @param GLL GLLメッセージの通知間隔。単位は秒。
 * @param RMC RMCメッセージの通知間隔。単位は秒。
 * @param VTG VTGメッセージの通知間隔。単位は秒。
 * @param ZDA ZDAメッセージの通知間隔。単位は秒。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
int gt_720f_configure_nmea_message_interval(
        gt_720f_t *p,
        unsigned char gga,
        unsigned char gsa,
        unsigned char gsv,
        unsigned char gll,
        unsigned char rmc,
        unsigned char vtg,
        unsigned char zda)
{
    unsigned char sbuf[9];
    unsigned char rbuf[32];

    sbuf[0] = 0x08;      /* Message ID */
    sbuf[1] = gga;       /* GGA Interval */
    sbuf[2] = gsa;       /* GSA Interval */
    sbuf[3] = gsv;       /* GSV Interval */
    sbuf[4] = gll;       /* GLL Interval */
    sbuf[5] = rmc;       /* RMC Interval */
    sbuf[6] = vtg;       /* VTG Interval */
    sbuf[7] = zda;       /* ZDA Interval */
    sbuf[8] = 0;         /* 0:update to SRAM, 1:update to both SRAM & FLASH */

    transmit(p, sbuf, sizeof(sbuf));

    receive(p, rbuf, sizeof(rbuf));
    // @todo ACKでなければ後続パケットはない。
    receive(p, rbuf, sizeof(rbuf));

    return 0;
}

/**
 * @brief 出力メッセージタイプを設定する。
 *
 * @param p ハンドラ。
 * @param type 出力メッセージタイプ。
 *
 * @retval 0 成功。
 * @retval !0 失敗。
 */
int gt_720f_configure_message_type(
        gt_720f_t *p,
        MessageType type)
{
    unsigned char sbuf[3];
    unsigned char rbuf[32];

    sbuf[0] = 0x09;                 /* Message ID */
    sbuf[1] = (unsigned char)type;  /* 0:No output, 1:NMEA, 2:Binary */
    sbuf[2] = 0;                    /* 0:update to SRAM, 1:update to both SRAM & FLASH */

    transmit(p, sbuf, sizeof(sbuf));

    receive(p, rbuf, sizeof(rbuf));
    // @todo ACKでなければ後続パケットはない。
    receive(p, rbuf, sizeof(rbuf));

    return 0;
}

