#ifndef MCS_1553_H_
#define MCS_1553_H_

#include <QObject>
#include <QVector>
#include <QLinkedList>

#include "avb_driver.h"

#include "ads_fastslots.h"

struct ads_1553msg_t;

class Ads1553MsgQ;
class Ads1553Manager;
class Ads1553MsgPrivateContext;

typedef void (*ads_1553_synchronous_message_routine_t)(IAvbDriver* const drv, ads_1553msg_t* const msg, avb_microseconds_t tt);
typedef void (*ads_1553_asynchronous_message_routine_t)(Ads1553MsgQ* msg);

struct ads_1553msg_t {
  avb_1553_message_t desc;
  //avb_1553_data_buffer_t data;

  ads_1553_synchronous_message_routine_t synch_routine;
  ads_1553_asynchronous_message_routine_t async_routine;

  Ads1553MsgPrivateContext* qmsg;
} ;

typedef ads_1553msg_t* ads_1553msg_list_t;  //null terminated!!


class Ads1553MessageCheck
{
public:
  Ads1553MessageCheck();

  const char* name() const { return info.name;}
  const char* description() const { return info.description;}

  unsigned long delayTime() const { return info.delayTime;}
  unsigned long durationTime() const { return info.durationTime;}

  unsigned int fails() const { return info.fails;}
  unsigned int checks() const { return info.checks;}

  const char* lastFailReason() const { return info.lastFailReason;}

  void* getCookie() { return info.usrCookie;}
  void  setCookie(void* cookie) { info.usrCookie=cookie;}

  bool groupItem() const { return false;}

  virtual void execute(avb_1553_mon_decoded_t&)=0;

  ADS_SIGNAL(updated, (Ads1553MessageCheck&));

protected:
  struct info_t {
    const char* name;
    const char* description;
    unsigned long delayTime;
    unsigned long durationTime;
    unsigned long checks;
    unsigned long fails;
    const char* lastFailReason;

    void* usrCookie;
  };
  info_t info;

};

class Ads1553MessageCheckGroup
{
public:
    const char* name() const { return "?";}
};


typedef void (*ads_simple_action_t)(void*);

typedef struct {
  int delay;
  ads_simple_action_t act;
  void* arg;
} ads_msg_scheduled_action_t;

typedef QLinkedList<ads_msg_scheduled_action_t> ads_msg_scheduled_action_vector_t;

class Ads1553MsgQ: public QObject
{
  Q_OBJECT

public:
  //typedef void (*callback_t)(Ads1553MsgQ*, void* arg);

  Ads1553MsgQ();

  avb_1553_word_t data(int w) { return buffer[w];}
  void setData(int w, avb_1553_word_t value);

  void setDataNoSignal(int w, avb_1553_word_t value);
  void setData32NoSignal(int w, long value);
  long data32(int w) { unsigned long tmp=buffer[w+1]|(buffer[w]<<16); return static_cast<long>(tmp);}
  void setData32(int w, long value);

  //Ads1553MsgWordQ& dataQ(int w) { return data_[w];}

  const ads_1553msg_t* rawInfo() const { return raw;}
  void setRawInfo(const ads_1553msg_t* const r) { raw=r;}

  bool isModified() const { return modified;}
  bool isPending() const { return pending_out;}
  void setModified(unsigned int w) { pending_out=true; modified=true; messageModified.events_pending|=1<<w;}
  void setModifiedMask(unsigned int m) { pending_out=true; modified=true; messageModified.events_pending|=m;}
  void resetModified() { modified=false; messageModified.events_pending=0;}
  void resetPending() { pending_out=false;}

  bool isReadOnly() const;
  int wordCount() const { return wc;}
  void setWordCount(int n) { wc=n;}

  const avb_1553_word_t* getBuffer() const { return &buffer[0];}

  void addDataCheck(Ads1553MessageCheck& chk);

  ADS_SIGNAL(messageReceived, (Ads1553MsgQ* msg));
  ADS_SIGNAL(messageModified, (Ads1553MsgQ* msg));

public slots:
  void updateFromBus();
  void updateFromUser();

