/*
 *  Implementation of COSS Relationship Service for MICO
 *  Copyright (C) 1998 Karel Gardas with the assistance of Kay Roemer
 *                                                       & Arno Puder
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Send comments and/or bug reports to:
 *                 mico@informatik.uni-frankfurt.de
 *  or to my private e-mail:
 *                 gardask@alpha.inf.upol.cz
 */


#include <string.h>
#include <iostream.h>
#include <fstream.h>
#include <unistd.h>
#include <mico/CosRelationships.h>
#include <mico/Role_impl.h>
#include <mico/RelationshipIterator_impl.h>


//#define DEBUG 1

Role_impl::Role_impl()
{
#if DEBUG
  cout << "<Role_impl> Role_impl()\n";
#endif
  iterator_server = CORBA::string_dup ("");
  rel_ship_repoid = CORBA::string_dup ("");
  rel_ships.length (0);
  rel_obj = CosRelationships::RelatedObject::_nil ();
}

Role_impl::Role_impl (CORBA::Object_ptr obj)
  : CosRelationships::Role_skel (obj)
{
  CORBA::ORB_ptr orb = _orb();
  char s[1000];
#if DEBUG
  cout << "  <Role_impl> restoring id " << obj->_ident() << endl;
#endif
  ifstream inp (obj->_ident());
  assert (inp);
  inp >> cardinality;
  inp >> max_cardinality;
  inp >> min_cardinality;
  inp >> s;
  CORBA::Object_var tmp_obj = orb->string_to_object (s);
  rel_obj = CORBA::Object::_duplicate (tmp_obj);
  inp >> s;
  iterator_server = CORBA::string_dup (s);
  inp >> s;
  if (strcmp (s, "NULL") == 0)
    rel_ship_repoid = CORBA::string_dup ("");
  else
    rel_ship_repoid = CORBA::string_dup (s);
#if DEBUG
  cout << "  <Role_impl> card: " << cardinality << " max: " << max_cardinality 
       << " min: " << min_cardinality << "\n";
  cout << "  <Role_impl> string:     " << s << "\n";
  cout << "  <Role_impl> iterator_server: " << iterator_server << "\n";
#endif

  CORBA::Long length;
  inp >> length;
#if DEBUG
  cout << "  <Role_impl> length: " << length << "\n";
#endif
  rel_ships.length (length);
#if DEBUG
  cout << "  <Role_impl> restoring " << length << " members of rel_ships" 
       << flush;
#endif
  for(int i=0; i<length; i++) {
#if DEBUG
    cout << "." << flush;
#endif
    inp >> s;
    if (!strcmp (s, "NIL\n")) {
      CORBA::Object_var obj = orb->string_to_object (s);
      CosRelationships::Relationship_ptr tmp_rel 
	= CosRelationships::Relationship::_narrow (obj);    
      rel_ships[i].the_relationship 
	= CosRelationships::Relationship::_duplicate (tmp_rel);
      CORBA::Long tmp_id;
      inp >> tmp_id;
      rel_ships[i].constant_random_id = tmp_id;
    }
    else {
      rel_ships[i].the_relationship = CosRelationships::Relationship::_nil();
      rel_ships[i].constant_random_id = 0;
    }
  }
#if DEBUG
  cout << "done.\n";
#endif


#if DEBUG
  if (CORBA::is_nil (tmp_obj))
    cout << "  <Role_impl> tmp_obj is NIL!\n";
  if (CORBA::is_nil (rel_obj))
    cout << "  <Role_impl> rel_obj is NIL!\n";
  cout << "  <Role_impl> Role_impl END!\n";
#endif
}


Role_impl::Role_impl (CosRelationships::RelatedObject_ptr obj, 
		      CORBA::Long card)
{
  rel_obj = obj;
  iterator_server = CORBA::string_dup ("");
  rel_ship_repoid = CORBA::string_dup ("");
  cardinality = card;
  rel_ships.length (0);
#if DEBUG
  cout << "  <Role_impl> Role_impl (obj, "  << card << ")\n";
  if (CORBA::is_nil (rel_obj))
    cout << "  <Role_impl> rel_obj is NIL!\n";
  else 
    cout << "  <Role_impl> rel_obj is DEFINED!\n";
  cout << "  <Role_impl> rel_obj = " << rel_obj << "\n";
  cout << "  <Role_impl> constructor done.\n";
#endif
}


