/******************************************************************************
 *
 * Project:  OpenCPN
 * Purpose:  NMEA Data 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.         *
 ***************************************************************************
 *
 *
 *
 *
 */

#ifndef __DATASTREAM_H__
#define __DATASTREAM_H__

#ifdef __MINGW32__
#undef IPV6STRICT  // mingw FTBS fix:  missing struct ip_mreq
#include <windows.h>
#endif

#include "wx/wxprec.h"

#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif  // precompiled header

#include <wx/datetime.h>

#ifdef __WXGTK__
// newer versions of glib define its own GSocket but we unfortunately use this
// name in our own (semi-)public header and so can't change it -- rename glib
// one instead
//#include <gtk/gtk.h>
#define GSocket GlibGSocket
#include "wx/socket.h"
#undef GSocket
#else
#include "wx/socket.h"
#endif

#ifndef __WXMSW__
#include <sys/socket.h>  // needed for (some) Mac builds
#include <netinet/in.h>
#endif

#ifdef __WXMSW__
// #include <windows.h>
// #include <dbt.h>
// #include <winioctl.h>
// #include <initguid.h>
#endif
#include <string>
#include "ConnectionParams.h"
#include "dsPortType.h"

//----------------------------------------------------------------------------
//   constants
//----------------------------------------------------------------------------
#ifndef PI
#define PI 3.1415926535897931160E0 /* pi */
#endif

#define TIMER_SOCKET 7006

//      Error codes, returned by GetLastError()
enum { DS_ERROR_PORTNOTFOUND };

//      End-of-sentence types
enum { DS_EOS_CRLF, DS_EOS_LF, DS_EOS_CR };

//      Serial port handshake type
enum { DS_HANDSHAKE_NONE, DS_HANDSHAKE_XON_XOFF };

#define DS_SOCKET_ID 5001
#define DS_SERVERSOCKET_ID 5002
#define DS_ACTIVESERVERSOCKET_ID 5003

#define MAX_RX_MESSSAGE_SIZE 4096
#define RX_BUFFER_SIZE 4096

// Class declarations

// Class declarations
class OCP_DataStreamInput_Thread;
class DataStream;
class GarminProtocolHandler;

extern const wxEventType wxEVT_OCPN_DATASTREAM;
extern const wxEventType wxEVT_OCPN_THREADMSG;

bool CheckSumCheck(const std::string &sentence);

//----------------------------------------------------------------------------
// DataStream
//
//      Physical port is specified by a string in the class ctor.
//      Examples strings:
//              Serial:/dev/ttyS0               (Standard serial port)
//              Serial:COM4
//              TCP:192.168.1.1:5200            (TCP source, address and port
//              specified) GPSD:192.168.2.3:5400           (GPSD Wire protocol,
//              address and port specified)
//
//----------------------------------------------------------------------------

class DataStream : public wxEvtHandler {
protected:
  DataStream(wxEvtHandler *input_consumer, const ConnectionType conn_type,
             const wxString &Port, const wxString &BaudRate,
             dsPortType io_select, int priority, bool bGarmin, int EOS_type,
             int handshake_type);
  DataStream(wxEvtHandler *input_consumer, const ConnectionParams *params);

public:
  virtual ~DataStream();

  virtual void Close();

  bool IsOk() const { return m_bok; }
  wxString GetPort() const { return m_portstring; }
  dsPortType GetIoSelect() const { return m_io_select; }
  int GetPriority() const { return m_priority; }

  virtual bool SendSentence(const wxString &sentence);

  int GetLastError() const { return m_last_error; }

  //    Secondary thread life toggle
  //    Used to inform launching object (this) to determine if the thread can
  //    be safely called or polled, e.g. wxThread->Destroy();
  void SetSecThreadActive(void) { m_bsec_thread_active = true; }
  void SetSecThreadInActive(void) { m_bsec_thread_active = false; }
  bool IsSecThreadActive() const { return m_bsec_thread_active; }

