/* 
 * File:   M10Gtop.cpp
 * Author: Viproz
 * Used code from rs1729
 * Created on December 13, 2018, 4:39 PM
 */

/*
#define stdFLEN        0x64  // pos[0]=0x64
#define pos_GPSlat     0x04  // 4 byte
#define pos_GPSlon     0x08  // 4 byte
#define pos_GPSalt     0x0C  // 3 byte
#define pos_GPSvE      0x0F  // 2 byte
#define pos_GPSvN      0x11  // 2 byte
#define pos_GPSvU      0x13  // 2 byte
#define pos_GPStime    0x15  // 3 byte
#define pos_GPSdate    0x18  // 3 byte

#define pos_SN         0x5D  // 2+3 byte
#define pos_Check     (stdFLEN-1)  // 2 byte*/

#include "M10GtopParser.h"
#include <ctime>
#include <string>
#include <sstream>
#include <iostream>

M10GtopParser::M10GtopParser() {
}

M10GtopParser::~M10GtopParser() {
}

void M10GtopParser::changeData(std::array<unsigned char, DATA_LENGTH> data, bool good) {
    M10GeneralParser::changeData(data, good);

    int i;
    unsigned short bytes[4];
    int val;

    for (i = 0; i < 3; i++) bytes[i] = frame_bytes[0x15 + i];
    val = 0;
    for (i = 0; i < 3; i++) val |= bytes[i] << (8 * (2 - i));
    time = val;

    for (i = 0; i < 3; i++) bytes[i] = frame_bytes[0x18 + i];
    val = 0;
    for (i = 0; i < 3; i++) val |= bytes[i] << (8 * (2 - i));
    date = val;
}

double M10GtopParser::getLatitude() {
    int i;
    unsigned short bytes[4];
    int val;

    for (i = 0; i < 4; i++)
        bytes[i] = frame_bytes[0x04 + i];
    val = 0;
    for (i = 0; i < 4; i++)
        val |= bytes[i] << (8 * (3 - i));
    return val / 1e6;
}

double M10GtopParser::getLongitude() {
    int i;
    unsigned short bytes[4];
    int val;

    for (i = 0; i < 4; i++)
        bytes[i] = frame_bytes[0x08 + i];
    val = 0;
    for (i = 0; i < 4; i++)
        val |= bytes[i] << (8 * (3 - i));
    return val / 1e6;
}

double M10GtopParser::getAltitude() {
    int i;
    unsigned short bytes[4];
    int val;

    for (i = 0; i < 3; i++)
        bytes[i] = frame_bytes[0x0C + i];
    val = 0;
    for (i = 0; i < 3; i++)
        val |= bytes[i] << (8 * (2 - i));
    if (val & 0x800000) val -= 0x1000000; // alt: signed 24bit?
    return val / 1e2;
}

int M10GtopParser::getDay() {
    return date / 10000;
}

int M10GtopParser::getMonth() {
    return (date % 10000) / 100;
}

int M10GtopParser::getYear() {
    return date % 100 + 2000;
}

int M10GtopParser::getHours() {
    return time / 10000;
}

int M10GtopParser::getMinutes() {
    return (time % 10000) / 100;
}

int M10GtopParser::getSeconds() {
    return time % 100;
}

double M10GtopParser::getVerticalSpeed() {
    int i;
    unsigned short bytes[2];
    short vel16;

    for (i = 0; i < 2; i++)
        bytes[i] = frame_bytes[0x13 + i];
    vel16 = bytes[0] << 8 | bytes[1];
    return vel16 / 1e2; // up
}

double M10GtopParser::getHorizontalSpeed() {
    int i;
    unsigned short bytes[2];
    short vel16;
    double vx, vy;

    for (i = 0; i < 2; i++)
        bytes[i] = frame_bytes[0x0F + i];
    vel16 = bytes[0] << 8 | bytes[1];
    vx = vel16 / 1e2; // east

    for (i = 0; i < 2; i++)
        bytes[i] = frame_bytes[0x11 + i];
    vel16 = bytes[0] << 8 | bytes[1];
    vy = vel16 / 1e2; // north

    return sqrt(vx * vx + vy * vy);
}

double M10GtopParser::getDirection() {
    int i;
    unsigned short bytes[2];
    short vel16;
    double vx, vy, dir;

    for (i = 0; i < 2; i++)
        bytes[i] = frame_bytes[0x0F + i];
    vel16 = bytes[0] << 8 | bytes[1];
    vx = vel16 / 1e2; // east

    for (i = 0; i < 2; i++)
        bytes[i] = frame_bytes[0x11 + i];
    vel16 = bytes[0] << 8 | bytes[1];
    vy = vel16 / 1e2; // north

    return sqrt(vx * vx + vy * vy);
    dir = atan2(vx, vy) * 180 / M_PI;
    if (dir < 0) dir += 360;
    return dir;
}