CORBA::Boolean
Role_impl::_save_object ()
{
  CORBA::ORB_ptr orb = _orb();
  CORBA::String_var tmp_string = orb->object_to_string (rel_obj); 

#if DEBUG
  cout << "  <Role_impl> saving id " << _ident() << endl;
#endif
  ofstream out (_ident());
  assert (out);
  out << cardinality << "\n";
  out << max_cardinality << "\n";
  out << min_cardinality << "\n";
  out << tmp_string.in() << "\n";
  out << iterator_server << "\n";
  if (strcmp (rel_ship_repoid, "") == 0)
    out << "NULL\n";
  else
    out << rel_ship_repoid << "\n";
  // writing rel_ships
#if DEBUG
  cout << "  <Role_impl> writing rel_ships of "<< rel_ships.length() 
       << " members" << flush;
#endif
  out << rel_ships.length() << "\n";
  for(CORBA::ULong i=0; i<rel_ships.length(); ++i) {
#if DEBUG
    cout << "." << flush;
#endif
    if (!CORBA::is_nil (rel_ships[i].the_relationship)) {
      tmp_string = orb->object_to_string (rel_ships[i].the_relationship);
      out << tmp_string.in() << "\n";
      out << rel_ships[i].constant_random_id;
    }
    else {
      out << "NIL\n";
    }
  }
#if DEBUG
  cout << "done.\n";
  cout << "  <Role_impl> rel_obj: " << tmp_string << "\n";
#endif

  out.close ();

  return TRUE;
}


CosRelationships::RelatedObject_ptr 
Role_impl::related_object ()
{
#if DEBUG
  cout << "  <Role_impl> related_object();\n";
  if (CORBA::is_nil (rel_obj))
      cout << "  <Role_impl> rel_obj is NIL!\n";
#endif
  return CosRelationships::RelatedObject::_duplicate (rel_obj);
}


CosRelationships::RelatedObject_ptr
Role_impl::get_other_related_object (
			    const CosRelationships::RelationshipHandle& rel,
			    const char *target_name)
{
  CosRelationships::Role_var tmp = get_other_role (rel, target_name);
  return tmp->related_object ();
}


CosRelationships::Role_ptr
Role_impl::get_other_role (const CosRelationships::RelationshipHandle& rel,
			   const char *target_name)
{
  // CosRelationships::Relationship tmp = rel.the_relationship

#if DEBUG
  cout << "  <Role_impl> get_other_role ();\n";
  cout << "  <Role_impl> target_name is " << target_name << "\n";
  cout << "  <Role_impl> random id: " << rel.constant_random_id << "\n";
#endif

  CORBA::Boolean is_in = FALSE;
  for (CORBA::ULong i = 0; i < rel_ships.length (); i++) {
    if (rel.constant_random_id == rel_ships[i].constant_random_id) {
      is_in = TRUE;
      CosRelationships::NamedRoles_var 
	nr = rel.the_relationship->named_roles();
      CORBA::ULong x = nr->length();
      for(CORBA::ULong j=0; j<x; j++) {
#if DEBUG
	cout << "  <Role_impl> " << j 
	     << ". role name: " << nr[j].name << " ..." << flush;
#endif
	if (strcmp(target_name, nr[j].name) == 0) {
#if DEBUG
	  cout << "FOUND!!!\n";
	  cout << "  <Role_impl> " << target_name << " = " << nr[j].name
	       << "\n";
#endif
	  return CosRelationships::Role::_duplicate (nr[j].aRole);
	}
	else {
#if DEBUG
          cout << "no.\n";
#endif
	}
	  
      }
    }
  }
  if (!is_in) {
    CosRelationships::Role::UnknownRelationship ex;
    mico_throw (ex);
  }
  CosRelationships::Role::UnknownRoleName ex;
  mico_throw (ex);

  return CosRelationships::Role::_nil ();
}


