/*
 Oximetry GUI Headers
 Copyright (c)2011 Mark Watkins <jedimark@users.sourceforge.net>
 License: GPL
*/

#ifndef OXIMETRY_H
#define OXIMETRY_H

#include <QWidget>
#include <QGLContext>
#include <QHBoxLayout>
#include <QSplitter>
#include <qextserialport/qextserialport.h>

#include "SleepLib/profiles.h"
#include "SleepLib/day.h"
#include "SleepLib/session.h"

#include "Graphs/gLineChart.h"
#include "Graphs/gFooBar.h"

//! \brief Oximeters current mode
enum SerialOxMode { SO_OFF, SO_IMPORT, SO_LIVE, SO_WAIT };

/*! \class SerialOximeter
    \author Mark Watkins <jedimark_at_users.sourceforge.net>
    \brief Base class for Serial Oximeters
    */
class SerialOximeter:public QObject
{
    Q_OBJECT
public:
    explicit SerialOximeter(QObject * parent,QString oxiname, QString portname="",BaudRateType baud=BAUD19200, FlowType flow=FLOW_OFF, ParityType parity=PAR_ODD, DataBitsType databits=DATA_8, StopBitsType stopbits=STOP_1);
    virtual ~SerialOximeter();

    virtual void setSession(Session * sess) { session=sess; }

    //! \brief Open the serial port in either EventDriven or Polling mode
    virtual bool Open(QextSerialPort::QueryMode mode=QextSerialPort::EventDriven);

    //! \brief Close the serial port
    virtual void Close();

    //! \brief Virtual method for Importing the Oximeters internal recording.
    virtual bool startImport()=0;
    //! \brief Virtual method to Abort importing the Oximeters internal recording.
    virtual void stopImport() {} // abort, default do nothing.

    //! \brief Start Serial "Live" Recording
    virtual bool startLive();
    //! \brief Stop Serial "Live" Recording
    virtual void stopLive();

    //! \brief Put the device in standard transmit mode
    virtual void resetDevice()=0;

    //! \brief Put the device in record request mode
    virtual void requestData()=0;

    //! \brief Return the current SerialOxMode, either SO_OFF, SO_IMPORT, SO_LIVE, SO_WAIT
    SerialOxMode mode() { return m_mode; }

    //! \brief Trash the session object
    void destroySession() { delete session; session=NULL; }

    //! \brief Returns true if the serial port is currently open
    bool isOpen() { return m_opened; }

    //! \brief Returns a count of callbacks, so a Timer can see the ports alive or dead.
    int callbacks() { return m_callbacks; }

    //! \brief Returns the time of the last callback in milliseconds since epoch
    qint64 lastTime() { return lasttime; }
    //! \brief Sets the time of the last callback in milliseconds since epoch
    void setLastTime(qint64 t) { lasttime=t; }

    //! \brief Return the current machine object
    Machine * getMachine() { return machine; }

    //! \brief Create a new Session object for the specified date
    Session *createSession(QDateTime date=QDateTime::currentDateTime());

    //! \brief Returns the current session
    Session * getSession() { return session; }

    //! \brief Removes the TimeCodes, converting the EventList to Waveform type
    void compactToWaveform(EventList *el);

    //! \brief Packs EventList to time delta format, also pruning zeros.
    void compactToEvent(EventList *el);

    //! \brief Packs SPO2 & Pulse to Events, and Plethy to Waveform EventList types.
    void compactAll();

    //! \brief Sets the serial port device name
    void setPortName(QString portname);

    //! \brief Sets the serial ports Baud Rate (eg. BAUD19200, BAUD115200)
    void setBaudRate(BaudRateType baud);

    //! \brief Sets the serial ports Flow control to one of FLOW_OFF, FLOW_HARDWARE, or FLOW_XONXOFF
    void setFlowControl(FlowType flow);

    //! \brief Sets the serial ports Parity to one of PAR_NONE, PAR_ODD, PAR_EVEN, PAR_MARK (WINDOWS ONLY), PAR_SPACE
    void setParity(ParityType parity);

