//
//  QObject.h
//
//  Qdos2 - base object class
//
//  David Burgess
//  July 1999
//

#ifndef QDOS_QOBJECT_H
#define QDOS_QOBJECT_H
#define CACHE_XREF_CHAR '_'

#include <iostream>
#include <string>

#include "TypeInfo.h"
#include "Exception.h"
#include "Viostream.h"
#include "Naming.h"
#include "Sequence.h"
#include "StringRep.h"

namespace QDOS {

class QdObject;
typedef Var<QdObject> QdObject_var;
typedef NamingContext<QdObject_var> ObjectNamesTable;
typedef Var<ObjectNamesTable> ObjectNamesTable_var;
class QdObjectSeq;
typedef SeqVar<QdObjectSeq> QdObjectSeq_var;
class FunctionAdapter;
class StringRep;

#define NULL_OBJECT "null_object"
#define OBJECT_ID "object_id"
#define OBJECT "object"

Vostream& operator<<(Vostream&,QdObject_var);
Vistream& operator>>(Vistream&,QdObject_var);

//-----------------------------------------------------------------------------
// class QdObject
//-----------------------------------------------------------------------------

class QdObject : public VarTarget {

  static int qobject_class_counter;

  int object_id;
  ObjectNamesTable_var xrefs;

  protected:
  static TypeInfo_var _typeinfo;

  public:

  QdObject(void);
  virtual ~QdObject();

  int get_id(void) const { return object_id; }

  virtual QdObject_var copy() const=0;
  virtual QdObject_var create() const=0;
  virtual QdObject_var create_elt() const
  { throw Exception("[QObjectException][create_elt() Not Defoned]");}

  virtual void setValue(const double& d) {;}

  virtual void ping() const
    { cout << "QdObject::ping() (Object #"<<object_id<<")\n"; }

  virtual bool is_sequence(void) const { return false; }
  virtual int sequence_size(void) const { return 1; }
  virtual string object_srep( const StringRep& stringrep ) const
  { return "QdObject"; }

  virtual void put(Vostream& v_os)
  {;}
  virtual void get(Vistream& v_is)
  {;}

  virtual string srep(const StringRep& stringrep,int sequence_index=0) const
  { return "No String Representation Available!!!";}

  virtual string size_srep(const StringRep& stringrep, int sz=-1 ) const;
    
  // adapter methods
   virtual FunctionAdapter* join(QdObject_var x_in, QdObject_var y_in,\
       QdObject_var x_out, QdObject_var y_out, \
       QdObject_var join_method, QdObject_var gap_value, \
       QdObject_var gap_handler) const;
  
   virtual FunctionAdapter* multijoin(QdObjectSeq_var x_in, \
       QdObjectSeq_var y_in,QdObject_var x_out,QdObjectSeq_var y_out,\
       QdObjectSeq_var join_method,\
       QdObjectSeq_var gap_value, QdObjectSeq_var gap_handler,\
       int triage_cnt=-1) const; 
 
 // xref methods
  QdObject_var change_xref( const string& xref_name, QdObject_var xref_obj)
  { 
    Name xrefN(upper_case(xref_name));
	return change_xref(xrefN, xref_obj);
  }
  QdObject_var get_xref( const string& xref_name )
    { return xrefs->resolve( Name(upper_case(xref_name)) ); }
  void set_xref( string xref_name, QdObject_var xref_obj  )
    { Name uc(upper_case(xref_name));
	  xrefs->bind(uc, xref_obj ); }
  QdObject_var delete_xref( const string& xref_name )
    { return delete_xref( Name(upper_case(xref_name)) ); }
  bool xref_exists( const string& xref_name )
    { return xref_exists( Name(upper_case(xref_name)) ); }
  QdObject_var change_xref(const Name &xref_name, QdObject_var xref_obj)
  {  
	QdObject_var old_xref;
     if(xref_exists(xref_name))
         old_xref=delete_xref(xref_name);
     if( xref_obj.is_nil() ) {
	    return old_xref; //  don't set new xref as change_xref() used without try{}
		cout << "nil xref rejected by change_xref()" << endl;
	 }
     set_xref(xref_name, xref_obj);
     return old_xref;
  }  
  QdObject_var get_xref( Name xref_name )
    { return xrefs->resolve( xref_name ); }
  void set_xref( Name xref_name, QdObject_var xref_obj  )
    { xrefs->bind( xref_name, xref_obj ); }
  QdObject_var delete_xref( Name xref_name )
    { QdObject_var xref = xrefs->resolve( xref_name );
      xrefs->unbind( xref_name );
      return xref; } 
  bool xref_exists( Name xref_name )
    { QdObject_var xref;
      try{ xref = xrefs->resolve( xref_name ); }
      catch( NamingException ) { }
      return !xref.is_nil(); }
  QdObject_var rename_xref(const string& old_xref,const string& new_xref)
  { return rename_xref(Name(upper_case(old_xref)),Name(upper_case(new_xref)));}