void
Role_impl::get_relationships 
(CORBA::ULong how_many,
 CosRelationships::RelationshipHandles*& rels,
 CosRelationships::RelationshipIterator_ptr& iterator)
{
  CORBA::ORB_var orb = _orb ();
  CORBA::BOA_var boa = _boa ();

#if DEBUG
  cout << "  <Role_impl> get_relationships (...)\n";
  cout << "  <Role_impl> rel_ships.length (): " << rel_ships.length () << "\n";
  cout << "  <Role_impl> first of all relationship_handles:\n";
#endif

  CosRelationships::RelationshipHandles* 
    tmp_handles = new CosRelationships::RelationshipHandles;
  int length;
  if (rel_ships.length () < how_many)
    length = rel_ships.length ();
  else
    length = how_many;
  tmp_handles->length (length);
  for (int i=0; i<length; i++) {
#if DEBUG
    cout << "  <Role_impl> rand id: " << rel_ships[i].constant_random_id 
	 << "\n"; 
#endif
    (*tmp_handles)[i].constant_random_id = rel_ships[i].constant_random_id;
    (*tmp_handles)[i].the_relationship 
      = CosRelationships::Relationship::_duplicate 
      (rel_ships[i].the_relationship);
  }
  rels = tmp_handles;

#if DEBUG
  cout << "  <Role_impl> Create handles for iterator:\n";
#endif

  if (how_many < rel_ships.length ()) {
#if DEBUG
    cout << "  <Role_impl> rel_ships.length (): " << rel_ships.length () 
	 << "\n";
    cout << "  <Role_impl> how_many: " << how_many << "\n";
#endif

    CosRelationships::RelationshipHandles tmp_relships;
    RelationshipIterator_impl* tmp_iter;
    int index = how_many;
    int max = rel_ships.length () - how_many;

#if DEBUG
    cout << "  <Role_impl> max: " << max << "\n";
    cout << "  <Role_impl> index: " << index << "\n";
#endif
 
    tmp_relships.length (max);
    for (CORBA::ULong i=0; i<tmp_relships.length (); i++) {
      tmp_relships[i].constant_random_id 
	= rel_ships[i + index].constant_random_id;
      tmp_relships[i].the_relationship 
	= CosRelationships::Relationship::_duplicate
	(rel_ships[i + index].the_relationship);
    }

#if DEBUG
    cout << "  <Role_impl> create iterator\n";
#endif

     tmp_iter = new RelationshipIterator_impl (tmp_relships);
    CosRelationships::RelationshipIterator_ptr iter;
    iter = tmp_iter;

#if DEBUG
    cout << "  <Role_impl> iter->_repoid (): " << iter->_repoid () 
	 << "\n";
#endif
 
    CORBA::ImplementationDef_var impl = find_impl (iterator_server);
#if DEBUG
    cout << "  <Role_impl> change impl for iterator to " << iterator_server
	 << "\n";
#endif

    boa->change_implementation (iter, impl);
    //boa->deactivate_obj (iter);
#if DEBUG
    cout << "  <Role_impl> boa->change_impl (...) done!\n";
#endif

    iterator = CosRelationships::RelationshipIterator::_duplicate (iter);
    //CosRelationships::RelationshipHandles* handles;
    //iterator->next_n (1, handles);

  }
  else {
#if DEBUG
    cout << "  <Role_impl> create iterator _nil ()\n";
#endif
    iterator = CosRelationships::RelationshipIterator::_nil ();
  }

#if DEBUG
  cout << "  <Role_impl> get_relationships DONE!\n";
#endif
 
}


void 
Role_impl::destroy_relationships ()
{
  CORBA::Boolean error = FALSE;
  CosRelationships::Role::CannotDestroyRelationship ex;
  ex.offenders.length (0);
  int index = 0;

  for (CORBA::ULong i=0; i<rel_ships.length (); i++) {
#ifdef HAVE_EXCEPTIONS
    try {
#endif
      rel_ships[i].the_relationship->destroy ();
#ifdef HAVE_EXCEPTIONS
    } catch (CosRelationships::Relationship::CannotUnlink_var& exception)
      {
	error = TRUE;
	ex.offenders.length (ex.offenders.length () + 1);
	index = ex.offenders.length () - 1;
	ex.offenders[index].constant_random_id 
	  = rel_ships[i].constant_random_id;
	ex.offenders[index].the_relationship 
	  = CosRelationships::Relationship::_duplicate 
	  (rel_ships[i].the_relationship);
      }
#endif
  }
  if (error) 
    mico_throw (ex);
    
}


void
Role_impl::destroy ()
{
  if (rel_ships.length () != 0) {
    CosRelationships::Role::ParticipatingInRelationship ex;
    ex.the_relationships = rel_ships;
    mico_throw (ex);
  }

  CORBA::BOA_var boa = _boa ();
  CORBA::ORB_var orb = _orb ();
  boa->deactivate_obj (this);
  orb->shutdown (TRUE);
  
}


CORBA::Boolean 
Role_impl::check_minimum_cardinality ()
{
  if (cardinality > min_cardinality)
    return TRUE;
  else
    return FALSE;
}


