
#define __STDC_LIMIT_MACROS
#include <stdint.h>

#include "ads_data.h"

#include <QVector>

typedef QVector<AdsLogicData*> logicDataDb_t;
typedef QVector<AdsDataAction*> actionDataDb_t;

static logicDataDb_t dDb;
static actionDataDb_t actDb;


AdsLogicData *AdsDataManager::findLogicData(const QString& name)
{
  AdsLogicData* d;
  foreach(d, dDb)
  {
    if (name.compare(d->objectName(), Qt::CaseInsensitive)==0)
      return d;
  }
  return 0;
}

AdsDataAction *AdsDataManager::findDataAction(const QString& name)
{
  AdsDataAction* d;
  foreach(d, actDb)
  {
    //if (name==d->objectName())
    if (name.compare(d->objectName(), Qt::CaseInsensitive)==0)
      return d;
  }
  return 0;
}

int AdsDataManager::logicDataCount()
{
  return dDb.size();
}

AdsLogicData *AdsDataManager::getLogicData(int pos)
{
  return dDb[pos];
}

int AdsDataManager::dataActionCount()
{
  return actDb.size();
}

AdsDataAction* AdsDataManager::getDataAction(int pos)
{
  return actDb[pos];
}

QString AdsDataManager::makeUnknownName()
{
  static unsigned int unkwnowObjCounter=0;

  ++unkwnowObjCounter;
  return QString("ads_unknown_%1").arg(unkwnowObjCounter);
}

AdsDataManager* AdsDataManager::instance()
{
    static AdsDataManager dmInstance(0);
    return &dmInstance;
}

AdsDataManager::AdsDataManager(QObject* parent): QObject(parent)
{
}

AdsDataManager::~AdsDataManager()
{
}

QObject *AdsDataManager::findDatum(const QString &name)
{
    return AdsDataManager::findLogicData(name);
}

QObject* AdsDataManagerInstance()
{
    return AdsDataManager::instance();
}

AdsLogicData::AdsLogicData(const char* const name, AdsLogicDataDelegate* delegate):
    manipulator(delegate),
    value_(0),
    cached_value(0),
    pMsg(0),
    wPos(0),
    rMin(SHRT_MIN),
    rMax(SHRT_MAX),
    rPos(0),
    rMask(0xFFFF),
    lsb(1.0),
    busMask(0xFFFF)
{
  QString str(name);
  setDataName(name);
  {
    //dDb.push_back(this);
    for(logicDataDb_t::iterator i=dDb.begin(); i!=dDb.end(); ++i)
    {
      QString tmp=(*i)->objectName();
      if (QString::compare(str, tmp, Qt::CaseInsensitive)<0)
      {
        dDb.insert(i, this);
        return;
      }
    }
    dDb.push_back(this);
  }
}

void AdsLogicData::setManipulator(AdsLogicDataDelegate &m)
{
  manipulator=&m;
}

void AdsLogicData::setDataName(const char *const name)
{
  if (name)
    setObjectName(name);
  else
    setObjectName(AdsDataManager::makeUnknownName());
}

void AdsLogicData::setDataName(const QString& name)
{
    if (!name.isEmpty())
        setObjectName(name);
    else
       setObjectName(AdsDataManager::makeUnknownName());
}

void AdsLogicData::setFormat(unsigned int pos, unsigned int mask)
{
  rPos=pos;
  rMask=mask;
  busMask= (rMask<<rPos);
}

void AdsLogicData::setRange(int min, int max)
{
  rMin=min;
  rMax=max;
}

void AdsLogicData::setLsb(double v)
{
  lsb=v;
  cached_value=value_*lsb;
}

void AdsLogicData::attachToMsg(Ads1553MsgQ *const msg, int w)
{
  if (pMsg)
    disconnect();

  pMsg=msg;

  wPos=w;

  unsigned int updMask=manipulator->wordSelector(w);

  if (pMsg)
  {
    pMsg->messageModified.ads_connect_mask(this, onMsgReceived_callback, updMask);
  }
}


ADS_SLOT_IMPLEMENT(AdsLogicData, onMsgReceived_callback, (Ads1553MsgQ * msg), (msg))
{
  onMsgReceived(msg);
}

void AdsLogicData::onMsgReceived(Ads1553MsgQ * msg)
{
  setFromRaw(manipulator->get(*msg, wPos, rPos, busMask));
}

double AdsLogicData::toDouble() const
{
  return value_*lsb;
}