  QdObject_var rename_xref(const Name& old_xref_name,const Name& new_xref_name)
  {
    QdObject_var xref_obj;
    try{xref_obj=delete_xref(old_xref_name);}
    catch(NamingException)
    {return xref_obj;}
    return change_xref(new_xref_name,xref_obj);
  }

  void list_xref_names( vector<string>& sl );
  void copy_xrefs_from(QdObject_var from_obj);
  void copy_xrefs_to(QdObject_var to_obj);

  static TypeInfo_var type_info()
  { return _typeinfo;}
  virtual TypeInfo_var vinfo()=0;
  static QdObject_var create(const string& name)
  { void* ptr=type_info()->create(name);
    return static_cast<QdObject*>(ptr);
  }
  virtual string object_type()
  { TypeInfo_var t=this->vinfo(); return t->getname();}
};

#define QDOBJECT \
  protected: \
    static TypeInfo_var _typeinfo; \
    static void* new_object(); \
  public: \
    static TypeInfo_var type_info(){return _typeinfo;} \
    virtual TypeInfo_var vinfo(){return _typeinfo;} 

#define DEF_QDOBJECT(T,N) \
  TypeInfo_var T::_typeinfo=new TypeInfo(N,QdObject::type_info(),T::new_object); \
  void* T::new_object() \
  { T* p=new T(); \
    return p; \
  } 

//-----------------------------------------------------------------------------
// class QdObjectSeq
//-----------------------------------------------------------------------------
class QdObjectSeq : public QdObject, public Sequence<QdObject_var>
{
  public:
  QdObjectSeq( int num=0 ) : Sequence<QdObject_var>(num) {;}
  QdObjectSeq( const vector<QdObject_var>& v ) : Sequence<QdObject_var>(v){;}

  virtual bool is_sequence(void) { return true; }
  int sequence_size(void) { return size(); }
  
  virtual QdObject_var create() const
  { return new QdObjectSeq();}
  
  virtual QdObject_var copy() const
  { return new QdObjectSeq(*this);}

  QDOBJECT

}; // end QdObjectSeq


//-----------------------------------------------------------------------------
// Exception Classes
//-----------------------------------------------------------------------------
class NObjectException: public Exception
{
public:
    NObjectException(ExceptionMsg m=""):\
        Exception(m<<"[NObjectException]"){;}
};
class BadNObject: public NObjectException
{
public:
    BadNObject(ExceptionMsg m=""):\
        NObjectException(m<<"[BadNObject]"){;}
};
class BadNArg: public NObjectException
{
public:
    BadNArg(ExceptionMsg m=""):\
        NObjectException(m<<"[BadNArg]"){;}
};
class BadNOp: public NObjectException
{
public:
    BadNOp(ExceptionMsg m=""):\
        NObjectException(m<<"[BadNOp]"){;}
};

class QdNObject;
typedef Var<QdNObject> QdNObject_var;
//-----------------------------------------------------------------------------
// class QdNObject
//-----------------------------------------------------------------------------
class QdNObject:public QdObject
{
public:
  ~QdNObject(){;}
  virtual QdNObject_var negate() const {throw BadNOp();}
  virtual QdNObject_var add(QdNObject_var) const {throw BadNOp();}
  virtual QdNObject_var sub(QdNObject_var) const {throw BadNOp();}
  virtual QdNObject_var mult(QdNObject_var) const {throw BadNOp();}
  virtual QdNObject_var div(QdNObject_var) const {throw BadNOp();}
  virtual QdNObject_var abs() const {throw BadNOp();}
  virtual QdNObject_var acos() const {throw BadNOp();}
  virtual QdNObject_var asin() const {throw BadNOp();}
  virtual QdNObject_var atan() const {throw BadNOp();}
  virtual QdNObject_var atan2(QdNObject_var) const {throw BadNOp();}
  virtual QdNObject_var cos() const {throw BadNOp();}
  virtual QdNObject_var cosh() const {throw BadNOp();}
  virtual QdNObject_var exp() const {throw BadNOp();}
  virtual QdNObject_var inner(QdNObject_var) const  {throw BadNOp();}
  virtual QdNObject_var invert() const {throw BadNOp();}
  virtual QdNObject_var log() const {throw BadNOp();}
  virtual QdNObject_var log10() const {throw BadNOp();}  
  virtual QdNObject_var mod() const {throw BadNOp();}
  virtual QdNObject_var outer(QdNObject_var) const {throw BadNOp();}
  virtual QdNObject_var pow(QdNObject_var obj) const {throw BadNOp();}
  virtual QdNObject_var sin() const {throw BadNOp();}  
  virtual QdNObject_var sinh() const {throw BadNOp();} 
  virtual QdNObject_var sqrt() const {throw BadNOp();}   
  