void 
Role_impl::link (const CosRelationships::RelationshipHandle& rel,
		 const CosRelationships::NamedRoles& named_roles)
{
#if DEBUG
  cout << "  <Role_impl> link (...)\n";
  cout << "  <Role_impl> cardinality = " << cardinality << "\n";
#endif

  if (cardinality == max_cardinality) {
#if DEBUG
    cout << "  <Role_impl> MaxCardinalityExceeded\n";
#endif
    CosRelationships::RelationshipFactory::MaxCardinalityExceeded ex;
    ex.culprits.length (1);
#if DEBUG
    cout << "  <Role_impl> ex.culprits.length (1);";
#endif

    ex.culprits[0].aRole = CosRelationships::Role::_duplicate (this);
    for (CORBA::ULong i=0; i<named_roles.length (); i++) {
#if DEBUG
      cout << "  <Role_impl> " << i << "\n";
#endif
      if (this->_is_equivalent (named_roles[i].aRole)) {
#if DEBUG
	cout << "  <Role_impl> is equivalent!, name is " 
	     << named_roles[i].name << "\n";
#endif
	ex.culprits[0].name = CORBA::string_dup (named_roles[i].name);
#if DEBUG
      cout << "  <Role_impl> ex.culprits[0].name is " << ex.culprits[0].name 
	   << "\n";
#endif

      }
    }
    mico_throw (ex);
  }
#if DEBUG
  cout << "  <Role_impl> cardinality is OK!\n";
#endif

  if (strcmp (rel_ship_repoid, "") != 0) {
    if (!rel.the_relationship->_get_interface ()->is_a 
	(CORBA::string_dup (rel_ship_repoid))) {
      CosRelationships::Role::RelationshipTypeError ex;
      mico_throw (ex);
    }
  }
 
  rel_ships.length (rel_ships.length () + 1);
  rel_ships[rel_ships.length () - 1].constant_random_id 
    = rel.constant_random_id;
  rel_ships[rel_ships.length () - 1].the_relationship 
    = CosRelationships::Relationship::_duplicate (rel.the_relationship);
 
  cardinality++;
#if DEBUG
  cout << "  <Role_impl> last relship random_id: " 
       << rel_ships[rel_ships.length () - 1].constant_random_id << "\n";
  cout << "  <Role_impl> link (...) done!\n";
#endif

}


void 
Role_impl::unlink(const CosRelationships::RelationshipHandle& rel)
{
  CosRelationships::RelationshipHandle tmp_rel_handle;
  tmp_rel_handle.constant_random_id = 0;
  tmp_rel_handle.the_relationship = CosRelationships::Relationship::_nil ();
  int pos = 0;
  CORBA::Boolean is_in = FALSE;
#if DEBUG
  cout << "  <Role_impl> unlink (...)\n";
  cout << "  <Role_impl> random_id: " << rel.constant_random_id << "\n";
  cout << "  <Role_impl> rel_ships length: " << rel_ships.length () << "\n";
#endif
  for (CORBA::ULong i=0; i<rel_ships.length (); i++) {
#if DEBUG
    cout << "  <Role_impl> " << i << ". r_handle random id: " 
	 << rel_ships[i].constant_random_id << "\n";
#endif
    if (rel.constant_random_id == rel_ships[i].constant_random_id) {
      pos = i;
      is_in = TRUE;
#if DEBUG
      cout << "  <Role_impl> position is " << pos << "\n";
#endif
      break;
    }
  }
  if (is_in) {
    for (CORBA::ULong i=pos; i<rel_ships.length () - 1; i++) {
      rel_ships[i] = rel_ships[i + 1];
    }
    rel_ships.length (rel_ships.length () - 1);
  }
  else {
    CosRelationships::Role::UnknownRelationship ex;
    mico_throw (ex);
  }
  
  cardinality--;
}

// extension


CORBA::ImplementationDef_ptr
Role_impl::find_impl (const char *name)
{
#if DEBUG
  cout << "  <Role_impl> find_impl ...\n";
#endif
  CORBA::Object_var obj =
      _orbnc()->resolve_initial_references ("ImplementationRepository");
  assert (!CORBA::is_nil (obj));

  CORBA::ImplRepository_var imr = CORBA::ImplRepository::_narrow (obj);
  assert (!CORBA::is_nil (imr));

  CORBA::ImplRepository::ImplDefSeq_var impls = imr->find_by_name (name);
    assert (impls->length() > 0);
#if DEBUG
    cout << "  <Role_impl> find_impl: " << impls[0]->name () << "\n";
#endif
    return CORBA::ImplementationDef::_duplicate (impls[(CORBA::ULong)0]);
}

void 
Role_impl::set_min_cardinality (CORBA::Long x)
{
  min_cardinality = x;
}


void 
Role_impl::set_max_cardinality (CORBA::Long x)
{
  max_cardinality = x;
}


void 
Role_impl::set_cardinality (CORBA::Long x)
{
  cardinality = x;
}


void 
Role_impl::set_iterator_server (char* s)
{
  iterator_server = CORBA::string_dup (s);
}


void 
Role_impl::set_relship_repoid (char* s)
{
  rel_ship_repoid = CORBA::string_dup (s);
}