  void setReadOnly(bool);

protected:
  int wc;
  bool m_readonly;
  bool pending_out;
  bool modified;
  bool force_refresh;

  const ads_1553msg_t* raw;
  avb_1553_word_t buffer[32+1];

  ads_msg_scheduled_action_vector_t sched_act;

  QVector<Ads1553MessageCheck*> dataChecks;
  void execDataChecks(avb_1553_mon_decoded_t&);

  friend class Ads1553Manager;
};

class Ads1553TerminalStatus
{
public:
  Ads1553TerminalStatus();

  bool valid() const { return d.valid;}

  const char* name() const;
  const char* longName() const;

  avb_1553_terminal_t* termDeclaration() const { return d.rawT;}

  unsigned int address() const { return d.adr;}
  unsigned int numOfMessages() const { return d.msg;}
  unsigned int received() const { return d.rx;}
  unsigned int transmitted() const { return d.tx;}
  unsigned int errors() const { return d.errs;}
  unsigned int noResponse() const { return d.nr;}
  avb_1553_sts_word_t lastSW() const { return d.last_sw;}
  unsigned int rawErrorCode() const { return d.rawErr;}

  bool simulated() const { return d.rawT ? d.rawT->mode==avb_term_simulated : false;}
  bool disabled() const { return d.rawT ? d.rawT->mode==avb_term_disabled : false;}
  bool active() const { return !simulated() && !disabled();}

  bool updated() const { return d.updated;}
  void resetUpdated() { d.updated=false;}
  void setClientCookie(void* cookie) { d.monCookie=cookie;}
  void* getClientCookie() const { return d.monCookie;}

protected:
  struct data_t
  {
    bool valid;
    bool updated;
    avb_1553_terminal_t* rawT;
    unsigned int adr;
    unsigned int msg;
    unsigned int rx;
    unsigned int tx;
    unsigned int errs;
    unsigned int nr;
    unsigned int me;
    avb_1553_sts_word_t last_sw;
    unsigned int rawErr;

    void* monCookie;
  };

  data_t d;

  friend class Ads1553Manager;
};

class Ads1553MessageStatus
{
public:
  Ads1553MessageStatus();

  bool valid() const { return d.valid;}

  const char* name() const;
  const char* longName() const;

  avb_1553_message_t* msgDeclaration() { return d.rawM;}
  Ads1553MsgQ* adsMsq() { return d.qMsg;}
  unsigned int code() const { return d.code;}
  avb_1553_cmd_word_t cw() const { return d.cw;}
  bool SwValid() const { return d.sw_valid;}
  avb_1553_sts_word_t lastSW() const { return d.sw;}
  unsigned int transferedWc() const { return d.tWc;}

  unsigned int numOfMessages() const { return d.msg;}
  unsigned int errors() const { return d.errs;}
  unsigned int noResponses() const { return d.nr;}
  bool lastIsNoResponse() const { return !d.sw_valid;}
  unsigned int rawErrorCode() const { return d.rawErr;}
  unsigned int exceptionBits() const { return d.exception_bits;}

  unsigned int lastTime() const { return d.last_tt;}
  unsigned int deltaTime() const { return d.delta_tt;}
  double deltaTimeMs() const { return d.delta_tt*0.001;}

  bool updated() const { return d.updated;}
  void resetUpdated() { d.updated=false;}
  void setClientCookie(void* cookie) { d.monCookie=cookie;}
  void* getClientCookie() const { return d.monCookie;}

  avb_1553_word_t* contents() { return &d.lastContents[0];}
protected:
  struct data_t
  {
    bool valid;
    bool updated;

    avb_1553_message_t* rawM;
    Ads1553MsgQ* qMsg;

    unsigned int code;
    unsigned int msg;
    unsigned int errs;

    avb_1553_cmd_word_t cw;

    unsigned int tWc;
    bool sw_valid;
    avb_1553_sts_word_t sw;
    unsigned int nr;
    unsigned int exception_bits;
    unsigned int rawErr;

