// ---------------------------------------------------------------------------
// - Signature.cpp                                                           -
// - afnix:sec module - message signature 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 "Item.hpp"
#include "Ascii.hpp"
#include "Vector.hpp"
#include "Unicode.hpp"
#include "Signature.hpp"
#include "QuarkZone.hpp"
#include "Exception.hpp"

namespace afnix {

  // -------------------------------------------------------------------------
  // - private section                                                       -
  // -------------------------------------------------------------------------

  // the dsa signature
  struct s_sdsa {
    // the dsa r value
    Relatif d_r;
    // the dsa s value
    Relatif d_s;
    // create a dsa signature by value
    s_sdsa (const Relatif& r, const Relatif& s) {
      d_r = r;
      d_s = s;
    }
    // copy construct this dsa signature
    s_sdsa (const s_sdsa& that) {
      d_r = that.d_r;
      d_s = that.d_s;
    }
    // return a formatted component by type
    String format (const Signature::t_isgn type) const {
      if (type == Signature::SDSA_SCMP) return d_s.tohexa ();
      if (type == Signature::SDSA_RCMP) return d_r.tohexa ();
      throw Exception ("signature-error", "invalid dsa component accessor");
    }
    // return a relatif component by type
    Relatif getscmp (const Signature::t_isgn type) const {
      if (type == Signature::SDSA_SCMP) return d_s;
      if (type == Signature::SDSA_RCMP) return d_r;
      throw Exception ("signature-error", "invalid dsa component accessor");
    }
  };

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

  // create a default signature

  Signature::Signature (void) {
    d_type = SNIL;
    p_snil = nilp;
  }

  // create a signature by type and value

  Signature::Signature (const t_sign type, const Relatif& r, const Relatif& s) {
    if (type == SDSA) {
      d_type = type;
      p_sdsa = new s_sdsa (r, s);
    } else {
      throw Exception ("signature-error", "invalid type at construction");
    }
  }

  // copy construct this signature

  Signature::Signature (const Signature& that) {
    that.rdlock ();
    try {
      d_type = that.d_type;
      switch (d_type) {
      case SNIL:
	p_snil = nilp;
	break;
      case SDSA:
	p_sdsa = new s_sdsa (*that.p_sdsa);
	break;
      }
      that.unlock ();
    } catch (...) {
      that.unlock ();
      throw;
    }
  }

  // destroy this signature

  Signature::~Signature (void) {
    switch (d_type) {
    case SDSA:
      delete p_sdsa;
      break;
    default:
      break;
    }
  }

  // return the class name

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

 // return a clone of this object

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

  // assign a signature to this one

  Signature& Signature::operator = (const Signature& that) {
    // check for self assignation
    if (this == &that) return *this;
    // lock and assign
    wrlock ();
    that.rdlock ();
    try {
      if (this != &that) {
	// reset the signature
	reset ();
	// set the new type
	d_type = that.d_type;
	// set the new signature
	switch (d_type) {
	case SNIL:
	  p_snil = nilp;
	  break;
	case SDSA:
	  p_sdsa = new s_sdsa (*that.p_sdsa);
	  break;
	}
      }
      unlock ();
      that.unlock ();
      return *this;
    } catch (...) {
      unlock ();
      that.unlock ();
      throw;
    }
  }

  // reset this signature