    //! \brief Sets the serial ports Data Bits to either DATA_5, DATA_6, DATA_7, or DATA_8
    void setDataBits(DataBitsType databits);

    //! \brief Sets the serial ports Stop Bits to either STOP_1, STOP_1_5 (WINDOWS ONLY) or STOP_2
    void setStopBits(StopBitsType stopbits);

    //! \brief Returns the serial port device name
    QString portName() { return m_portname; }

    //! \brief Returns the serial ports baud rate
    BaudRateType baudRate() { return m_baud; }
    //! \brief Returns the serial ports flow control setting
    FlowType flowControl() { return m_flow; }

    //! \brief Returns the serial ports parity setting
    ParityType parity() { return m_parity; }

    //! \brief Returns the serial ports data bits setting
    DataBitsType dataBits() { return m_databits; }

    //! \brief Returns the serial ports stop bits setting
    StopBitsType stopBits() { return m_stopbits; }


    EventList * Pulse() { return pulse; }
    EventList * Spo2() { return spo2; }
    EventList * Plethy() { return plethy; }
    virtual void addPulse(qint64 time, EventDataType pr);
    virtual void addSpO2(qint64 time, EventDataType o2);
    virtual void addPlethy(qint64 time, EventDataType pleth);

signals:
    void sessionCreated(Session *);
    void dataChanged();

    //! \brief This signal is called after import completion, to parse the event data.
    void importProcess();

    //! \brief importProcess emits this signal after completion.
    void importComplete(Session *);

    //! \brief emitted when something goes wrong during import
    void importAborted();

    //! \brief emitted to allow for UI updates to the progress bar
    void updateProgress(float f); // between 0 and 1.

    //! \brief emitted when live mode stops recording, passing the current Session
    void liveStopped(Session *);


    void updatePulse(float p);
    void updateSpO2(float p);

protected slots:
    //! \brief Override this to process the serial import as it's received
    virtual void ReadyRead()=0;

    //! \brief Override this to parse the read import data
    virtual void import_process()=0;

    //! \brief This slot gets called when the serial port Times out
    virtual void Timeout();

    //! \brief Override this to start the Import Timeout
    virtual void startImportTimeout()=0;

protected:
    //virtual void addEvents(EventDataType pr, EventDataType o2, EventDataType pleth=-1000000);

    //! \brief Pointer to current session object
    Session * session;

    EventList * pulse;
    EventList * spo2;
    EventList * plethy;

    //! \brief Holds the serial port object
    QextSerialPort *m_port;

    SerialOxMode m_mode;
    bool m_opened;
    QString m_oxiname;
    QString m_portname;
    BaudRateType m_baud;
    FlowType m_flow;
    ParityType m_parity;
    DataBitsType m_databits;
    StopBitsType m_stopbits;
    QextSerialPort::QueryMode m_portmode;
    Machine *machine;

    qint64 lasttime;
    bool import_mode;

    int m_callbacks;
    bool done_import;
    QTimer *timer;
    EventDataType lasto2,lastpr;

};

/*! \class CMS50Serial
    \author Mark Watkins <jedimark_at_users.sourceforge.net>
    \brief Serial Import & Live module
    */
class CMS50Serial:public SerialOximeter
{
public:
    explicit CMS50Serial(QObject * parent,QString portname);
    virtual ~CMS50Serial();

    //! \brief Start the serial parts of Import mode.
    virtual bool startImport();

    //! \brief Sends the 0xf6,0xf6,0xf6 data string to the serial port to start live mode again
    virtual void resetDevice();

    //! \brief Sends the 0xf5, 0xf5 data string to request devices serial recording
    virtual void requestData();

protected:
    //! \brief CMS50 Time-out detection
    virtual void startImportTimeout();

    //! \brief Called on completion of data import, to convert bytearray into event data
    virtual void import_process();

    //! \brief Serial callback to process live view & store import data
    virtual void ReadyRead();
    bool waitf6;
    short cntf6;
    short failcnt;

