#ifndef QD_OBJECT_OPERATORS
#define QD_OBJECT_OPERATORS

#include <string>
#include <vector>
#include <map>
#include <qstring.h>
#include <stack>

#include "qt_version.h"
#include "qar.h"

// Arithmetic operators
#define  strOpAdd         "A + B"
#define  strOpSub         "A - B"
#define  strOpMult        "A * B"
#define  strOpDiv         "A / B"
#define  strOpMod         "mod ( A )"
#define  strOpPower       "A ^ B"
#define  strOpChSign      "- A"

// Trig operators
#define  strOpCos           "cos ( A )"
#define  strOpSin           "sin ( A )"
#define  strOpTan           "tan ( A )"
#define  strOpCot           "cot ( A )"
#define  strOpCosh          "cosh ( A )"
#define  strOpSinh          "sinh ( A )"
#define  strOptanh          "tanh ( A )"
#define  strOpCoth          "coth ( A )"
#define  strOpACos          "acos ( A )"
#define  strOpASin          "asin ( A )"
#define  strOpATan          "atan ( A )"
#define  strOpATan2         "atan2 ( A, B )"
#define  strOpACot          "acot ( A )"
#define  strOpACosh         "acosh ( A )"
#define  strOpASinh         "asinh ( A )"
#define  strOpAtanh         "atanh ( A )"
#define  strOpACoth         "acoth ( A )"

// Vector Operators
#define  strOpVecProd          "A x B"
#define  strOpDotProd          "A . B"

// Filter Operators
#define  strOpRemainder      "A % B"
#define  strOPAbsValue       "|A|"
#define  strOpLog            "ln ( A )"
#define  strOpLog10          "log ( A )"
#define  strOpInvers         "1 / A"
#define  strOpSqrt           "sqrt ( A )"
#define  strOpExp            "exp ( A )"
#define  strOpJoin           "join only"
#define  strOpUnits          "Convert Units (obj= A, SIconv= B )"
#define  strOpFills          "Convert Fills (obj= A, fill= B )"

// Operation description
#define  strAddDescription            "Addition"
#define  strSubDescription            "Substraction"
#define  strMultDescription           "Multiplication"
#define  strDivDescription            "Division by scalar"
#define  strModDescription            "Modulus = Abs for scalar, Magnitude of vector and Norm of matrix"
#define  strPowDescription            "Raise A to Power B"
#define  strChSignDescription         "Change Sign of each element"
#define  strCosDescription            "Cosine"
#define  strSinDescription            "Sine"
#define  strTanDescription            "Tangent"
#define  strCotDescription            "Cotangent"
#define  strCoshDescription           "Hyperbolic Cosine"
#define  strSinhDescription           "Hyperbolic Sine"
#define  strtanhDescription           "Hyperbolic Tangent"
#define  strCothDescription           "Hyperbolic Cotangent"
#define  strACosDescription           "arc cosine"
#define  strASinDescription           "arc sine"
#define  strATanDescription           "arc tangent"
#define  strATan2Description          "arc tangent2 = atan (A/B) sign taken from A & B to get quadrant"
#define  strACotDescription           "arc cotangent"
#define  strACoshDescription          "Inverse hyperbolic cosine"
#define  strASinhDescription          "Inverse hyperbolic sine"
#define  strAtanhDescription          "Inverse hyperbolic tangent"
#define  strACothDescription          "Inverse hyperbolic cotangent"
#define  strVecProdDescription         "Vector Product of two 3-vectors"
#define  strDotProdDescription         "Scalar Product of two 3-vectors"
#define  strMakeVectDescription        "Make Vector"
#define  strComponentDescription       "Vector Time Series Decomposition into scalar Time series"
#define  strRemainderDescription       "Remainder = A modulo B"
#define  strAbsValueDescription        "Absolute Value of scalar"
#define  strLog10Description           "Base 10 Logarithm"
#define  strLogDescription             "Natural Logarithm"
#define  strInversDescription          "Inverse of scalar"
#define  strSqrtDescription            "square root"
#define  strExpDescription             "Value of e (base of natural logarithms) raised to the power of A"
#define  strUnitsDescription           "Convert data to units of second object"
#define  strFillsDescription           "Change fill values to value provided (Fillval xref also gets changed)"