  void Signature::reset (void) {
    wrlock ();
    try {
      // clean the old signature
      switch (d_type) {
      case SDSA:
	delete p_sdsa;
	p_sdsa = nilp;
	break;
      default:
	break;
      }
      // reset the signature
      d_type = SNIL;
      p_snil = nilp;
      // done
      unlock ();
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return a formatted component representation by type

  String Signature::format (const t_isgn type) const {
    rdlock ();
    try {
      // initialize result
      String result;
      // select the key
      switch (d_type) {
      case SDSA:
	result = p_sdsa->format (type);
	break;
      default:
	throw Exception ("signature-error", "unsupported component accessor");
	break;
      }
      unlock ();
      return result;      
    } catch (...) {
      unlock ();
      throw;
    }
  }

  // return a relatif key by type

  Relatif Signature::getrcmp (const t_isgn type) const {
    rdlock ();
    try {
      // initialize result
      Relatif result = 0;
      // select the key
      switch (d_type) {
      case SDSA:
	result = p_sdsa->getscmp (type);
	break;
      default:
	throw Exception ("signature-error", "unsupported component accessor");
	break;
      }
      unlock ();
      return result;      
    } catch (...) {
      unlock ();
      throw;
    }
  }

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

  // the object eval quarks
  static const long QUARK_SIGN     = String::intern ("Signature");
  static const long QUARK_SNIL     = String::intern ("NIL");
  static const long QUARK_SDSA     = String::intern ("DSA");
  static const long QUARK_SDSASCMP = String::intern ("DSA-S-COMPONENT");
  static const long QUARK_SDSARCMP = String::intern ("DSA-R-COMPONENT");

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

  // the signature supported quarks
  static const long QUARK_RESET    = zone.intern ("reset");
  static const long QUARK_FORMAT   = zone.intern ("format");
  static const long QUARK_GETRCMP  = zone.intern ("get-relatif-component");

  // map an item to a signature type
  static inline Signature::t_sign item_to_sign (const Item& item) {
    // check for a key item
    if (item.gettid () != QUARK_SIGN)
      throw Exception ("item-error", "item is not a signature item");
    // map the item to the enumeration
    long quark = item.getquark ();
    if (quark == QUARK_SNIL) return Signature::SNIL;
    if (quark == QUARK_SDSA) return Signature::SDSA;
    throw Exception ("item-error", "cannot map item to signature type");
  }

  // map a signature type to an item
  static inline Item* sign_to_item (const Signature::t_sign type) {
    switch (type) {
    case Signature::SNIL:
      return new Item (QUARK_SIGN, QUARK_SNIL);
      break;
    case Signature::SDSA:
      return new Item (QUARK_SIGN, QUARK_SDSA);
      break;
    }
    return nilp;
  }

  // map an item to a component accessor
  static inline Signature::t_isgn item_to_isgn (const Item& item) {
    // check for a key item
    if (item.gettid () != QUARK_SIGN)
      throw Exception ("item-error", "item is not a signature item");
    // map the item to the enumeration
    long quark = item.getquark ();
    if (quark == QUARK_SDSASCMP) return Signature::SDSA_SCMP;
    if (quark == QUARK_SDSARCMP) return Signature::SDSA_RCMP;
    throw Exception ("item-error", 
		     "cannot map item to signature index accesoor");
  }

  // map a component accessor to an item
  static inline Item* isgn_to_item (const Signature::t_isgn type) {
    switch (type) {
    case Signature::SDSA_SCMP:
      return new Item (QUARK_SIGN, QUARK_SDSASCMP);
      break;
    case Signature::SDSA_RCMP:
      return new Item (QUARK_SIGN, QUARK_SDSARCMP);
      break;
    }
    return nilp;
  }

  // evaluate an object data member

  Object* Signature::meval (Runnable* robj, Nameset* nset, const long quark) {
    // key type part
    if (quark == QUARK_SNIL)
      return new Item (QUARK_SIGN, QUARK_SNIL);
    if (quark == QUARK_SDSA)
      return new Item (QUARK_SIGN, QUARK_SDSA);
    // component accessor
    if (quark == QUARK_SDSASCMP)
      return new Item (QUARK_SIGN, QUARK_SDSASCMP);
    if (quark == QUARK_SDSARCMP)
      return new Item (QUARK_SIGN, QUARK_SDSARCMP);
    throw Exception ("eval-error", "cannot evaluate member",
                     String::qmap (quark));
  }
  
  // create a new object in a generic way
  
  Object* Signature::mknew (Vector* argv) {
    long argc = (argv == nilp) ? 0 : argv->length ();
    // check for 0 argument
    if (argc == 0) return new Signature;
    // invalid arguments
    throw Exception ("argument-error", "too many arguments with key");
  }

  // return true if the given quark is defined

  bool Signature::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* Signature::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 argument
    if (argc == 0) {
      if (quark == QUARK_RESET) {
	reset ();
	return nilp;
      }
    }
    // dispatch 1 argument
    if (argc == 1) {
      if (quark == QUARK_FORMAT) {
	// check for an item type
	Object* obj = argv->get (0);
	Item*  iobj = dynamic_cast <Item*> (obj);
	if (iobj == nilp) {
	  throw Exception ("type-error", "invalid object with format",
			   Object::repr (obj));
	}
	t_isgn type = item_to_isgn (*iobj);
	// get the relatif
	return new String (format (type));
      }
      if (quark == QUARK_GETRCMP) {
	// check for an item type
	Object* obj = argv->get (0);
	Item*  iobj = dynamic_cast <Item*> (obj);
	if (iobj == nilp) {
	  throw Exception ("type-error", 
			   "invalid object with get-relatif-component",
			   Object::repr (obj));
	}
	t_isgn type = item_to_isgn (*iobj);
	// get the relatif
	return new Relatif (getrcmp (type));
      }      
    }
    // call the object method
    return Object::apply (robj, nset, quark, argv);
  }
}