  virtual bool LT(QdNObject_var ) const {throw BadNOp();}
  virtual bool LE(QdNObject_var ) const {throw BadNOp();}
  virtual bool EQ(QdNObject_var ) const {throw BadNOp();}
  virtual bool GE(QdNObject_var ) const {throw BadNOp();}
  virtual bool GT(QdNObject_var ) const {throw BadNOp();}
};
//-----------------------------------------------------------------------------
// inline cover arithmetic operators/functions
//-----------------------------------------------------------------------------

inline QdObject_var operator-(QdObject_var obj)
{
     QdNObject_var nobj=QdNObject_var::narrow(obj);
     if (nobj.is_nil())
         throw BadNObject();
     return nobj->negate();
}

inline QdObject_var operator+(QdObject_var obj1, QdObject_var obj2)
{    
     QdNObject_var nobj1=QdNObject_var::narrow(obj1);
     if (nobj1.is_nil())
         throw BadNObject();
     QdNObject_var nobj2=QdNObject_var::narrow(obj2);
     if (nobj2.is_nil())
         throw BadNObject();     
     return nobj1->add(nobj2);
}

inline QdObject_var operator-(QdObject_var obj1, QdObject_var obj2)
{    
     QdNObject_var nobj1=QdNObject_var::narrow(obj1);
     if (nobj1.is_nil())
         throw BadNObject();
     QdNObject_var nobj2=QdNObject_var::narrow(obj2);
     if (nobj2.is_nil())
         throw BadNObject();     
     return nobj1->sub(nobj2);
}

inline QdObject_var operator*(QdObject_var obj1, QdObject_var obj2)
{    
     QdNObject_var nobj1=QdNObject_var::narrow(obj1);
     if (nobj1.is_nil())
         throw BadNObject();
     QdNObject_var nobj2=QdNObject_var::narrow(obj2);
     if (nobj2.is_nil())
         throw BadNObject();     
     return nobj1->mult(nobj2);
}

inline QdObject_var operator/(QdObject_var obj1, QdObject_var obj2)
{    
     QdNObject_var nobj1=QdNObject_var::narrow(obj1);
     if (nobj1.is_nil())
         throw BadNObject();
     QdNObject_var nobj2=QdNObject_var::narrow(obj2);
     if (nobj2.is_nil())
         throw BadNObject();     
     return nobj1->div(nobj2);
}
/*------Comparaison operators---------------------*/
inline bool operator<(QdObject_var obj1, QdObject_var obj2)
{
    QdNObject_var nobj1 = QdNObject_var::narrow(obj1);
    if(nobj1.is_nil())
        throw BadNObject();
    QdNObject_var nobj2 = QdNObject_var::narrow(obj2);
    if(nobj2.is_nil())
        throw BadNObject();    
    return nobj1->LT(nobj2);
}
inline bool operator==(QdObject_var obj1, QdObject_var obj2)
{
    QdNObject_var nobj1 = QdNObject_var::narrow(obj1);
    if(nobj1.is_nil())
        throw BadNObject();
    QdNObject_var nobj2 = QdNObject_var::narrow(obj2);
    if(nobj2.is_nil())
        throw BadNObject();    
    return nobj1->EQ(nobj2);
}
inline bool operator<=(QdObject_var obj1, QdObject_var obj2)
{
    QdNObject_var nobj1 = QdNObject_var::narrow(obj1);
    if(nobj1.is_nil())
        throw BadNObject();
    QdNObject_var nobj2 = QdNObject_var::narrow(obj2);
    if(nobj2.is_nil())
        throw BadNObject();    
    return nobj1->LE(nobj2);
}
inline bool operator>=(QdObject_var obj1, QdObject_var obj2)
{
    QdNObject_var nobj1 = QdNObject_var::narrow(obj1);
    if(nobj1.is_nil())
        throw BadNObject();
    QdNObject_var nobj2 = QdNObject_var::narrow(obj2);
    if(nobj2.is_nil())
        throw BadNObject();    
    return nobj1->GE(nobj2);
}
inline bool operator>(QdObject_var obj1, QdObject_var obj2)
{
    QdNObject_var nobj1 = QdNObject_var::narrow(obj1);
    if(nobj1.is_nil())
        throw BadNObject();
    QdNObject_var nobj2 = QdNObject_var::narrow(obj2);
    if(nobj2.is_nil())
        throw BadNObject();    
    return nobj1->GT(nobj2);
}
/*------End of Comparaison operators---------------*/

inline QdObject_var abs(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->abs();
}

inline QdObject_var acos(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->acos();
}

inline QdObject_var asin(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->asin();
}

inline QdObject_var atan(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->atan();
}

inline QdObject_var atan2(QdObject_var obj1,QdObject_var obj2)
{ 
     QdNObject_var nobj1=QdNObject_var::narrow(obj1);
     if (nobj1.is_nil())
         throw BadNObject();
     QdNObject_var nobj2=QdNObject_var::narrow(obj2);
     if (nobj2.is_nil())
         throw BadNObject();     
     return nobj1->atan2(nobj2);   
}

inline QdObject_var cos(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->cos();
}

inline QdObject_var cosh(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->cosh();
}

inline QdObject_var exp(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->exp();
}

inline QdObject_var inner(QdObject_var obj1,QdObject_var obj2)
{ 
    QdNObject_var nobj1=QdNObject_var::narrow(obj1);
    if (nobj1.is_nil())
        throw BadNObject();
     QdNObject_var nobj2=QdNObject_var::narrow(obj2);
     if (nobj2.is_nil())
         throw BadNObject();          
    return nobj1->inner(nobj2);
}

inline QdObject_var invert(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->invert();
}

void join(QdObject_var x_in, QdObject_var y_in,\
      QdObject_var x_out, QdObject_var y_out, \
      QdObject_var join_method, QdObject_var gap_value, \
      QdObject_var gap_handler);

inline QdObject_var log(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->log();
}

inline QdObject_var log10(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->log10();
}

inline QdObject_var mod(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->mod();
}

void multijoin(QdObjectSeq_var x_in,QdObjectSeq_var y_in,\
    QdObject_var x_out, QdObjectSeq_var y_out, \
    QdObjectSeq_var join_method, QdObjectSeq_var gap_value, \
    QdObjectSeq_var gap_handler, int triage_cnt=-1);

inline QdObject_var outer(QdObject_var obj1,QdObject_var obj2)
{ 
    QdNObject_var nobj1=QdNObject_var::narrow(obj1);
    if (nobj1.is_nil())
        throw BadNObject();
    QdNObject_var nobj2=QdNObject_var::narrow(obj2);
    if (nobj2.is_nil())
         throw BadNObject();
    return nobj1->outer(nobj2);
}

inline QdObject_var pow(QdObject_var obj1,QdObject_var obj2)
{ 
     QdNObject_var nobj1=QdNObject_var::narrow(obj1);
     if (nobj1.is_nil())
         throw BadNObject();
     QdNObject_var nobj2=QdNObject_var::narrow(obj2);
     if (nobj2.is_nil())
         throw BadNObject();     
     return nobj1->pow(nobj2);   
}

inline QdObject_var sin(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->sin();
}

inline QdObject_var sinh(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->sinh();
}

inline QdObject_var sqrt(QdObject_var obj)
{ 
    QdNObject_var nobj=QdNObject_var::narrow(obj);
    if (nobj.is_nil())
        throw BadNObject();
    return nobj->sqrt();
}





} // end namespace QDOS

#endif // #ifndef QDOS_QOBJECT_H