double M10GtopParser::getTemperature() {
    return 0;
}

double M10GtopParser::getHumidity() {
    return 0;
}

double M10GtopParser::getDp() {
    return 0;
}

std::string M10GtopParser::getSerialNumber() {
    int i;
    unsigned byte;
    unsigned short sn_bytes[5];
    char SN[18];

    for (i = 0; i < 17; i++)
        SN[i] = ' ';
    SN[17] = '\0';

    for (i = 0; i < 5; i++) {
        byte = frame_bytes[0x5D + i];
        sn_bytes[i] = byte;
    }

    /*
     * The serial number is in the form M10-A-BCC-D-EEEE
     * - A is the frame type, T for Trimble, the original GPS used for this modulation
     * G for Gtop GPS
     * - B is the year of fabrication (8 = 2018)
     * - CC is the month of fabrication
     * - D is the product type, 2 is production type
     * - EEEE is the RS serial number
     */

    byte = sn_bytes[2];
    sprintf(SN, "M10-G-%1X%02u", (byte >> 4)&0xF, byte & 0xF);
    byte = sn_bytes[3] | (sn_bytes[4] << 8);
    sprintf(SN + 9, "-%1X-%1u%04u", sn_bytes[0]&0xF, (byte >> 13)&0x7, byte & 0x1FFF);

    return SN;
}

std::string M10GtopParser::getdxlSerialNumber() {
    int i;
    unsigned byte;
    unsigned short sn_bytes[5];

    for (i = 0; i < 5; i++) {
        byte = frame_bytes[0x5D + i];
        sn_bytes[i] = byte;
    }

    // The way used by dxlARPS used for compatibility.
    uint32_t id;
    char ids[9];

    id = (uint32_t) (((uint32_t) ((uint32_t) (uint8_t)
            sn_bytes[4] + 256UL * (uint32_t) (uint8_t)
            sn_bytes[3] + 65536UL * (uint32_t) (uint8_t)
            sn_bytes[2])^(uint32_t) ((uint32_t) (uint8_t)
            sn_bytes[0] / 16UL + 16UL * (uint32_t) (uint8_t)
            sn_bytes[1] + 4096UL * (uint32_t) (uint8_t)
            sn_bytes[2]))&0xFFFFFUL);
    i = 8UL;
    ids[8U] = 0;
    --i;
    do {
        ids[i] = (char) (id % 10UL + 48UL);
        id = id / 10UL;
        --i;
    } while (i != 1UL);
    ids[i] = 'E';
    --i;
    ids[i] = 'M';

    return ids;
}

void M10GtopParser::printFrame() {
    if (dispRaw) {
        for (int i = 0; i < frameLength + 1; ++i) {
            printf("%02X", frame_bytes[i]);
        }
        if (correctCRC)
            printf(" [OK]");
        else
            printf(" [NO]");
        printf("\n");
    } else {
        setenv("TZ", "", 1); // Set local timezone to UTC
        time_t frame = 0;
        struct tm timeinfo;

        timeinfo.tm_hour = getHours();
        timeinfo.tm_min = getMinutes();
        timeinfo.tm_sec = getSeconds();
        timeinfo.tm_mday = getDay();
        timeinfo.tm_mon = getMonth() - 1;
        timeinfo.tm_year = getYear() - 1900;
        timeinfo.tm_isdst = 0;

        frame = mktime(&timeinfo);

        printf("{ "
                "\"sub_type\": \"%s\", "
                "\"frame\": %ld, "
                "\"id\": \"%s\", "
                "\"dxlid\": \"%s\", "
                "\"datetime\": \"%04d-%02d-%02dT%02d:%02d:%02dZ\", "
                "\"lat\": %.5f, "
                "\"lon\": %.5f, "
                "\"alt\": %.2f, "
                "\"vel_h\": %.5f, "
                "\"heading\": %.5f, "
                "\"vel_v\": %.2f, "
                //"\"temp\": %.1f "
                "\"crc\": %d "
                "}\n",
                "Gtop", frame, getSerialNumber().c_str(), getdxlSerialNumber().c_str(), getYear(), getMonth(), getDay(), getHours(), getMinutes(), getSeconds(), getLatitude(), getLongitude(),
                getAltitude(), getHorizontalSpeed(), getDirection(), getVerticalSpeed()/*, getTemperature()*/, correctCRC);
    }
    fflush(stdout);
}