const double PI_            = 3.14159265358979323846;
static const char*  pRadYes        = "rad";
static const char*  pDegYes        = "deg";

// Extern function fro displaying log/error messages
extern void QcalcAppendTextDisplay ( const char* );

enum    UniteEnum { DEG_ID = 0, RAD_ID = 1, DEFAULT_ID = 2 };
//#include <qmutex.h>
typedef QdObject_var ( *OperatorFnPtr )( QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2 );
typedef QdObject_var ( *QdObjectFnPtr )( QdObject_var qdOperand1, QString* pstrErrorMsg);

QdObject_var QdAdd(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdChSign(  QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdDiv(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdDotProd( QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdInvers(  QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdLog(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdLog10(   QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdMod(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdMult(    QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdPower(   QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdSub(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdVecProd( QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdCos(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdSin(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdTan(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdCot(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdCosh(    QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdSinh(    QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var Qdtanh(    QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdCoth(    QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdaCos(    QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdaSin(    QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdaTan(    QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdaTan2(   QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdaCot(    QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdaCosh(   QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdaSinh(   QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var Qdatanh(   QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdaCoth(   QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2  = NULL );
QdObject_var QdAbsValue( QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2 = NULL );
QdObject_var QdRemainder( QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2 = NULL );
QdObject_var QdComponent( QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2 = NULL );
QdObject_var QdSqrt(      QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2 = NULL );
QdObject_var QdExp(       QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2 = NULL );
QdObject_var QdUnits(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2 = NULL );
QdObject_var QdFills(     QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2 = NULL );



QdObject_var QdTrigFunction ( QdObjectFnPtr pFn,
                              QdObject_var  qdOperand1,
							  QdObject_var  qdOperand2,
							  QString&      rqstrErrorMsg,
							  QdString_var  funct,
							  bool          bInverse,
							  bool          bHyperbolic = false );

QdObject_var QdcosLocal(      QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdsinLocal(      QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdcoshLocal(     QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdsinhLocal(     QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdTangent(       QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdCTangent(      QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdTangentH(      QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdCTangentH(     QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdAcosLocal(     QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdAsinLocal(     QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdAtanLocal(     QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdAcotLocal(     QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdAcoshLocal(    QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdAsinhLocal(    QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdAtanhLocal(    QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
QdObject_var QdAcothLocal(    QdObject_var  qdOperand1, QString* pstrErrorMsg = NULL );
char*        str_dup(         string& s,    int n );
char* QpAxisLabelMakeStem ( QdObject_var data_obj );


//----------------------Class Operator-----------------------//
class QdOperator
{
    private:
       string       m_qdStrOpName;     // Name of the operator
	   int           m_nOpPrecedence;   // Operator precedence
	   int           m_nNumOperands;    // Number of operands required per opertor
	   OperatorFnPtr m_pOpRole;         // The low level function that computes the operator role
	   bool          m_bTrig;           // Flag set to signify that the function is trigonometric
	   string        m_strDescription;  // Description of the operation


    public:
           QdOperator()
	    : m_qdStrOpName  ( ""   ),
	      m_nOpPrecedence ( 0    ),
	      m_nNumOperands  ( 0    ),
	      m_pOpRole       ( NULL ),
	      m_bTrig         ( false ),
	      m_strDescription ( "" )
	   { ; }

           QdOperator(  string        qdStrOpName,
			int           nOpPrecedence,
			int           nNumOperands,
			OperatorFnPtr pOpRole,
			string        strDescription,
			bool          bTrig = false )
	   : m_qdStrOpName    ( qdStrOpName    ),
	     m_nOpPrecedence  ( nOpPrecedence  ),
	     m_nNumOperands   ( nNumOperands   ),
	     m_pOpRole        ( pOpRole        ),
	     m_strDescription ( strDescription ),
	     m_bTrig          ( bTrig          )
	     {;}

	   ~QdOperator()
	   { ; }

		QdOperator & operator = ( const QdOperator& qdOp )
		{
			m_qdStrOpName     = string ( qdOp.m_qdStrOpName.c_str() );
			m_nOpPrecedence   = qdOp.m_nOpPrecedence;
			m_nNumOperands    = qdOp.m_nNumOperands;
			m_pOpRole         = qdOp.m_pOpRole;
			m_bTrig           = qdOp.m_bTrig;
			m_strDescription  = string( qdOp.m_strDescription.c_str() );

			return ( *this );
		}


	   // Accessors

	   // Operator Role
	   OperatorFnPtr GetOpRole() const
	   {
	        return ( m_pOpRole );
	   }

	   void SetOpRole( OperatorFnPtr pOpRole )
	   {
	          m_pOpRole = pOpRole;
	   }


	   // Operator Name
	   string GetOperatorName() const
	   {
	       return ( m_qdStrOpName );
	   }

	   void SetOperatorName( string qdStrOpName )
	   {
	       m_qdStrOpName = qdStrOpName;
	   }

	   
	   // Operator precedence
	   int GetOpPrecedence() const
	   {
	       return ( m_nOpPrecedence );
	   }

	   void SetOpPrecedence( int nOpPrecedence )
	   {
	       m_nOpPrecedence = nOpPrecedence;
	   }

	 bool GetTrigFlag() const
	 {
	   return  ( m_bTrig );
	 }

	 void SetTrigFlag( bool bTrig )
	 {
	    m_bTrig = bTrig;
	 }


	   // Number of operands required per operator
	   int GetNumOperands() const
	   {
	       return ( m_nNumOperands );
	   }

	   void SetNumOperands( int nNumOperands )
	   {
	       m_nNumOperands = nNumOperands;
	   }


	   string GetDescription() const
	   {
	   		return ( m_strDescription );
	   }

	   void setDescription(string strDescription )
	   {
	   		m_strDescription = strDescription;
	   }

};


// Vector Functions

inline QdObject_var QdMakeVect( QString& rqstrErrorMsg, QdObject_var qdOperand1, QdObject_var qdOperand2 = NULL )
{

    QdObject_var  qdResult;

    if ( qdOperand1.is_nil() )
    {
        // The object will be empty - this should never happen
	// The qdResult object is NULL at this point
        return ( qdResult );
    }

    // TODO calculation

    return ( qdResult );
}



class ltstr
{
  public:

  bool operator()(const char* qs1, const char* qs2) const
  {
    //return ( QString::compare( (const QString)qs1, (const QString)qs2 ) < 0 );
	return ( strcmp( qs1, qs2 ) < 0 );
  }
};


typedef std::map< string, QdOperator > MapOperators;

class MapCalculator : public MapOperators
{
   private:
      //QMutex mutex;

   public:
    void LoadMap( string strKey, QdOperator  qOpDetails )
	{
             //this->insert( pair< QString, QdOperator > ( strKey, qOpDetails ) );
			 ( *this )[ strKey ] = qOpDetails;
	}

	bool FindOperator( QString qOdSignature, QdOperator&  rqOpDetails )
	{
	    bool bFound = false;
	    map< string, QdOperator>::iterator itr;
		string  strKey = string( qOdSignature.latin1() );

		if ( ( qOdSignature.isEmpty() ) || ( qOdSignature.isNull() ) )
		{
			return ( bFound );
		}


		itr = this->find( strKey );
	    if ( itr != this->end() )
	    {
	        bFound      = true;
            rqOpDetails = itr->second;
	    }

	    return ( bFound );
	}

	void LoadAll()
	{
		LoadMap( strOpAdd ,        QdOperator( strOpAdd,       1, 2, QdAdd,     strAddDescription       ) );
		LoadMap( strOpSub ,        QdOperator( strOpSub,       1, 2, QdSub,     strSubDescription         ) );
		LoadMap( strOpMult ,       QdOperator( strOpMult,      1, 2, QdMult,    strMultDescription        ) );
		LoadMap( strOpDiv ,        QdOperator( strOpDiv,       1, 2, QdDiv,     strDivDescription         ) );
		LoadMap( strOpMod ,        QdOperator( strOpMod,       1, 1, QdMod,     strModDescription         ) );
		LoadMap( strOpPower ,      QdOperator( strOpPower,     1, 2, QdPower,   strPowDescription       ) );
		LoadMap( strOpChSign ,     QdOperator( strOpChSign,    1, 1, QdChSign,  strChSignDescription      ) );

		LoadMap(    strOpCos ,       QdOperator( strOpCos,       1, 1, QdCos,   strCosDescription, true   ) );
		LoadMap(    strOpSin ,       QdOperator( strOpSin,       1, 1, QdSin,   strSinDescription, true   ) );
		LoadMap(    strOpTan ,       QdOperator( strOpTan,       1, 1, QdTan,   strTanDescription,true   ) );
		LoadMap(    strOpCot ,       QdOperator( strOpCot,       1, 1, QdCot,   strCotDescription,true   ) );
		LoadMap(    strOpCosh ,      QdOperator( strOpCosh,      1, 1, QdCosh,  strCoshDescription,true  ) );
		LoadMap(    strOpSinh ,      QdOperator( strOpSinh,      1, 1, QdSinh,  strSinhDescription,true  ) );
		LoadMap(    strOptanh ,      QdOperator( strOptanh,      1, 1, Qdtanh,  strtanhDescription,true  ) );
		LoadMap(    strOpCoth ,      QdOperator( strOpCoth,      1, 1, QdCoth,  strCothDescription,true  ) );

		LoadMap(    strOpACos ,       QdOperator( strOpACos,      1, 1, QdaCos,  strACosDescription,true  ) );
		LoadMap(    strOpASin ,       QdOperator( strOpASin,      1, 1, QdaSin,  strASinDescription,true  ) );
		LoadMap(    strOpATan ,       QdOperator( strOpATan,      1, 1, QdaTan,  strATanDescription,true  ) );
		LoadMap(    strOpATan2 ,      QdOperator( strOpATan2,     1, 2, QdaTan2, strATan2Description,false ) ); // don't handle as trig
		LoadMap(    strOpACot ,       QdOperator( strOpACot,      1, 1, QdaCot,  strACotDescription,true  ) );
		LoadMap(    strOpACosh ,      QdOperator( strOpACosh,     1, 1, QdaCosh, strACoshDescription,true ) );
		LoadMap(    strOpASinh ,      QdOperator( strOpASinh,     1, 1, QdaSinh, strASinhDescription,true ) );
		LoadMap(    strOpAtanh ,      QdOperator( strOpAtanh,     1, 1, Qdatanh, strAtanhDescription,true ) );
		LoadMap(    strOpACoth ,      QdOperator( strOpACoth,     1, 1, QdaCoth, strACothDescription,true ) );

		LoadMap(    strOpVecProd ,   QdOperator( strOpVecProd,   1, 2, QdVecProd,    strVecProdDescription      ) );
		LoadMap(    strOpDotProd ,   QdOperator( strOpDotProd,   1, 2, QdDotProd,    strDotProdDescription      ) );

		LoadMap(    strOpRemainder , QdOperator( strOpRemainder, 1, 2, QdRemainder,  strRemainderDescription    ) );
		LoadMap(    strOPAbsValue ,  QdOperator( strOPAbsValue,  1, 1, QdAbsValue,   strAbsValueDescription     ) );
		LoadMap(    strOpLog ,       QdOperator( strOpLog,       1, 1, QdLog,        strLogDescription          ) );
		LoadMap(    strOpLog10 ,     QdOperator( strOpLog10,     1, 1, QdLog10,      strLog10Description        ) );
		LoadMap(    strOpInvers ,    QdOperator( strOpInvers,    1, 1, QdInvers,     strInversDescription       ) );
		LoadMap(    strOpSqrt ,      QdOperator( strOpSqrt,      1, 1, QdSqrt,       strSqrtDescription         ) );
		LoadMap(    strOpExp ,       QdOperator( strOpExp,       1, 1,  QdExp,       strExpDescription          ) );
		LoadMap(    strOpUnits ,     QdOperator( strOpUnits,     1, 2,  QdUnits,     strUnitsDescription        ) );
		LoadMap(    strOpFills ,     QdOperator( strOpFills,     1, 2,  QdFills,     strFillsDescription        ) );

	}
};



#endif  // QD_OBJECT_OPERATORS

