/***************************************************************************
**
**  This file is part of GeopsyCore.
**
**  GeopsyCore 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 3 of the License, or
**  (at your option) any later version.
**
**  GeopsyCore 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 Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2006-08-24
**  Copyright: 2006-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef METADATA_H
#define METADATA_H

#include <QGpCoreTools.h>

#include "GeopsyCoreDLLExport.h"

class QScriptEngine;
class QScriptValue;

namespace GeopsyCore {

#define METADATA_CLASS(_className_) \
  public: \
    virtual const QString& xml_tagName() const {return xml##_className_##Tag;} \
    static const QString xml##_className_##Tag; \
\
    virtual void setId(int id) const {_id=id;} \
    virtual int id() const {return _id;} \
    static int staticId() {return _id;} \
\
    virtual MetaData * clone() const {return new _className_(*this);} \
    virtual const MetaData& defaultValue() const {return _defaultValue;} \
    static const MetaData& staticDefaultValue() {return _defaultValue;} \
  private: \
    static _className_ _defaultValue; \
    static int _id;


  class SignalHeaderObject;
  class Signal;

  class GEOPSYCORE_EXPORT MetaData: public XMLClass, public SharedObject
  {
  public:
    MetaData() {}
    virtual ~MetaData() {}

    virtual MetaData * clone() const=0;
    virtual const MetaData& defaultValue() const=0;
    virtual bool operator==(const MetaData& o) const=0;
    virtual void operator=(const MetaData&) {}

    virtual QStringList subNames() const {return QStringList();}
    virtual QStringList indexList(int subId) const {Q_UNUSED(subId) return QStringList();}
    virtual void setId(int id) const=0;
    virtual int id() const=0;
    int key() const {return id();}

    virtual bool isArray(int subId) const {Q_UNUSED(subId) return false;}
    virtual int count(int subId) const {Q_UNUSED(subId) return 1;}
    virtual bool hasIndex(int subId) const {Q_UNUSED(subId) return false;}
    virtual bool hasIndex(int subId, const QString& index) const {Q_UNUSED(subId) return index.isEmpty();}
    virtual QString name(int subId, const QString& index) const {Q_UNUSED(subId) Q_UNUSED(index) return QString();}
    virtual QString baseName(int subId) const {Q_UNUSED(subId) return QString();}
    virtual QString title(int subId, const QString& index) const {Q_UNUSED(subId) Q_UNUSED(index) return QString();}
    virtual bool isReadOnly(int subId) const {Q_UNUSED(subId) return false;}
    virtual QVariant data(int subId, const QString& index) const {Q_UNUSED(subId) Q_UNUSED(index) return QVariant();}
    virtual bool setData(int subId, const QString& index, const QVariant& val) {Q_UNUSED(subId) Q_UNUSED(index) Q_UNUSED(val) return true;}
    virtual int compare(int subId, const QString& index, const MetaData& o) const=0;

    virtual QString toString(const QString& index) const {Q_UNUSED(index) return QString();}
    virtual bool fromString(const QString& index, QString string) {Q_UNUSED(index) Q_UNUSED(string)return true;}

    virtual bool copyAllowed() const {return true;}
    virtual bool isStored() const {return true;}
    virtual bool storeAsProperty() const {return true;}
    virtual void writeProperties(XMLStream& s, const XMLSaveAttributes& attributes) const;
    void setSharedId(int id) {_sharedId=id;}
    void xml_writeLink(XMLStream& s) const;

    virtual void registerScriptTypes(QScriptEngine * engine) {Q_UNUSED(engine)}
    virtual void addScriptProperties(SignalHeaderObject * sig) {Q_UNUSED(sig)}
  private:
    int _sharedId;
  };

  class GEOPSYCORE_EXPORT MetaInteger: public MetaData
  {
  public:
    MetaInteger() {_value=0;}
    MetaInteger(int val) : MetaData() {_value=val;}

    virtual bool operator==(const MetaData& o) const {return _value==reinterpret_cast<const MetaInteger&>(o)._value;}
    virtual void operator=(const MetaData& o) {_value=reinterpret_cast<const MetaInteger&>(o)._value;}

    int value() const {return _value;}
    void setValue(int v) {_value=v;}

    virtual QVariant data(int subId, const QString& index) const {Q_UNUSED(subId) Q_UNUSED(index) return _value;}
    virtual bool setData(int subId, const QString& index, const QVariant& val);
    virtual int compare(int subId, const QString& index, const MetaData& o) const;

    virtual QString toString(const QString&) const {return QString::number(_value);}
    virtual bool fromString(const QString& , QString string) {bool ok; setValue(string.toInt(&ok)); return ok;}
  private:
    int _value;
  };

  class GEOPSYCORE_EXPORT MetaDouble: public MetaData
  {
  public:
    MetaDouble() {_value=0;}
    MetaDouble(double val) : MetaData() {_value=val;}

    virtual bool operator==(const MetaData& o) const {return _value==reinterpret_cast<const MetaDouble&>(o)._value;}
    virtual void operator=(const MetaData& o) {_value=reinterpret_cast<const MetaDouble&>(o)._value;}

    double value() const {return _value;}
    void setValue(double v) {_value=v;}

    virtual QVariant data(int subId, const QString& index) const {Q_UNUSED(subId) Q_UNUSED(index) return _value;}
    virtual bool setData(int subId, const QString& index, const QVariant& val);
    virtual int compare(int subId, const QString& index, const MetaData &o) const;

    virtual QString toString(const QString&) const {return QString::number(_value, 'g', 20);}
    virtual bool fromString(const QString& , QString string) {bool ok; setValue(string.toDouble(&ok)); return ok;}
  private:
    double _value;
  };

  template <class valueType> class GEOPSYCORE_EXPORT MetaArray: public MetaData
  {
  public:
    MetaArray() {_count=0;_values=nullptr;}
    ~MetaArray() {delete [] _values;}

    virtual bool operator==(const MetaData& o) const;
    virtual void operator=(const MetaData& o);

    valueType value(int index) const {return index<_count ? _values[index] : 0;}
    void setValue(int index, valueType v) {if(index>=_count) enlarge(index); _values[index]=v;}

    virtual bool isArray(int subId) const {Q_UNUSED(subId) return true;}
    virtual int count(int subId) const {Q_UNUSED(subId) return _count;}
    virtual bool hasIndex(int subId, const QString& index) const;
    virtual QVariant data(int subId, const QString& index) const;

    virtual void writeProperties(XMLStream& s, const XMLSaveAttributes& attributes) const;
  private:
    int _count;
    valueType * _values;

    void enlarge(int index);
  };

  template <class valueType>
  bool MetaArray<valueType>::operator==(const MetaData& o) const
  {
    const MetaArray<valueType>& ro=reinterpret_cast<const MetaArray<valueType>&>(o);
    if(_count!=ro._count) {
      return false;
    }
    for(int i=0; i<_count; i++) {
      if(_values[i]!=ro._values[i]) {
        return false;
      }
    }
    return true;
  }

  template <class valueType>
  void MetaArray<valueType>::operator=(const MetaData& o)
  {
    const MetaArray<valueType>& ro=reinterpret_cast<const MetaArray<valueType>&>(o);
    _count=ro._count;
    _values=new valueType[_count];
    memcpy(_values, ro._values, sizeof(valueType)*_count);
  }

  template <class valueType>
  bool MetaArray<valueType>::hasIndex(int subId, const QString& index) const
  {
    Q_UNUSED(subId)
    bool ok;
    int i=index.toInt(&ok);
    return ok && i<_count && _values[i]!=0;
  }

  template <class valueType>
  QVariant MetaArray<valueType>::data(int subId, const QString& index) const
  {
    Q_UNUSED(subId)
    bool ok;
    int i=index.toInt(&ok);
    if(ok && i<_count) {
      return _values[i];
    } else {
      return QVariant();
    }
  }

  template <class valueType>
  void MetaArray<valueType>::enlarge(int index)
  {
    TRACE;
    int newSize=_count>0 ? _count : 1;
    while(newSize<=index) newSize=newSize << 1;
    valueType * tmp=new valueType[newSize];
    if(_values) {
      memcpy(tmp, _values, sizeof(valueType)*_count);
      delete [] _values;
    }
    memset(tmp+_count, 0, sizeof(valueType)*(newSize-_count));
    _values=tmp;
    _count=newSize;
  }

  template <class valueType>
  void MetaArray<valueType>::writeProperties(XMLStream& s, const XMLSaveAttributes& attributes) const
  {
    TRACE;
    if(storeAsProperty()) {
      static const QString key("index");
      XMLSaveAttributes att(attributes);
      QString& value=att.add(key);
      for(int i=0;i<_count;i++) {
        value=QString::number(i);
        if(hasIndex(0, value)) {
          writeProperty(s,  xml_tagName(), att, toString(value));
        }
      }
    }
  }

  class GEOPSYCORE_EXPORT MetaArrayInteger: public MetaArray<int>
  {
  public:
    MetaArrayInteger() : MetaArray<int>() {}

    virtual bool setData(int subId, const QString& index, const QVariant& val);
    virtual int compare(int subId, const QString& index, const MetaData &o) const;

    virtual QString toString(const QString& index) const;
    virtual bool fromString(const QString& index, QString string);
  };


  class GEOPSYCORE_EXPORT MetaArrayDouble: public MetaArray<double>
  {
  public:
    MetaArrayDouble() : MetaArray<double>() {}

    virtual bool setData(int subId, const QString& index, const QVariant& val);
    virtual int compare(int subId, const QString& index, const MetaData& o) const;

    virtual QString toString(const QString& index) const;
    virtual bool fromString(const QString& index, QString string);
  };

  class GEOPSYCORE_EXPORT MetaString: public MetaData
  {
  public:
    MetaString() {}
    MetaString(const QString& str) : MetaData() {setValue(str);}

    virtual bool operator==(const MetaData& o) const {return _value==reinterpret_cast<const MetaString&>(o)._value;}
    virtual void operator=(const MetaData& o) {_value=reinterpret_cast<const MetaString&>(o)._value;}

    virtual QVariant data(int subId, const QString& index) const {Q_UNUSED(subId) Q_UNUSED(index) return _value;}
    virtual bool setData(int subId, const QString& index, const QVariant& val);
    virtual int compare(int subId, const QString& index, const MetaData& o) const;

    const QString& value() const {return _value;}
    void setValue(const QString& v) {_value=v;}
    void operator+=(const QString& s) {_value+=s;}

    virtual QString toString(const QString& ) const {return value();}
    virtual bool fromString(const QString& , QString string) {setValue(string); return true;}
  private:
    QString _value;
  };

} // namespace GeopsyCore

#endif // METADATA_H