    QByteArray data;
    QVector<QDateTime> f2time;
    int datasize;

    int received_bytes;
    int import_fails;
};

namespace Ui {
    class Oximetry;
}

enum PORTMODE { PM_LIVE, PM_RECORDING };
const int max_data_points=1000000;

/*! \class Oximetry
    \author Mark Watkins <jedimark_at_users.sourceforge.net>
    \brief Oximetry view for working with Pulse Oximetry data and devices
    */
class Oximetry : public QWidget
{
    Q_OBJECT

public:
    explicit Oximetry(QWidget *parent, gGraphView * shared=NULL);
    ~Oximetry();

    //! \brief Calls updateGL to redraw the graphs
    void RedrawGraphs();

    //! \brief Returns the gGraphView object containing Oximetry graphs
    gGraphView *graphView() { return GraphView; }

    //! \brief Loads and displays a session containing oximetry data into into the Oximetry module
    void openSession(Session * session);

private slots:
    //! \brief Scans the list of serial ports and detects any oximetry devices
    void on_RefreshPortsButton_clicked();

    //! \brief Start or Stop live view mode
    void on_RunButton_toggled(bool checked); // Live mode button

    //! \brief This slot gets called when a new serial port is selected from the drop down
    void on_SerialPortsCombo_activated(const QString &arg1);

    //! \brief Start the Serial import process from the devices internal recordings
    void on_ImportButton_clicked();

    //! \brief Asks to save oximetry session into SleepLib database
    void on_saveButton_clicked();

    //! \brief Data has been changed, so it sets all the bits for live graph display
    void data_changed();

    //! \brief Updates the Pulse Rate LCD widget when the live pulse changes
    void pulse_changed(float p);

    //! \brief Updates the SpO2 LCD widget when the live spO2 changes
    void spo2_changed(float o2);

    //! \brief Updates the progress bar during import
    void update_progress(float f);

    //! \brief Import failed, so cleanup.
    void import_aborted();

    //! \brief Import completed, so get ready to display graphs
    void import_complete(Session *session);

    //! \brief Callback to make sure the oximeter is running
    void oximeter_running_check();

    //! \brief Callback after liveView mode is stopped
    void live_stopped(Session *session);

    //! \brief Open button was clicked, so select and load .spo/.spoR data files
    void on_openButton_clicked();

    //! \brief The datetime editor changed, so move the session data accordingly.
    void on_dateEdit_dateTimeChanged(const QDateTime &date);

    //! \brief Reset the datetime to what was set when first loaded
    void on_resetTimeButton_clicked();

private:
    //! \brief Imports a .spo file
    bool openSPOFile(QString filename);
    //! \brief Imports a .spoR file (from SPO2Review software in windows)
    bool openSPORFile(QString filename);

    //! \brief Clean up after import process, whether successful or not
    void import_finished();

    //! \brief update the graphs to show the session information
    void updateGraphs();
    Ui::Oximetry *ui;

    gGraphView *GraphView;
    MyScrollBar *scrollbar;
    QHBoxLayout *layout;

    gLineChart *pulse,*spo2,*plethy;
    Layer *lo1,*lo2;
    gGraph *PULSE,*SPO2,*PLETHY,*CONTROL;

    //! \brief Contains a list of gLineCharts that display Pulse, Plethy & SPO2 data
    QVector<gLineChart *> Data;

    QextSerialPort *port;
    QString portname;
    PORTMODE portmode;
    double lasttime,starttime;
    int lastpulse, lastspo2;

    Day * day;
    //Session * session;
    //EventList * ev_pulse;
    //EventList * ev_spo2;
    //EventList * ev_plethy;
    Layer * foobar;
    gGraphView * m_shared;

    SerialOximeter *oximeter;
    qint64 saved_starttime;
    bool firstSPO2Update;
    bool firstPulseUpdate;
    bool secondPulseUpdate;
    bool secondSPO2Update;
    bool dont_update_date;
};

#endif // OXIMETRY_H