  void SetChecksumCheck(bool check) { m_bchecksumCheck = check; }

  void SetInputFilter(wxArrayString filter) { m_input_filter = filter; }
  void SetInputFilterType(ListType filter_type) {
    m_input_filter_type = filter_type;
  }
  void SetOutputFilter(wxArrayString filter) { m_output_filter = filter; }
  void SetOutputFilterType(ListType filter_type) {
    m_output_filter_type = filter_type;
  }
  bool SentencePassesFilter(const wxString &sentence,
                            FilterDirection direction);
  bool ChecksumOK(const std::string &sentence);
  bool GetGarminMode() const { return m_bGarmin_GRMN_mode; }

  wxString GetBaudRate() const { return m_BaudRate; }
  dsPortType GetPortType() const { return m_io_select; }
  wxArrayString GetInputSentenceList() const { return m_input_filter; }
  wxArrayString GetOutputSentenceList() const { return m_output_filter; }
  ListType GetInputSentenceListType() const { return m_input_filter_type; }
  ListType GetOutputSentenceListType() const { return m_output_filter_type; }
  bool GetChecksumCheck() const { return m_bchecksumCheck; }
  ConnectionType GetConnectionType() const { return m_connection_type; }
  const ConnectionParams *GetConnectionParams() const { return &m_params; }
  int m_Thread_run_flag;

protected:
  void SetOk(bool ok) { m_bok = ok; };

  void SetGarminProtocolHandler(GarminProtocolHandler *garminHandler) {
    m_GarminHandler = garminHandler;
  }

  void SetSecondaryThread(OCP_DataStreamInput_Thread *secondary_Thread) {
    m_pSecondary_Thread = secondary_Thread;
  }
  OCP_DataStreamInput_Thread *GetSecondaryThread() {
    return m_pSecondary_Thread;
  }
  void SetThreadRunFlag(int run) { m_Thread_run_flag = run; }

  wxEvtHandler *GetConsumer() { return m_consumer; }
  wxString GetPortString() { return m_portstring; }

  void SetConnectTime(wxDateTime time) { m_connect_time = time; }
  wxDateTime GetConnectTime() { return m_connect_time; }

private:
  virtual void Open();

  bool m_bok;
  wxEvtHandler *m_consumer;
  wxString m_portstring;
  wxString m_BaudRate;
  dsPortType m_io_select;
  int m_priority;
  int m_handshake;

  OCP_DataStreamInput_Thread *m_pSecondary_Thread;
  bool m_bsec_thread_active;
  int m_last_error;

  ConnectionType m_connection_type;

  bool m_bchecksumCheck;
  wxArrayString m_input_filter;
  ListType m_input_filter_type;
  wxArrayString m_output_filter;
  ListType m_output_filter_type;

  bool m_bGarmin_GRMN_mode;
  GarminProtocolHandler *m_GarminHandler;
  wxDateTime m_connect_time;
  ConnectionParams m_params;
};

class InternalGPSDataStream : public DataStream {
public:
  InternalGPSDataStream(wxEvtHandler *input_consumer,
                        const ConnectionParams *params)
      : DataStream(input_consumer, params) {
    Open();
  }

private:
  void Open();
};

class InternalBTDataStream : public DataStream {
public:
  InternalBTDataStream(wxEvtHandler *input_consumer,
                       const ConnectionParams *params)
      : DataStream(input_consumer, params) {
    Open();
  }

private:
  void Open();
  bool SendSentence(const wxString &sentence);
};

class NullDataStream : public DataStream {
public:
  NullDataStream(wxEvtHandler *input_consumer, const ConnectionParams *params)
      : DataStream(input_consumer, params) {}
};

// Factory methods, preparation to split into subclasses with clearer
// Responsibilities

DataStream *makeDataStream(wxEvtHandler *input_consumer,
                           const ConnectionParams *params);

#endif
