/***************************************************************************
**
**  This file is part of QGpCoreTools.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2007-04-20
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <math.h>

#include "ExpressionContext.h"
#include "ExpressionStorage.h"
#include "ExpressionAction.h"
#include "Trace.h"
#include "MemoryChecker.h"

namespace QGpCoreTools {

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

  Full description of class still missing
*/

/*!
  Description of destructor still missing
*/
ExpressionContext::~ExpressionContext()
{
  TRACE;
  _varNames.clear();
  qDeleteAll(_varValues);
  _varValues.clear();
}

bool ExpressionContext::isValidVariable(const QString& name) const
{
  TRACE;
  bool c=_varNames.contains(name);
  if(!c && _parent) return _parent->isValidVariable(name);
  else return c;
}

QVariant ExpressionContext::variableValue(const QString& name, const QString& index) const
{
  TRACE;
  QMap<QString, int>::const_iterator it;
  it=_varNames.find (name);
  if(it!=_varNames.end()) {
    return _varValues[it.value()]->value(index);
  } else if(_parent) {
    return _parent->variableValue(name, index);
  } else {
    return QVariant();
  }
}

void ExpressionContext::addVariable(const QString& name, const QString& index, const QVariant& val)
{
  TRACE;
  QMap<QString, int>::iterator it;
  it=_varNames.find (name);
  if(it!=_varNames.end()) {
    variable(it.value())->setValue(index, val);
  } else {
    int varIndex=_varValues.count();
    _varNames.insert(name, varIndex);
    _varValues.append(new ExpressionVariableStorage);
    _varValues[varIndex]->setValue(index, val);
  }
}

/*!
  Add a new variable if variable named \a name does not exist. In all cases, it returns the index of the variable
*/
int ExpressionContext::addVariable(const QString& name, ExpressionStorage * var)
{
  TRACE;
  int index;
  QMap<QString, int>::iterator it;
  it=_varNames.find (name);
  if(it==_varNames.end()) {
    index=_varValues.count();
    _varNames.insert(name, index);
    _varValues.append(var);
  } else {
    index=it.value();
    delete var;
  }
  return index;
}

QStringList ExpressionContext::variables() const
{
  QStringList v;
  for(QMap<QString, int>::const_iterator it=_varNames.begin();it!=_varNames.end();it++) {
    v.append(it.key());
  }
  return v;
}
QStringList ExpressionContext::functions() const
{
  TRACE;
  QStringList l;
  l << "if( <condition>, <true>, <false> )"
    << "justify( <string>, <nChars>, <fillChar> )"
    << "left( <string>, <nChars> )"
    << "length( <string> )"
    << "mid( <string>, <pos>, <length> )"
    << "right( <string>, <nChars> )"
    << "sqrt( <value> )"
    << "pow( <base>, <exp> )"
    << "log10( <value> )"
    << "cos( <value> )"
    << "sin( <value> )"
    << "tan( <value> )";
  return l;
}

bool ExpressionContext::isValidFunction(const QString& name) const
{
  TRACE;
  switch(name[0].unicode()) {
  case 'c':
    if(name=="cos") return true;
    break;
  case 'i':
    if(name=="if") return true;
    break;
  case 'j':
    if(name=="justify") return true;
    break;
  case 'l':
    if(name=="left") return true;
    else if(name=="length") return true;
    else if(name=="log10") return true;
    break;
  case 'm':
    if(name=="mid") return true;
    break;
  case 'p':
    if(name=="pow") return true;
    break;
  case 'r':
    if(name=="right") return true;
    break;
  case 's':
    if(name=="sqrt") return true;
    else if(name=="sin") return true;
    break;
  case 't':
    if(name=="tan") return true;
    break;
  default:
    break;
  }
  return false;
}

QVariant ExpressionContext::functionValue(const QString& name, const QVector<ExpressionAction *>& args) const
{
  TRACE;
  switch(name[0].unicode()) {
  case 'c':
    if(name=="cos") {
      if(args.count()==1) {
        return ::cos(args.at(0)->value().toDouble());
      }
    }
    break;
  case 'i':
    if(name=="if") {
      if(args.count()==3) {
        if(args.at(0)->value().toBool()) {
          return args.at(1)->value();
        } else {
          return args.at(2)->value();
        }
      } else if(args.count()==2) {
        if(args.at(0)->value().toBool()) {
          return args.at(1)->value();
        }
      }
    }
    break;
  case 'j':
    if(name=="justify") {
      if(args.count()==3) {
        QString s=args.at(2)->value().toString();
        QChar c;
        if(s.isEmpty()) c=' '; else c=s[0];
        return args.at(0)->value().toString().rightJustified(args.at(1)->value().toInt(),c);
      }
    }
    break;
  case 'l':
    if(name=="left") {
      if(args.count()==2) {
        return args.at(0)->value().toString().left(args.at(1)->value().toInt());
      }
    } else if(name=="length") {
      if(args.count()==1) {
        return args.at(0)->value().toString().length();
      }
    } else if(name=="log10") {
      if(args.count()==1) {
        return log10(args.at(0)->value().toDouble());
      }
    }
    break;
  case 'm':
    if(name=="mid") {
      if(args.count()==3) {
        return args.at(0)->value().toString().mid(args.at(1)->value().toInt(),
                                                  args.at(2)->value().toInt());
      }
    }
    break;
  case 'p':
    if(name=="pow") {
      if(args.count()==2) {
        return pow(args.at(0)->value().toDouble(),
                    args.at(1)->value().toDouble());
      }
    }
    break;
  case 'r':
    if(name=="right") {
      if(args.count()==2) {
        return args.at(0)->value().toString().right(args.at(1)->value().toInt());
      }
    }
    break;
  case 's':
    if(name=="sqrt") {
      if(args.count()==1) {
        return ::sqrt(args.at(0)->value().toDouble());
      }
    }
    else if(name=="sin") {
      if(args.count()==1) {
        return ::sin(args.at(0)->value().toDouble());
      }
    }
    break;
  case 't':
    if(name=="tan") {
      if(args.count()==1) {
        return ::tan(args.at(0)->value().toDouble());
      }
    }
    break;
  default:
    break;
  }
  return QVariant();
}

} // namespace QGpCoreTools