double AdsLogicData::cvFromExtern(const unsigned short* const m) const
{
  int tmp=manipulator->get(m, wPos, rPos, busMask);
  return tmp*lsb;
}

bool AdsLogicData::toBool() const
{
  return !!value_;
}

bool AdsLogicData::isReadOnly() const
{
  if (pMsg)
    return pMsg->isReadOnly();
  return false;
}

bool AdsLogicData::isOutOfRange() const
{
  return value_<rMin || value_>rMax;
}

int AdsLogicData::getRawValue()
{
    return value_;
}

void AdsLogicData::setFromRaw(int v)
{
  //v=(v>>rPos) & rMask;

  if (value_!=v)
  {
    value_=v;
    cached_value=v*lsb;
    Q_EMIT sigUpdated();
  }
}

void AdsLogicData::setFromUser(int v)
{
  if (value_!=v)
  {
    value_=v;

    manipulator->set(*pMsg, wPos, v, rPos, busMask);
    /*
    short int x=pData->value();
    x= x & busMask;
    v=v<<rPos;
    x=x|v;

    //Q_EMIT rawUpdated(x);
    pData->setFromUser(x);
    */

    //After the previous signal, the datum slot setFromRaw is called finding the value unchanged
    //Another signal is required to force an update of all the widget attached to me
    Q_EMIT sigUpdated();
  }
}

AdsLogicDataDelegate& AdsLogicData::getManipulator() const
{
  return *manipulator;
}

void AdsLogicData::setFromUser(double val)
{
  //setFromUser(int(v));
  int v=int((val/lsb)+0.5);

  //v= (v & rMask);

  if (value_!=v)
  {
    cached_value=v*lsb;  //note, could be cached_value!=val
    setFromUser(v);
    /*
    value_=v;

    int x=pData->value();
    x= x & busMask;
    v=v<<rPos;
    x=x|v;

    //Q_EMIT rawUpdated(x);
    pData->setFromUser(x);
    //After the previous signal, the datum slot setFromRaw is called finding the value unchanged
    //Another signal is required to force an update of all the widget attached to me
    emit sigUpdated();
    */
  }

}

AdsDataAction::AdsDataAction(AdsLogicData *datum, const char *const name)
{
  if (name)
    setObjectName(name);
  actDb.push_back(this);
  attachTo(datum);
}

void AdsDataAction::attachTo(AdsLogicData * datum)
{
  d=datum;
}

void AdsDataAction::onQAction()
{
  execute();
}

void AdsDataAction::execute(bool)
{
}

AdsDataActionSet::AdsDataActionSet(AdsLogicData *datum):
    AdsDataAction(datum)
{
}

void AdsDataActionSet::setActionValue(int value)
{
  set_value=value;
}

void AdsDataActionSet::execute(bool)
{
  if (!d)
    return;
  d->setFromUser(set_value);
}

AdsDataActionSet2::AdsDataActionSet2(AdsLogicData *datum, AdsLogicData* datum2):
    AdsDataAction(datum),
    d2(datum2)
{
}

void AdsDataActionSet2::setActionValue(int value1, int value2)
{
  set_value1=value1;
  set_value2=value2;
}

void AdsDataActionSet2::attachTo(AdsLogicData *d1, AdsLogicData *d2_)
{
  d=d1;
  d2=d2_;
}

void AdsDataActionSet2::execute(bool)
{
  if (!d)
    return;
  d->setFromUser(set_value1);
  if (!d2)
    return;
  d2->setFromUser(set_value2);
}

AdsDataActionSetReset::AdsDataActionSetReset(AdsLogicData *datum):
    AdsDataActionSet(datum)
{
}

void AdsDataActionSetReset::setActionValue(int s_value, int r_value, int delay)
{
  AdsDataActionSet::setActionValue(s_value);
  reset_value=r_value;
  reset_delay=delay;
}

void AdsDataActionSetReset::execute(bool)
{
  if (!d)
    return;

  int r_delay=reset_delay;
  if (d->getValue()!=reset_value)
  {
    d->setFromUser(reset_value);
    Ads1553Manager::instance().scheduleMessageAction(*d->getMsg(), r_delay, AdsDataActionSetReset::preset_action, this);
    r_delay*=2;
  }
  else
    d->setFromUser(set_value);

  Ads1553Manager::instance().scheduleMessageAction(*d->getMsg(), r_delay, AdsDataActionSetReset::reset_action, this);
}