    unsigned int last_tt;
    unsigned int delta_tt;

    avb_1553_word_t lastContents[32];

    void* monCookie;
  };

  data_t d;
  friend class Ads1553Manager;
};

struct ads_acq_data_t
{
  Ads1553MsgQ* msg;
  avb_1553_mon_decoded_t decoded;
};

class Ads1553AcqAnalyzer
{
public:
  virtual unsigned int items() const =0;

  virtual const ads_acq_data_t& get(unsigned int n) const =0;

};

class Ads1553Manager: public QObject
{
  Q_OBJECT
public:
  enum run_sts_t { sts_null=0, sts_device_err=1, sts_ready=2, sts_halted=3, sts_bc_running=4, sts_mon_running=8, sts_bc_mon_running=8|4};
  enum com_sts_t { sts_comm_null=0, sts_comm_device_err, sts_comm_idle, sts_comm_ok, sts_comm_some_errors, sts_comm_error};

  static Ads1553Manager& instance();

  static void setDriver(IAvbDriver* const d);

  void installTerminalList(avb_1553_terminal_t** const);

  void installMessages(ads_1553msg_t** const);
  void installFrame(avb_1553_bc_message_t** const frame, unsigned int minor_frame_time_us);

  void setBeginOfMajorFrameHandler(avb_1553_major_frame_handler_t);

  const char* fromAdrToName(unsigned int rt);
  void run(bool run_forever=true);
  void stop();
  void shutdown();
  void run_monitor();
  void stop_monitor();

  void setPollingPeriod(int ms); //0=data driven

  int numOfTerminals() const;
  avb_1553_terminal_t* getTerminal(int id);

  Ads1553TerminalStatus* terminalStatus(unsigned int ard);

  int numOfMessages() const;

  unsigned int numOfDynMessage() const;
  Ads1553MessageStatus*  messageStatusByIndex(unsigned int pos);
  //Ads1553MessageStatus* const firstDynMessage();
  //Ads1553MessageStatus* const nextDynMessage(Ads1553MessageStatus*);

  Ads1553MessageStatus*  messageStatus(unsigned int code);

  virtual Ads1553MsgQ* getMsgObj(int index);
  virtual Ads1553MsgQ* getMsgObj(const char* const name);
  virtual Ads1553MsgQ* getMsgObj(int rt, int sa, int tr, int wc=-1);

  void writeDeviceBuffer(Ads1553MsgQ&, avb_1553_data_buffer_t& data);

  void scheduleAction(int delay, ads_simple_action_t act, void*);
  void scheduleMessageAction(Ads1553MsgQ&, int num_msg_pending, ads_simple_action_t act, void*);

  unsigned int getTimetag();

  IAvbDriver* getDriver();

  void acqStart();
  void acqStop();
  Ads1553AcqAnalyzer& getAcqAnalyzer();

  void addCheck(Ads1553MsgQ& msg, Ads1553MessageCheck& chk);

  static void setMonitorOnly(bool enable=true);
  static bool monitorOnly();
  static void setMonitorSave(bool enable);

private:
  static void create();

  virtual void timerEvent(QTimerEvent *);
  virtual void customEvent(QEvent *);

  virtual void processMessages();
  virtual void processMonitor();

  Ads1553Manager();
  Ads1553Manager(const Ads1553Manager&);
  void operator=(const Ads1553Manager&);
  int tid;
  int pending;
  unsigned int polling_period;
  unsigned int mon_polling_period;

  void changeRunStatus(run_sts_t);

signals:
  void terminalListChanged();
  void messageListChanged();
  void runStatusChanged(Ads1553Manager::run_sts_t);

  void commStatusChanged(Ads1553Manager::com_sts_t);

  void monitorUpdated(avb_1553_monitor_info_t* info);

  void acqUpdated(bool in_progress, unsigned int filled_percentage);

  void checkAdded(Ads1553MessageCheck& chk);
};

#endif /*MCS_1553_H_*/
