/***************************************************************************
 *
 * Project:  OpenCPN
 * Purpose:  PlugIn Manager Object
 * Author:   David Register
 *
 ***************************************************************************
 *   Copyright (C) 2010 by David S. Register                               *
 *                                                                         *
 *   This program 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 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
 **************************************************************************/
 //Originally by balp on 2018-07-28.

#include <cstddef>

#include "wx/jsonval.h"
#include "wx/jsonwriter.h"
#include "wx/jsonreader.h"

#include "geodesic.h"
#include "SignalKEventHandler.h"
#include "OCPN_SignalKEvent.h"
#include "chart1.h"
#include "pluginmanager.h"
#include "logger.h"

extern PlugInManager    *g_pi_manager;
wxString                g_ownshipMMSI_SK;
bool             bGPSValid_SK;

void SignalKEventHandler::OnEvtOCPN_SignalK(OCPN_SignalKEvent &event)
{
    wxJSONReader jsonReader;
    wxJSONValue root;

    LOG_DEBUG("%s\n", event.GetString().c_str());

    std::string msgTerminated = event.GetString();
    msgTerminated.append("\r\n");

    int errors = jsonReader.Parse(msgTerminated, &root);
    if (errors > 0) {
        wxLogMessage( wxString::Format(_T("SignalKDataStream ERROR: the JSON document is not well-formed:%d"),
                      errors));
        return;
    }

    if (root.HasMember(_T("version"))) {
        wxString msg = _T("Connected to Signal K server version: ");
        msg << (root[_T("version")].AsString());
        wxLogMessage(msg);
    }

    if(root.HasMember("self")) {
        if(root["self"].AsString().StartsWith(_T("vessels.")))
            m_self = (root["self"].AsString());                                 // for java server, and OpenPlotter node.js server 1.20
        else
            m_self = _T("vessels.") + (root["self"].AsString());                // for Node.js server
        g_ownshipMMSI_SK = m_self;    
    }
    
    if(root.HasMember("context")
       && root["context"].IsString()) {
        auto context = root["context"].AsString();
        if (context != m_self) {
#if 0
            wxLogMessage(_T("** Ignore context of other ships.."));
#endif
            return;
        }
    }

    if(root.HasMember("updates")
       && root["updates"].IsArray()) {
        wxJSONValue &updates = root["updates"];
        for (int i = 0; i < updates.Size(); ++i) {
            handleUpdate(updates[i]);
        }
    }
}

void SignalKEventHandler::handleUpdate(wxJSONValue &update) const {
    wxString sfixtime = "";

    if(update.HasMember("timestamp")) {
        sfixtime = update["timestamp"].AsString();
    }
    if(update.HasMember("values")
       && update["values"].IsArray())
    {
        for (int j = 0; j < update["values"].Size(); ++j) {
            wxJSONValue &item = update["values"][j];
            updateItem(item, sfixtime);
        }
    }
}

void SignalKEventHandler::updateItem(wxJSONValue &item, wxString &sfixtime) const {
    if(item.HasMember("path")
       && item.HasMember("value")) {
        const wxString &update_path = item["path"].AsString();
        wxJSONValue &value = item["value"];
        if(update_path == _T("navigation.position")) {
            updateNavigationPosition(value, sfixtime);
        } else if(update_path == _T("navigation.speedOverGround") && bGPSValid_SK)
        {
            updateNavigationSpeedOverGround(value, sfixtime);
        } else if(update_path == _T("navigation.courseOverGroundTrue") && bGPSValid_SK)
        {
            updateNavigationCourseOverGround(value, sfixtime);
        } else if(update_path == _T("navigation.courseOverGroundMagnetic"))
        {  // Ignore magnetic COG as OpenCPN don't handle yet.
        } else if(update_path == _T("navigation.gnss.satellites"))
        {
            updateGnssSatellites(value, sfixtime);
        } else if(update_path == _T("navigation.headingTrue"))
        {
            updateHeadingTrue(value, sfixtime);
        } else if(update_path == _T("navigation.headingMagnetic"))
        {
            updateHeadingMagnetic(value, sfixtime);
        } else if(update_path == _T("navigation.magneticVariation"))
        {
            updateMagneticVariance(value, sfixtime);
        } else {
            //wxLogMessage(wxString::Format(_T("** Signal K unhandled update: %s"), update_path));
#if 0
            wxString dbg;
            wxJSONWriter writer;
            writer.Write(item, dbg);
            wxString msg( _T("update: ") );
            msg.append(dbg);
            wxLogMessage(msg);
#endif
        }

    }
}

void SignalKEventHandler::updateNavigationPosition(wxJSONValue &value, const wxString &sfixtime) const {
    if ((value.HasMember("latitude" && value["latitude"].IsDouble()))
        && (value.HasMember("longitude") && value["longitude"].IsDouble())) {
        //wxLogMessage(_T(" ***** Position Update"));
        m_frame->setPosition(value["latitude"].AsDouble(),
                             value["longitude"].AsDouble());
        m_frame->PostProcessNMEA(true, false, sfixtime);
        bGPSValid_SK = true;
    }
    else {
        bGPSValid_SK = false;
    }
}

void SignalKEventHandler::updateNavigationSpeedOverGround(wxJSONValue &value,
                                                          const wxString &sfixtime) const {
    double sog_ms = value.AsDouble();
    double sog_knot = sog_ms * ms_to_knot_factor;
    //wxLogMessage(wxString::Format(_T(" ***** SOG: %f, %f"), sog_ms, sog_knot));
    m_frame->setSpeedOverGround(sog_knot);
    m_frame->PostProcessNMEA(false, true, sfixtime);
}

void SignalKEventHandler::updateNavigationCourseOverGround(wxJSONValue &value,
                                                           const wxString &sfixtime) const {
    double cog_rad = value.AsDouble();
    double cog_deg = GEODESIC_RAD2DEG(cog_rad);
    //wxLogMessage(wxString::Format(_T(" ***** COG: %f, %f"), cog_rad, cog_deg));
    m_frame->setCourseOverGround(cog_deg);
    m_frame->PostProcessNMEA(false, true, sfixtime);
}

void SignalKEventHandler::updateGnssSatellites(wxJSONValue &value,
                                               const wxString &sfixtime) const
{
    m_frame->setSatelitesInView(value.AsInt());
}

void SignalKEventHandler::updateHeadingTrue(wxJSONValue &value,
                                            const wxString &sfixtime) const
{
    m_frame->setHeadingTrue(GEODESIC_RAD2DEG(value.AsDouble()));
}

void SignalKEventHandler::updateHeadingMagnetic(wxJSONValue &value,
                                            const wxString &sfixtime) const
{
    m_frame->setHeadingMagnetic(GEODESIC_RAD2DEG(value.AsDouble()));
}

void SignalKEventHandler::updateMagneticVariance(wxJSONValue &value,
                                                 const wxString &sfixtime) const
{
    m_frame->setMagneticVariation(GEODESIC_RAD2DEG(value.AsDouble()));
}
