#include <iostream>
#include "GPSConnection.h"

using namespace std;

// ******* global functions *******
static struct gps_data_t *gpsDataGlobal;
static pthread_mutex_t gpsMutex = PTHREAD_MUTEX_INITIALIZER;
static GPSConnection *gpsConGlobal = NULL;

void GPSConnection::gpsCallback (struct gps_data_t *sentence, char *buf,  size_t len, int level)
{
  struct gps_data_t gpsDataLocal;

  pthread_mutex_lock (&gpsMutex);
  memcpy (&gpsDataLocal, sentence, sizeof (struct gps_data_t));
  pthread_mutex_unlock (&gpsMutex);

  update ();
}

// ******* member functions *******

GPSConnection::GPSConnection () :
    signal (false),
    connection (false)
{
  gpsConGlobal = this;
}

GPSConnection::~GPSConnection ()
{
  this->close ();
}

void GPSConnection::update ()
{
  struct gps_data_t gpsDataLocal;

  if (gpsDataGlobal)
  {
    pthread_mutex_lock (&gpsMutex);
    memcpy (&gpsDataLocal, gpsDataGlobal, sizeof (struct gps_data_t));
    pthread_mutex_unlock (&gpsMutex);

    if (gpsConGlobal)
    {
      gpsConGlobal->signalData.emit (&gpsDataLocal);
    }
  }
}

bool GPSConnection::open ()
{
  return this->open ("127.0.0.1", "2947");
}

bool GPSConnection::open (const string &host, const string &port)
{
  if (!connection)
  {
    gpsDataGlobal = gps_open (host.c_str(), port.c_str());

    if (gpsDataGlobal)
    {
      connection = true;
      return true;
    }
    else
    {
      return false;
    }
  }
  else
    return false;
}

bool GPSConnection::query (const string &request)
{
  struct gps_data_t gpsDataLocal;

  pthread_mutex_lock (&gpsMutex);
  memcpy (&gpsDataLocal, gpsDataGlobal, sizeof (struct gps_data_t));
  pthread_mutex_unlock (&gpsMutex);

  int retVal = gps_query (&gpsDataLocal, request.c_str());
  if (retVal != -1)
    return true;
  else
    return false;
}

void GPSConnection::close ()
{
  gps_close (gpsDataGlobal);
  connection = false;
}

bool GPSConnection::poll (struct gps_data_t *outGPSData)
{
  if (connection)
  {
    if (signal)
    {
      pthread_mutex_lock (&gpsMutex);
      memcpy (outGPSData, gpsDataGlobal, sizeof (struct gps_data_t));
      pthread_mutex_unlock (&gpsMutex);

      return true;
    }
    else
    {
      // we don't need a mutex here, because we're only here if callback is stopped
      int retVal = gps_poll (gpsDataGlobal);
      if (retVal != -1)
        return true;
      else
        return false;
    }
  }
  else
    return false;
}

void GPSConnection::setSignaling (bool inSignal)
{
  signal = inSignal;

  if (inSignal)
  {
    gps_set_callback (gpsDataGlobal, &gpsCallback, &gpsThread);
  }
  else
  {
    gps_del_callback (gpsDataGlobal, &gpsThread);
  }
}