void AdsDataActionSetReset::preset_action(void *arg)
{
  AdsDataActionSetReset* obj=reinterpret_cast<AdsDataActionSetReset*>(arg);
  if (!obj->d)
    return;
  obj->d->setFromUser(obj->set_value);
}

void AdsDataActionSetReset::reset_action(void* arg)
{
  AdsDataActionSetReset* obj=reinterpret_cast<AdsDataActionSetReset*>(arg);
  if (!obj->d)
    return;
  obj->d->setFromUser(obj->reset_value);
}

AdsDataActionCycle::AdsDataActionCycle(AdsLogicData *datum):
    AdsDataActionSet(datum)
{
}

void AdsDataActionCycle::setActionValue(int min, int max)
{
  AdsDataActionSet::setActionValue(min);
  v_max=max;
}

void AdsDataActionCycle::execute(bool /*active*/)
{
  if (!d)
    return;
  int x=d->getValue();
  ++x;
  if (x>v_max)
    x=set_value;
  d->setFromUser(x);
}

AdsDataActionSetAndCycle::AdsDataActionSetAndCycle(AdsLogicData* master_datum, AdsLogicData* datum):
  AdsDataActionCycle(datum),
  m_datum(master_datum)
{
}

void AdsDataActionSetAndCycle::attachTo(AdsLogicData* d1, AdsLogicData* d2)
{
  m_datum=d1;
  AdsDataActionCycle::attachTo(d2);
}

void AdsDataActionSetAndCycle::setActionValue(int master_value, int min, int max)
{
  m_value=master_value;
  AdsDataActionCycle::setActionValue(min, max);
}

void AdsDataActionSetAndCycle::execute(bool b)
{
  if (!d)
    return;
  if (!m_datum)
    return;
  if (m_datum->getValue()==m_value)
  {
    AdsDataActionCycle::execute(b);
  }
  else
    m_datum->setFromUser(m_value);
}

/*
AdsDataActionMap::AdsDataActionMap(AdsDataAction *dataAct, QAction *qact):
    dAct(dataAct),
    qAct(qact)
{
  if (qAct)
    connect(qAct, SIGNAL(triggered()), SLOT(execute()));
}

void AdsDataActionMap::execute()
{
  if (dAct)
    dAct->execute();
}
*/

//Delegates:

class AdsLogicDataDelegateUInt16_: public AdsLogicDataDelegate
{
public:
  void set(Ads1553MsgQ& m, int w, long value, unsigned int pos, unsigned int mask) const
  {
    int v=int(value);
    v=v & 0x0FFFF;
    v=v<<pos;
    v=v & mask;
    avb_1553_word_t original=m.data(w);
    avb_1553_word_t tmp=original & (~mask);
    v=v|tmp;
    if (v!=original)
      m.setData(w, v);
  }

  virtual long get(const unsigned short* const d, int w, unsigned int pos, unsigned int mask) const
  {
    unsigned int tmp=d[w];
    tmp=tmp & mask;
    tmp=tmp>>pos;
    return tmp;
  }

  long get(Ads1553MsgQ& m, int w, unsigned int pos, unsigned int mask) const
  {
    unsigned int tmp=m.data(w);
    tmp=tmp & mask;
    tmp=tmp>>pos;
    return tmp;
  }
  unsigned int wordSelector(int w) const
  {
    return 1<<w;
  }
  long min()  const
  {
    return 0;
  }
  long max()  const
  {
    return UINT16_MAX;
  }

};

class AdsLogicDataDelegateInt16_: public AdsLogicDataDelegate
{
public:
  void set(Ads1553MsgQ& m, int w, long value, unsigned int pos, unsigned int mask) const
  {
    int tmp=int(value);
    //tmp=tmp & 0x0FFFF;
    tmp=tmp<<pos;
    tmp=tmp & mask;
    avb_1553_word_t new_val=tmp;
    avb_1553_word_t original=m.data(w);
    avb_1553_word_t tmp2=original & (~mask);
    new_val=new_val|tmp2;
    if (new_val!=original)
      m.setData(w, new_val);
  }

  virtual long get(const unsigned short* const  d, int w, unsigned int pos, unsigned int mask) const
  {
    unsigned int tmp=d[w];
    mask=mask & 0x0FFFF;
    unsigned int m2=mask>>pos;
    tmp=tmp & mask;
    tmp=tmp>>pos;
    //Sign extension:
    unsigned int sign_mask=~(m2>>1);
    unsigned int sign=tmp & sign_mask;
    if (sign)
    {
      unsigned int x=0xFFFFFFFF;
      x=x ^ m2;
      tmp=tmp|x;
    }
    return tmp;
  }

