// ---------------------------------------------------------------------------
// - Integer.cpp                                                             -
// - standard object library - integer class implementation                  -
// ---------------------------------------------------------------------------
// - This program is free software;  you can redistribute it  and/or  modify -
// - it provided that this copyright notice is kept intact.                  -
// -                                                                         -
// - This program  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.  In no event shall -
// - the copyright holder be liable for any  direct, indirect, incidental or -
// - special damages arising in any way out of the use of this software.     -
// ---------------------------------------------------------------------------
// - copyright (c) 1999-2008 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Real.hpp"
#include "Input.hpp"
#include "Stdsid.hxx"
#include "Output.hpp"
#include "Vector.hpp"
#include "Recycle.hpp"
#include "Utility.hpp"
#include "Boolean.hpp"
#include "Runnable.hpp"
#include "QuarkZone.hpp"
#include "Character.hpp"
#include "Exception.hpp"
#include "ccnv.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - memory section                                                        -
  // -------------------------------------------------------------------------

  // the integer recycler
  static Recycle recycler;

  // allocate a new integer
  void* Integer::operator new (const t_size size) {
    return recycler.pop (size);
  }

  // delete an integer
  void Integer::operator delete (void* handle) {
    recycler.push (handle);
  }

  // -------------------------------------------------------------------------
  // - class section                                                         -
  // -------------------------------------------------------------------------

  // create a new default integer
  
  Integer::Integer (void) {
    d_value = 0;
  }

  // create a new integer from a long integer

  Integer::Integer (const t_long value) {
    d_value = value;
  }

  // create a new integer from a string

  Integer::Integer (const String& value) {
    d_value = Utility::tointeger (value);
  }
  
  // copy constructor for this integer

  Integer::Integer (const Integer& that) {
    that.rdlock ();
    d_value = that.d_value;
    that.unlock ();
  }

  // return the class name

  String Integer::repr (void) const {
    return "Integer";
  }

  // return a literal representation of this integer

  String Integer::toliteral (void) const {
    return tostring ();
  }

  // get a string representation on this integer

  String Integer::tostring (void) const {
    rdlock ();
    String result = Utility::tostring (d_value);
    unlock ();
    return result;
  }

  // return a clone of this integer

  Object* Integer::clone (void) const {
    return new Integer (*this);
  }

  // return the integer serial code

  t_byte Integer::serialid (void) const {
    return SERIAL_INTG_ID;
  }

  // serialize this integer

  void Integer::wrstream (Output& os) const {
    rdlock ();
    t_byte data[8];
    c_ohton (d_value, data);
    os.write ((char*) data, 8);
    unlock ();
  }

  // deserialize this integer

  void Integer::rdstream (Input& is) {
    wrlock ();
    t_byte data[8];
    for (long i = 0; i < 8; i++) data[i] = (t_byte) is.read ();
    d_value = c_ontoh (data);
    unlock ();
  }
  
  // set an integer with a value

  Integer& Integer::operator = (const t_long value) {
    wrlock ();
    d_value = value;
    unlock ();
    return *this;
  }

  // set an integer with a value

  Integer& Integer::operator = (const Integer& value) {
    wrlock ();
    value.rdlock ();
    d_value = value.d_value;
    value.unlock ();
    unlock ();
    return *this;
  }

  // compute the opposite of the current integer
  
  Integer operator - (const Integer& x) {
    x.rdlock ();
    Integer result = -x.d_value;
    x.unlock ();
    return result;
  }
  
  // add two integers together from two integers

  Integer operator + (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    Integer result = x.d_value + y.d_value;
    y.unlock ();
    x.unlock ();
    return result;
  }

  // substract two integers together from two integers
  
  Integer operator - (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    Integer result = x.d_value - y.d_value;
    y.unlock ();
    x.unlock ();
    return result;
  }
  
  // multiply two integers together from two integers
  
  Integer operator * (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    Integer result = x.d_value * y.d_value;
    y.unlock ();
    x.unlock ();
    return result;
  }
    
  // divide two integers together from two integers
  
  Integer operator / (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    if (y.d_value == 0) {
      y.unlock ();
      x.unlock ();
      throw Exception ("integer-error","division by zero");
    }
    Integer result = x.d_value / y.d_value;
    y.unlock ();
    x.unlock ();
    return result;
  }

  // return the remainder of this number with its argument

  Integer operator % (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    if (y.d_value == 0) {
      y.unlock ();
      x.unlock ();
      throw Exception ("integer-error","division by zero");
    }
    Integer result = x.d_value % y.d_value;
    y.unlock ();
    x.unlock ();
    return result;
  }

  // bitwise negate an integer
  Integer operator ~ (const Integer& x) {
    x.rdlock ();
    Integer result = ~x.d_value;
    x.unlock ();
    return result;
  }

  // bitwise and an integer with another one

  Integer operator & (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    Integer result = x.d_value & y.d_value;
    y.unlock ();
    x.unlock ();
    return result;
  }

  // bitwise or an integer with another one

  Integer operator | (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    Integer result = x.d_value | y.d_value;
    y.unlock ();
    x.unlock ();
    return result;
  }

  // bitwise xor an integer with another one

  Integer operator ^ (const Integer& x, const Integer& y) {
    x.rdlock ();
    y.rdlock ();
    Integer result = x.d_value ^ y.d_value;
    y.unlock ();
    x.unlock ();
    return result;
  }

  // add an integer to this one

  Integer& Integer::operator += (const Integer& x) {
    wrlock   ();
    x.rdlock ();
    d_value += x.d_value;
    x.unlock ();
    unlock   ();
    return *this;
  }

  // substract an integer to this one
  
  Integer& Integer::operator -= (const Integer& x) {
    wrlock   ();
    x.rdlock ();
    d_value -= x.d_value;
    x.unlock ();
    unlock   ();
    return *this;
  }

  // multiply an integer with this one
  
  Integer& Integer::operator *= (const Integer& x) {
    wrlock   ();
    x.rdlock ();
    d_value *= x.d_value;
    x.unlock ();
    unlock   ();
    return *this;
  }

  // divide an integer with this one
  
  Integer& Integer::operator /= (const Integer& x) {
    wrlock   ();
    x.rdlock ();
    if (x.d_value == 0) {
      x.unlock ();
      unlock   ();
      throw Exception ("integer-error","division by zero");
    }
    d_value /= x.d_value;
    x.unlock ();
    unlock   ();
    return *this;
  }

  // prefix add one to the integer

  Integer& Integer::operator ++ (void) {
    wrlock ();
    ++d_value;
    unlock ();
    return *this;
  }

  // postfix add one to the integer

  Integer Integer::operator ++ (int) {
    wrlock ();
    Integer result = *this;
    d_value++;
    unlock ();
    return result;
  }

  // prefix sub one to the integer

  Integer& Integer::operator -- (void) {
    wrlock ();
    --d_value;
    unlock ();
    return *this;
  }

  // postfix sub one to the integer

  Integer Integer::operator -- (int) {
    wrlock ();
    Integer result = *this;
    d_value--;
    unlock ();
    return result;
  }

  // compare an integer with a native value

  bool Integer::operator == (const t_long value) const {
    rdlock ();
    bool result = (d_value == value);
    unlock ();
    return result;
  }

  bool Integer::operator != (const t_long value) const {
    rdlock ();
    bool result = (d_value != value);
    unlock ();
    return result;
  }

  // compare two integers

  bool Integer::operator == (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    bool result = (d_value == value.d_value);
    value.unlock ();
    unlock ();
    return result;
  }

  // compare two integers

  bool Integer::operator != (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    bool result = (d_value != value.d_value);
    value.unlock ();
    unlock ();
    return result;
  }

  // compare two integers

  bool Integer::operator < (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    bool result = (d_value <  value.d_value);
    value.unlock ();
    unlock ();
    return result;
  }

  // compare two integers

  bool Integer::operator <= (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    bool result = (d_value <= value.d_value);
    value.unlock ();
    unlock ();
    return result;
  }
  
  // compare two integers

  bool Integer::operator > (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    bool result = (d_value > value.d_value);
    value.unlock ();
    unlock ();
    return result;
  }

  // compare two integers

  bool Integer::operator >= (const Integer& value) const {
    rdlock ();
    value.rdlock ();
    bool result = (d_value >= value.d_value);
    value.unlock ();
    unlock ();
    return result;
  }

  // return true if the integer is zero

  bool Integer::iszero (void) const {
    rdlock ();
    bool result = (d_value == 0);
    unlock ();
    return result;
  }

  // return true if the integer is even

  bool Integer::iseven (void) const {
    rdlock ();
    bool result = ((d_value & 1) == 0);
    unlock ();
    return result;
  }

  // return true if the integer is odd

  bool Integer::isodd (void) const {
    rdlock ();
    bool result = ((d_value & 1) == 1);
    unlock ();
    return result;
  }

  // shift left this integer

  Integer Integer::shl (const long pos) const {
    rdlock ();
    Integer result = d_value << (pos % 64);
    unlock ();
    return result;
  }

  // shift right this integer

  Integer Integer::shr (const long pos) const {
    rdlock ();
    Integer result = d_value >> (pos % 64);
    unlock ();
    return result;
  }

  // return the absolute value of this number

  Integer Integer::abs (void) const {
    rdlock ();
    Integer result = (d_value < 0) ? -d_value : d_value;
    unlock ();
    return result;
  }

  // return a native integer value

  t_long Integer::tointeger (void) const {
    rdlock ();
    t_long result = d_value;
    unlock ();
    return result;
  }

  // -------------------------------------------------------------------------
  // - object section                                                        -
  // -------------------------------------------------------------------------

  // the quark zone
  static const long QUARK_ZONE_LENGTH = 27;
  static QuarkZone  zone (QUARK_ZONE_LENGTH);

  // the object supported quarks
  static const long QUARK_OR    = zone.intern ("or");
  static const long QUARK_OPP   = zone.intern ("++");
  static const long QUARK_OMM   = zone.intern ("--");
  static const long QUARK_ADD   = zone.intern ("+");
  static const long QUARK_SUB   = zone.intern ("-");
  static const long QUARK_MUL   = zone.intern ("*");
  static const long QUARK_DIV   = zone.intern ("/");
  static const long QUARK_EQL   = zone.intern ("==");
  static const long QUARK_NEQ   = zone.intern ("!=");
  static const long QUARK_LTH   = zone.intern ("<");
  static const long QUARK_LEQ   = zone.intern ("<=");
  static const long QUARK_GTH   = zone.intern (">");
  static const long QUARK_GEQ   = zone.intern (">=");
  static const long QUARK_AEQ   = zone.intern ("+=");
  static const long QUARK_SEQ   = zone.intern ("-=");
  static const long QUARK_MEQ   = zone.intern ("*=");
  static const long QUARK_DEQ   = zone.intern ("/=");
  static const long QUARK_ABS   = zone.intern ("abs");
  static const long QUARK_AND   = zone.intern ("and");
  static const long QUARK_SHL   = zone.intern ("shl");
  static const long QUARK_SHR   = zone.intern ("shr");
  static const long QUARK_XOR   = zone.intern ("xor");
  static const long QUARK_MOD   = zone.intern ("mod");
  static const long QUARK_NOT   = zone.intern ("not");
  static const long QUARK_ODDP  = zone.intern ("odd-p");
  static const long QUARK_EVENP = zone.intern ("even-p");
  static const long QUARK_ZEROP = zone.intern ("zero-p");

  // evaluate an object to a native value

  t_long Integer::evalto (Runnable* robj, Nameset* nset, Object* object) {
    Object* obj = (object == nilp) ? nilp : object->eval (robj, nset);
    Integer* val = dynamic_cast <Integer*> (obj);
    if (val == nilp) throw Exception ("type-error", "nil object to evaluate");
    return val->tointeger ();
  }

  // create a new object in a generic way

  Object* Integer::mknew (Vector* argv) {
    if ((argv == nilp) || (argv->length () == 0)) return new Integer;
    if (argv->length () != 1) 
      throw Exception ("argument-error", 
		       "too many argument with integer constructor");
    // try to map the integer argument
    Object* obj = argv->get (0);
    if (obj == nilp) return new Integer;

    // try an integer object
    Integer* ival = dynamic_cast <Integer*> (obj);
    if (ival != nilp) return new Integer (*ival);

    // try a real object
    Real* rval = dynamic_cast <Real*> (obj);
    if (rval != nilp) return new Integer (rval->tointeger ());

    // try a character object
    Character* cval = dynamic_cast <Character*> (obj);
    if (cval != nilp) return new Integer (cval->toquad ());

    // try a string object
    String* sval = dynamic_cast <String*> (obj);
    if (sval != nilp) return new Integer (*sval);

    // illegal object
    throw Exception ("type-error", "illegal object with integer constructor",
		     obj->repr ());
  }

  // return true if the given quark is defined

  bool Integer::isquark (const long quark, const bool hflg) const {
    rdlock ();
    if (zone.exists (quark) == true) {
      unlock ();
      return true;
    }
    bool result = hflg ? Literal::isquark (quark, hflg) : false;
    unlock ();
    return result;
  }

  // operate this object with another object

  Object* Integer::oper (t_oper type, Object* object) {
    Integer* iobj = dynamic_cast <Integer*> (object);
    Real*    dobj = dynamic_cast <Real*>    (object);
    switch (type) {
    case Object::ADD:
      if (iobj != nilp) return new Integer (*this + *iobj);
      if (dobj != nilp) return new Integer (*this + dobj->tointeger ());
      break;
    case Object::SUB:
      if (iobj != nilp) return new Integer (*this - *iobj);
      if (dobj != nilp) return new Integer (*this - dobj->tointeger ());
      break;
    case Object::MUL:
      if (iobj != nilp) return new Integer (*this * *iobj);
      if (dobj != nilp) return new Integer (*this * dobj->tointeger ());
      break;
    case Object::DIV:
      if (iobj != nilp) return new Integer (*this / *iobj);
      if (dobj != nilp) return new Integer (*this / dobj->tointeger ());
      break;
    case Object::UMN:
      return new Integer (-(*this));
      break;
    case Object::EQL:
      if (iobj != nilp) return new Boolean (*this == *iobj);
      if (dobj != nilp) return new Boolean (*this == dobj->tointeger ());
      break;
    case Object::NEQ:
      if (iobj != nilp) return new Boolean (*this != *iobj);
      if (dobj != nilp) return new Boolean (*this != dobj->tointeger ());
      break;
    case Object::GEQ:
      if (iobj != nilp) return new Boolean (*this >= *iobj);
      if (dobj != nilp) return new Boolean (*this >= dobj->tointeger ());
      break;
    case Object::GTH:
      if (iobj != nilp) return new Boolean (*this > *iobj);
      if (dobj != nilp) return new Boolean (*this > dobj->tointeger ());
      break;
    case Object::LEQ:
      if (iobj != nilp) return new Boolean (*this <= *iobj);
      if (dobj != nilp) return new Boolean (*this <= dobj->tointeger ());
      break;
    case Object::LTH:
      if (iobj != nilp) return new Boolean (*this < *iobj);
      if (dobj != nilp) return new Boolean (*this < dobj->tointeger ());
      break;
    }
    throw Exception ("type-error", "invalid operand with integer",
		     Object::repr (object));
  }

  // set an object to this integer

  Object* Integer::vdef (Runnable* robj, Nameset* nset, Object* object) {
    Integer* iobj = dynamic_cast <Integer*> (object);
    if (iobj != nilp) {
      *this = *iobj;
      robj->post (this);
      return this;
    }
    Real* dobj = dynamic_cast <Real*> (object);
    if (dobj != nilp) {
      *this = dobj->tointeger ();
      robj->post (this);
      return this;
    }
    Character* cobj = dynamic_cast <Character*> (object);
    if (cobj != nilp) {
      *this = (t_long) cobj->toquad ();
      robj->post (this);
      return this;
    }
    throw Exception ("type-error", "invalid object with integer vdef",
		     Object::repr (object));
  }

  // apply this object with a set of arguments and a quark

  Object* Integer::apply (Runnable* robj, Nameset* nset, const long quark,
			  Vector* argv) {
    // get the number of arguments
    long argc = (argv == nilp) ? 0 : argv->length ();

    // dispatch 0 argument
    if (argc == 0) {
      if (quark == QUARK_ABS)   return new Integer (abs    ());
      if (quark == QUARK_EVENP) return new Boolean (iseven ());
      if (quark == QUARK_ODDP)  return new Boolean (isodd  ());
      if (quark == QUARK_ZEROP) return new Boolean (iszero ());

      if (quark == QUARK_OPP) {
	++(*this);
	robj->post (this);
	return this;
      }
      if (quark == QUARK_OMM) {
	--(*this);
	robj->post (this);
	return this;
      }
      if (quark == QUARK_NOT) {
	return new Integer (~(*this));
      } 
    }
    // dispatch 1 argument
    if (argc == 1) {
      if (quark == QUARK_ADD) return oper (Object::ADD, argv->get (0));
      if (quark == QUARK_SUB) return oper (Object::SUB, argv->get (0));
      if (quark == QUARK_MUL) return oper (Object::MUL, argv->get (0));
      if (quark == QUARK_DIV) return oper (Object::DIV, argv->get (0));
      if (quark == QUARK_EQL) return oper (Object::EQL, argv->get (0));
      if (quark == QUARK_NEQ) return oper (Object::NEQ, argv->get (0));
      if (quark == QUARK_LTH) return oper (Object::LTH, argv->get (0));
      if (quark == QUARK_LEQ) return oper (Object::LEQ, argv->get (0));
      if (quark == QUARK_GTH) return oper (Object::GTH, argv->get (0));
      if (quark == QUARK_GEQ) return oper (Object::GEQ, argv->get (0));

      if (quark == QUARK_AEQ) {
	t_long val = argv->getint (0);
	*this += val;
	robj->post (this);
	return this;
      }
      if (quark == QUARK_SEQ) {
	t_long val = argv->getint (0);
	*this -= val;
	robj->post (this);
	return this;
      }
      if (quark == QUARK_MEQ) {
	t_long val = argv->getint (0);
	*this *= val;
	robj->post (this);
	return this;
      }
      if (quark == QUARK_DEQ) {
	t_long val = argv->getint (0);
	*this /= val;
	robj->post (this);
	return this;
      }
      if (quark == QUARK_MOD) {
	t_long val = argv->getint (0);
	return new Integer (*this % val);
      }
      if (quark == QUARK_SHL) {
	t_long val = argv->getint (0);
	Object* result = new Integer (shl (val));
	return result;
      }
      if (quark == QUARK_SHR) {
	t_long val = argv->getint (0);
	Object* result = new Integer (shr (val));
	return result;
      }
      if (quark == QUARK_XOR) {
	t_long val = argv->getint (0);
	Object* result = new Integer (*this ^ val);
	return result;
      }
      if (quark == QUARK_AND) {
	t_long val = argv->getint (0);
	Object* result = new Integer (*this & val);
	return result;
      }
      if (quark == QUARK_OR) {
	t_long val = argv->getint (0);
	Object* result = new Integer (*this | val);
	return result;
      }
    } 
    // call the literal method
    return Literal::apply (robj, nset, quark, argv);
  }
}
