// ---------------------------------------------------------------------------
// - Slot.cpp                                                                -
// - afnix:pim module - slot 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-2011 amaury darsch                                   -
// ---------------------------------------------------------------------------

#include "Slot.hpp"
#include "Vector.hpp"
#include "Integer.hpp"
#include "Runnable.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"

namespace afnix {

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

  // create a new slot

  Slot::Slot (void) {
    d_time = 0;
    d_dlen = 0;
    d_sidx = 0;
  }

  // create a new slot by time and duration

  Slot::Slot (const t_long time, const t_long dlen) {
    setslot (time, dlen);
    d_sidx = 0;
  }

  // copy construct this slot

  Slot::Slot (const Slot& that) {
    that.rdlock ();
    try {
      d_time = that.d_time;
      d_dlen = that.d_dlen;
      d_sidx = that.d_sidx;
      that.unlock ();
    } catch (...) {
      that.unlock ();
      throw;
    }
  }

  // return the slot class name

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

  // assign a slot to this one

  Slot& Slot::operator = (const Slot& that) {
    // check for self assignation
    if (this == &that) return *this;
    // lock and assign
    wrlock ();
    that.rdlock ();
    try {
      d_time = that.d_time;
      d_dlen = that.d_dlen;
      d_sidx = that.d_sidx;
      unlock ();
      that.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      that.unlock ();
      throw;
    }
  }

  // reset this slot

  void Slot::reset (void) {
    wrlock ();
    try {
      d_time = 0;
      d_dlen = 0;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the slot time

  void Slot::settime (const t_long time) {
    wrlock ();
    try {
      d_time = time;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the slot time
    
  t_long Slot::gettime (void) const {
    rdlock ();
    try {
      t_long result = d_time;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the slot duration
  
  void Slot::setdlen (const t_long dlen) {
    wrlock ();
    try {
      d_dlen = dlen;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the slot duration

  t_long Slot::getdlen (void) const {
    rdlock ();
    try {
      t_long result = d_dlen;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the slot at once

  void Slot::setslot (const t_long time, const t_long dlen) {
    wrlock ();
    try {
      d_time = time;    
      d_dlen = dlen;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // set the slot index

  void Slot::setsidx (const long sidx) {
    wrlock ();
    try {
      d_sidx = sidx;
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // get the slot index
    
  long Slot::getsidx (void) const {
    rdlock ();
    try {
      t_long result = d_sidx;
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return rtue if the slot match in time and duration

  bool Slot::match (const Slot& slot) const {
    rdlock ();
    try {
      bool result = (d_time == slot.d_time) && (d_dlen == slot.d_dlen);
      unlock ();
      return result;
    } catch (...) {
      unlock ();
      throw;
    }
  }

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

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

  // the object supported quarks
  static const long QUARK_RESET   = zone.intern ("reset");
  static const long QUARK_SETTIME = zone.intern ("set-time");
  static const long QUARK_GETTIME = zone.intern ("get-time");
  static const long QUARK_SETDLEN = zone.intern ("set-duration");
  static const long QUARK_GETDLEN = zone.intern ("get-duration");
  static const long QUARK_SETSLOT = zone.intern ("set-slot");
  static const long QUARK_SETSIDX = zone.intern ("set-index");
  static const long QUARK_GETSIDX = zone.intern ("get-index");

  // create a new object in a generic way

  Object* Slot::mknew (Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    // create a default slot object
    if (argc == 0) return new Slot;
    // check for 2 arguments
    if (argc == 2) {
      t_long time = argv->getlong (0);
      t_long dlen = argv->getlong (1);
      return new Slot (time, dlen);
    }
    throw Exception ("argument-error",
                     "too many argument with slot constructor");
  }

  // return true if the given quark is defined

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

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

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

    // check for 0 arguments
    if (argc == 0) {
      if (quark == QUARK_GETTIME) return new Integer (gettime ());
      if (quark == QUARK_GETDLEN) return new Integer (getdlen ());
      if (quark == QUARK_GETSIDX) return new Integer (getsidx ());
      if (quark == QUARK_RESET) {
	reset ();
	return nilp;
      }
    }
    // check for 1 argument
    if (argc == 1) {
      if (quark == QUARK_SETTIME) {
        t_long time = argv->getlong (0);
        settime (time);
        return nilp;
      }
      if (quark == QUARK_SETDLEN) {
        t_long dlen = argv->getlong (0);
        setdlen (dlen);
        return nilp;
      }
      if (quark == QUARK_SETSIDX) {
        long sidx = argv->getlong (0);
        setsidx (sidx);
        return nilp;
      }
    }
    // check for 2 argument
    if (argc == 2) {
      if (quark == QUARK_SETSLOT) {
        t_long time = argv->getlong (0);
        t_long dlen = argv->getlong (1);
        setslot (time, dlen);
        return nilp;
      }
    }    
    // call the object method
    return Object::apply (robj, nset, quark, argv);
  }
}