  long get(Ads1553MsgQ& m, int w, unsigned int pos, unsigned int mask) const
  {
    unsigned int tmp=m.data(w);
    mask=mask & 0x0FFFF;
    unsigned int m2=mask>>pos;
    tmp=tmp & mask;
    tmp=tmp>>pos;
    //Sign extension:
    unsigned int sign_mask=~(m2>>1);
    unsigned int sign=tmp & sign_mask;
    if (sign)
    {
      unsigned int x=0xFFFFFFFF;
      x=x ^ m2;
      tmp=tmp|x;
    }
    return tmp;
  }
  unsigned int wordSelector(int w) const
  {
    return 1<<w;
  }
  long min()  const
  {
    return INT16_MIN;
  }
  long max()  const
  {
    return INT16_MAX;
  }
};

class AdsLogicDataDelegateInt32_: public AdsLogicDataDelegate
{
public:
  void set(Ads1553MsgQ& m, int w, long value, unsigned int pos, unsigned int mask) const
  {
      long tmp=long(value);
      tmp=tmp<<pos;
      tmp=tmp & mask;
      long new_val=tmp;
      long original=m.data32(w);
      long tmp2=original & (~mask);
      new_val=new_val|tmp2;
      if (new_val!=original)
        m.setData32(w, new_val);
#if 0
    unsigned long tmp=value;
    tmp=(tmp<<pos);// & mask;
    unsigned long original=m.data32(w);
    unsigned int src=original & mask;
    if (tmp!=src)
    {
        src=original & ((~mask)<<pos);
        tmp=tmp|src;
        m.setData32(w, tmp);
    }
    //if (tmp!=original)
    //  m.setData32(w, tmp);
#endif
  }

  virtual long get(const unsigned short* const  d, int w, unsigned int pos, unsigned int mask) const
  {
      unsigned long tmp=(d[w]<<16)|d[w+1];
      unsigned long m2=mask>>pos;
      tmp=tmp & mask;
      tmp=tmp>>pos;
      //Sign extension:
      unsigned int sign_mask=~(m2>>1);
      unsigned int sign=tmp & sign_mask;
      if (sign)
      {
        unsigned long x=0xFFFFFFFF;
        x=x ^ m2;
        tmp=tmp|x;
      }
      return tmp;
#if 0
    unsigned long tmp=(d[w]<<16)|d[w+1];
    unsigned int ext=(tmp & 0x80000000) ? (0xFFFFFFFF<<(32-pos)) : 0;
    tmp=tmp>>pos;
    tmp|=ext;
    return tmp;
#endif
  }

  long get(Ads1553MsgQ& m, int w, unsigned int pos, unsigned int mask) const
  {
      unsigned int tmp=m.data32(w);
      unsigned int m2=mask>>pos;
      tmp=tmp & mask;
      tmp=tmp>>pos;
      //Sign extension:
      unsigned long sign_mask=~(m2>>1);
      unsigned long sign=tmp & sign_mask;
      if (sign)
      {
        unsigned int x=0xFFFFFFFF;
        x=x ^ m2;
        tmp=tmp|x;
      }
      return tmp;
#if 0
    unsigned long tmp=m.data32(w);
    unsigned int ext=(tmp & 0x80000000) ? (0xFFFFFFFF<<(32-pos)) : 0;
    tmp=tmp>>pos;
    tmp|=ext;
    return tmp;
#endif
  }
  unsigned int wordSelector(int w) const
  {
    return (1<<w)|(1<<(w+1));
  }
  long min()  const
  {
    return INT32_MIN;
  }
  long max()  const
  {
    return INT32_MAX;
  }

};

static AdsLogicDataDelegateUInt16_ adsLogicDataDelegateUInt16_;
static AdsLogicDataDelegateInt16_ adsLogicDataDelegateInt16_;
static AdsLogicDataDelegateInt32_ adsLogicDataDelegateInt32_;

AdsLogicDataDelegate& AdsLogicDataDelegateUInt16=adsLogicDataDelegateUInt16_;
AdsLogicDataDelegate& AdsLogicDataDelegateInt16=adsLogicDataDelegateInt16_;
AdsLogicDataDelegate& AdsLogicDataDelegateInt32=adsLogicDataDelegateInt32_;
