/***************************************************************************
                                  enodes.h
                                 ----------      
    begin                : Feb 12 2004
    copyright            : (C) 2004 The University of Toronto
    email                :
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program 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 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef ENODES_H
#define ENODES_H

#include "kstvector.h"

class Plugin;

#define EQ_FALSE 0.0
#define EQ_TRUE  1.0

namespace Equation {

  class Context {
    public:
      Context() : i(0), x(0.0), xVector(0L), noPoint(0.0), sampleCount(0) {}
      ~Context() {}

      long i;
      double x;
      KstVectorPtr xVector;
      double noPoint;
      long sampleCount;
  };

  class NodeVisitor;
  
  class Node {
    public:
      Node();
      virtual ~Node();

      virtual bool isConst() = 0; // can't be const
      virtual void collectVectors(KstVectorList& c);
      virtual double value(Context*) = 0;
      virtual void visit(NodeVisitor*);
      virtual KstObject::UpdateType update(int counter, Context *ctx);
  };

  class BinaryNode : public Node {
    public:
      BinaryNode(Node *left, Node *right);
      virtual ~BinaryNode();

      virtual void collectVectors(KstVectorList& c);
      virtual void visit(NodeVisitor*);
      virtual KstObject::UpdateType update(int counter, Context *ctx);

      Node *& left();
      Node *& right(); 

    protected:
      Node *_left, *_right;
  };


  class ArgumentList : public Node {
    friend class Function;
    public:
      ArgumentList();
      virtual ~ArgumentList();

      void appendArgument(Node *arg);

      // Makes no sense for this type
      virtual double value(Context*) { return 0.0; }

      virtual bool isConst();
      virtual void collectVectors(KstVectorList& c);
      double at(int, Context*);
      virtual Node *node(int idx);
      virtual KstObject::UpdateType update(int counter, Context *ctx);

    protected:
      QPtrList<Node> _args;
  };


  // FIXME: consider an approach to reduce the weight of this node since most
  //        of the weight is due entirely to plugin calling.
  class Function : public Node {
    public:
      Function(char *name, ArgumentList *args);
      virtual ~Function();

      virtual bool isPlugin() const;
      virtual bool isConst();
      virtual double value(Context*);
      virtual void collectVectors(KstVectorList& c);
      virtual KstObject::UpdateType update(int counter, Context *ctx);

    protected:
      char *_name;
      ArgumentList *_args;
      void *_f;
      KstSharedPtr<Plugin> _plugin;

    private:
      double evaluatePlugin(Context *ctx);
      unsigned _inputScalarCnt, _inputVectorCnt, _inputStringCnt;
      unsigned _inPid, _outputScalarCnt, _outputVectorCnt, _outputStringCnt;
      int *_inArrayLens, *_outArrayLens;
      double *_inScalars, *_outScalars;
      double **_inVectors, **_outVectors;
      int _outputIndex; // -1 = scalar 0, -424242 = none/error, +0...n = vector
      void *_localData;
  };
 

  class Number : public Node {
    public:
      Number(double n);
      virtual ~Number();

      virtual bool isConst();
      virtual double value(Context*);

    protected:
      double _n;
  };


  class Identifier : public Node {
    public:
      Identifier(char *name);
      virtual ~Identifier();

      virtual bool isConst();
      virtual double value(Context*);
      virtual const char *name() const;

    protected:
      char *_name;
      double *_const;
  };


  class Data : public Node {
    friend class Function;
    public:
      Data(char *name);
      virtual ~Data();

      virtual bool isConst();
      virtual double value(Context*);
      virtual void collectVectors(KstVectorList& c);
      virtual KstObject::UpdateType update(int counter, Context *ctx);

    protected:
      char *_name;
      KstVectorPtr _vector;
      KstScalarPtr _scalar;
  };

  class NodeVisitor {
    public:
      virtual ~NodeVisitor();

      virtual void visitNode(Node*);
      virtual void visitBinaryNode(BinaryNode*);
    
    protected:
      NodeVisitor();  // cannot instantiate base class directly; need a sub-class that does something interesting
  };
  
    
  class FoldVisitor : public NodeVisitor {
    public:
      FoldVisitor(Context*, Node*);
      virtual ~FoldVisitor();
      
      virtual void visitNode(Node*);
      virtual void visitBinaryNode(BinaryNode*);
    
    protected:
      Context* _ctx;
  };


  class LogicalNot : public Node {
    public:
      LogicalNot(Node *node);
      virtual ~LogicalNot();
      virtual bool isConst();
      virtual double value(Context*);

    protected:
      Node *_n;
  };

#define CreateNode(x)                     \
  class x : public BinaryNode {           \
    public:                               \
      x(Node *left, Node *right);         \
      virtual ~x();                       \
      virtual bool isConst();             \
      virtual double value(Context*);     \
  };

CreateNode(Addition)
CreateNode(Subtraction)
CreateNode(Multiplication)
CreateNode(Division)
CreateNode(Modulo)
CreateNode(Power)
CreateNode(BitwiseAnd)
CreateNode(BitwiseOr)
CreateNode(LogicalAnd)
CreateNode(LogicalOr)
CreateNode(LessThan)
CreateNode(LessThanEqual)
CreateNode(GreaterThan)
CreateNode(GreaterThanEqual)
CreateNode(EqualTo)
CreateNode(NotEqualTo)
#undef CreateNode

}

#endif

// vim: ts=2 sw=2 et
