/***************************************************************************
**
**  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: 2010-05-03
**  Copyright: 2010-2019
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "MetaDataFactory.h"

namespace GeopsyCore {

  /*!
    \class MetaDataFactory MetaDataFactory.h
    \brief Brief description of class still missing

    Full description of class still missing
  */

  MetaDataFactory * MetaDataFactory::_self=0;

  void MetaDataFactory::init()
  {
    if(!_self) {
      _self=new MetaDataFactory;
      registerFactory(_self); // Required for proper deletion
    }
  }

  MetaDataFactory::MetaDataFactory()
  {
    // Register standard names
    for(int i=0; i<DataCount; i++) {
      int id=newId();
      ASSERT(id==i);
      _names.insert(standardName(static_cast<StandardData>(id)), MetaDataIndex(id, 0, 0));
    }
  }

  int MetaDataFactory::registerClass(const QString& tag, XMLClassCreator * creator)
  {
    MetaData * d=reinterpret_cast<MetaData *>(creator->create());
    QStringList l=d->subNames();
    int n=l.count();
    // Check uniqueness of names
    for(int i=0; i<n; i++) {
      if(_names.contains(l.at(i))) {
        qWarning("Registering meta data '%s': sub name '%s'' already registered.",
                 tag.toLatin1().data(), l.at(i).toLatin1().data());
        delete d;
        delete creator;
        return -1;
      }
    }
    int id=XMLClassFactory::registerClass(tag, creator);
    // Add all sub names
    for(int i=0; i<n; i++) {
      _names.insert(l.at(i), MetaDataIndex(id, i, 0));
    }
    // Store id
    d->setId(id);
    delete d;
    return id;
  }

  QString MetaDataFactory::standardName(StandardData id)
  {
    switch(id) {
    case ID: return "Id";
    case StartTime: return "StartTime";
    case SamplingPeriod: return "SamplingPeriod";
    case NSamples: return "NSamples";
    case ReceiverX: return "ReceiverX";
    case ReceiverY: return "ReceiverY";
    case ReceiverZ: return "ReceiverZ";
    case UtmZone: return "UtmZone";
    case Name: return "Name";
    case Type: return "Type";
    case FileNumber: return "FileNumber";
    case NumberInFile: return "NumberInFile";
    case FileName: return "FileName";
    case FileFormat: return "FileFormat";
    case ShortFileName: return "ShortFileName";
    case Duration: return "Duration";
    case EndTime: return "EndTime";
    case Component: return "Component";
    case MaximumAmplitude: return "MaximumAmplitude";
    case AverageAmplitude: return "AverageAmplitude";
    case IsOriginalFile: return "IsOriginalFile";
    case SamplingFrequency: return "SamplingFrequency";
    case Pointer: return "Pointer";
    case CountPerVolt: return "CountPerVolt";
    case VoltPerCount: return "VoltPerCount";
    case VoltPerUnit: return "VoltPerUnit";
    case UnitPerVolt: return "UnitPerVolt";
    case CountPerUnit: return "CountPerUnit";
    case UnitPerCount: return "UnitPerCount";
    case AmplitudeUnit: return "AmplitudeUnit";
    case SampleSize: return "SampleSize";
    case HeaderModified: return "HeaderModified";
    case Dummy: return "Dummy";
    case DataCount: return "DataCount";
    }
    return QString();
  }

  /*!
    Convert a \a name (eventually including an array index) to a MetaDataIndex.
    Returns an invalid index if \a name cannot be recognized.
  */
  MetaDataIndex MetaDataFactory::index(const QString& name) const
  {
    int is=name.lastIndexOf("[");
    int ie=name.lastIndexOf("]");
    MetaDataIndex index;
    if(is<ie && is>-1) {
      QMap<QString, MetaDataIndex>::const_iterator it;
      it=_names.find(name.left(is));
      if(it!=_names.end()) {
        index=it.value();
        QString arg=name.mid(is+1, ie-is-1);
        if(arg[0]=='\"') {
          arg=arg.mid(1, arg.count()-2);
        }
        index.setIndex(arg);
      } else {
        App::log(tr("Meta data '%1': unrecognized name, missing plugin?\n").arg(name) );
        return MetaDataIndex();
      }
    } else {
      QMap<QString, MetaDataIndex>::const_iterator it;
      it=_names.find(name);
      if(it!=_names.end()) {
        index=it.value();
      } else {
        App::log(tr("Meta data '%1': unrecognized name, missing plugin?\n").arg(name) );
        return MetaDataIndex();
      }
    }
    return index;
  }

  /*!
    Converts an \a index into a variable name including array index if any.
  */
  QString MetaDataFactory::name(const MetaDataIndex& index) const
  {
    if(index.isValid()) {
      if(index.id()<DataCount) {
        return standardName(static_cast<StandardData>(index.id()));
      } else {
        MetaData * d=create(index.id());
        if(d) {
          QString n=d->name(index.subId(), index.index());
          delete d;
          return n;
        }
      }
    }
    return "Dummy";
  }

  /*!
    Converts an \a index into a variable name excluding array index.
  */
  QString MetaDataFactory::baseName(const MetaDataIndex& index) const
  {
    if(index.isValid()) {
      if(index.id()<DataCount) {
        // No array in standard names, identical to name()
        return standardName(static_cast<StandardData>(index.id()));
      } else {
        MetaData * d=create(index.id());
        if(d) {
          QString n=d->baseName(index.subId());
          delete d;
          return n;
        }
      }
    }
    return "Dummy";
  }

  QString MetaDataFactory::title(const MetaDataIndex& index) const
  {
    if(index.isValid()) {
      if(index.id()<DataCount) { // First custom meta data has id==DataCount
        switch(static_cast<StandardData>(index.id())) {
        case ID: return tr("ID");
        case StartTime: return tr("Start time");
        case SamplingPeriod: return tr("Sampling period");
        case NSamples: return tr("N samples");
        case ReceiverX: return tr("Rec x");
        case ReceiverY: return tr("Rec y");
        case ReceiverZ: return tr("Rec z");
        case UtmZone: return tr("UTM zone");
        case Name: return tr("Name");
        case Type: return tr("Type");
        case FileNumber: return tr("File number");
        case NumberInFile: return tr("Number in file");
        case FileName: return tr("File name");
        case FileFormat: return tr("File format");
        case ShortFileName: return tr("Short file name");
        case Duration: return tr("Duration");
        case EndTime: return tr("End time");
        case Component: return tr("Component");
        case MaximumAmplitude: return tr("Maximum amplitude");
        case AverageAmplitude: return tr("Average amplitude");
        case IsOriginalFile: return tr("Original file?");
        case SamplingFrequency: return tr("Sampling frequency");
        case Pointer: return tr("Pointer");
        case CountPerVolt: return tr("Count/Volt");
        case VoltPerCount: return tr("Volt/Count");
        case VoltPerUnit: return tr("Volt/Unit");
        case UnitPerVolt: return tr("Unit/Volt");
        case CountPerUnit: return tr("Count/Unit");
        case UnitPerCount: return tr("Unit/Count");
        case AmplitudeUnit: return tr("Amplitude unit");
        case SampleSize: return tr("Sample size (Mb)");
        case HeaderModified: return tr("Header modified?");
        case Dummy: return "Dummy";
        case DataCount: break;
        }
      } else {
        MetaData * d=create(index.id());
        if(d) {
          QString t=d->title(index.subId(), index.index());
          delete d;
          return t;
        }
      }
    }
    return tr("Untitled");
  }

  bool MetaDataFactory::isReadOnly(const MetaDataIndex& index) const
  {
    TRACE;
    if(index.id()<DataCount) {
      switch (index.id()) {
      case StartTime:
      case SamplingPeriod:
      case ReceiverX:
      case ReceiverY:
      case ReceiverZ:
      case UtmZone:
      case Name:
      case Component:
      case SamplingFrequency:
      case Duration:
      case EndTime:
      case CountPerVolt:
      case VoltPerCount:
      case VoltPerUnit:
      case UnitPerVolt:
      case AmplitudeUnit:
        return false;
      READONLY_STANDARD_METADATA
        return true;
      }
    } else {
      MetaData * d=create(index.id());
      if(d) {
        bool ro=d->isReadOnly(index.subId());
        delete d;
        return ro;
      }
    }
    return true;
  }

  bool MetaDataFactory::isStored(const MetaDataIndex& index) const
  {
    TRACE;
    if(index.id()<DataCount) {
      switch (index.id()) {
      case ID:
      case StartTime:
      case SamplingPeriod:
      case NSamples:
      case ReceiverX:
      case ReceiverY:
      case ReceiverZ:
      case UtmZone:
      case Name:
      case Type:
      case Component:
      case NumberInFile:
      case CountPerVolt:
      case VoltPerUnit:
      case AmplitudeUnit:
      case ShortFileName:
        return true;
      case FileNumber:
      case FileFormat:
      case FileName:
      case Duration:
      case EndTime:
      case MaximumAmplitude:
      case AverageAmplitude:
      case IsOriginalFile:
      case VoltPerCount:
      case UnitPerVolt:
      case UnitPerCount:
      case CountPerUnit:
      case SamplingFrequency:
      case Pointer:
      case SampleSize:
      case HeaderModified:
      case Dummy:
      case DataCount:
        return false;
      }
    } else {
      MetaData * d=create(index.id());
      if(d) {
        bool s=d->isStored();
        delete d;
        return s;
      }
    }
    return false;
  }

  bool MetaDataFactory::isArray(const MetaDataIndex& index) const
  {
    TRACE;
    if(index.id()<DataCount) {
      return false;
    } else {
      MetaData * d=create(index.id());
      if(d) {
        bool f=d->isArray(index.subId());
        delete d;
        return f;
      }
    }
    return false;
  }

  int MetaDataFactory::count(const MetaDataIndex& index) const
  {
    TRACE;
    if(index.id()<DataCount) {
      return 1;
    } else {
      MetaData * d=create(index.id());
      if(d) {
        int c=d->count(index.subId());
        delete d;
        return c;
      }
    }
    return 0;
  }

  QList<MetaDataIndex> MetaDataFactory::registeredData() const
  {
    QList<MetaDataIndex> list;
    QMap<QString, MetaDataIndex>::const_iterator it;
    for(it=_names.begin(); it!=_names.end(); it++) {
       list.append(it.value());
    }
    return list;
  }

  QStringList MetaDataFactory::registeredNames() const
  {
    QStringList list;
    QMap<QString, MetaDataIndex>::const_iterator it;
    for(it=_names.begin(); it!=_names.end(); it++) {
       list.append(it.key());
    }
    return list;
  }

  void MetaDataFactory::registerScriptTypes(QScriptEngine * engine) const
  {
    QList<int> list=registeredIds();
    for(QList<int>::iterator it=list.begin(); it!=list.end(); it++) {
      MetaData * d=create(*it);
      d->registerScriptTypes(engine);
      delete d;
    }
  }

  void MetaDataFactory::addScriptProperties(SignalHeaderObject * sig) const
  {
    QList<int> list=registeredIds();
    for(QList<int>::iterator it=list.begin(); it!=list.end(); it++) {
      MetaData * d=create(*it);
      d->addScriptProperties(sig);
      delete d;
    }
  }

} // namespace GeopsyCore
