/* -*- c++ -*- */

/*
 *  MICO --- a free CORBA implementation
 *
 *  The MICO CORBA Component Project was sponsored by Alcatel.
 */

#include <CORBA.h>
#include <mico/CCM.h>
#include <mico/CosNaming.h>

#ifndef __MICO_CCM_CONTAINER_H__
#define __MICO_CCM_CONTAINER_H__

namespace MICO {

  namespace CCM {

    /*
     * Base for all Containers
     */

    class ContainerBase {
    public:
      virtual ~ContainerBase ();
      virtual void activate () = 0;
      virtual void deactivate () = 0;
    };

    /*
     * Service Container
     */

    class ServiceContainer;

    class ServiceContext : virtual public Components::SessionContext {
    private:
      ServiceContainer * _container;
      
    public:
      ServiceContext (ServiceContainer *);
      Components::CCMHome_ptr get_CCM_home ();
      CORBA::Object_ptr get_CCM_object ();
    };

    class ServiceContainer : virtual public ContainerBase {
    private:
      CORBA::ORB_var _orb;
      PortableServer::POA_var _my_poa;
      CosNaming::NamingContextExt_var _naming;
      ServiceContext * _context;

    public:
      struct ComponentInfo {
	string home_short_name;
	string home_absolute_name;
	string home_id;
	string component_short_name;
	string component_absolute_name;
	string component_id;
	PortableServer::ServantBase_var home_skel;
	PortableServer::ServantBase_var comp_skel;
      };

    private:
      bool _have_info;
      ComponentInfo _info;
      CORBA::Object_var _home_ref;
      CORBA::Object_var _comp_ref;

    public:
      ServiceContainer (CORBA::ORB_ptr orb);
      ~ServiceContainer ();

      void load (const ComponentInfo & info);
      void activate ();
      void deactivate ();

      Components::CCMHome_ptr
      get_reference_for_home ();

      Components::CCMObject_ptr
      get_reference_for_component ();

      Components::SessionContext_ptr
      get_session_context ();

      /*
	CORBA::Object_ptr
	get_reference_for_facet (CORBA::LocalObject_ptr);

	CORBA::Object_ptr
	activate_facet (CORBA::LocalObject_ptr, PortableServer::Servant);
      */
    };

    /*
     * Session Container
     */

    class SessionContainer;

    class SessionContext : virtual public Components::SessionContext {
    private:
      CORBA::ORB_var _orb;
      PortableServer::Current_var _current;
      SessionContainer * _container;
      
    public:
      SessionContext (CORBA::ORB_ptr, SessionContainer *);
      Components::CCMHome_ptr get_CCM_home ();
      CORBA::Object_ptr get_CCM_object ();
    };

    class SessionContainer : virtual public ContainerBase {
    private:
      CORBA::ORB_var _orb;
      PortableServer::POA_var _home_poa;
      PortableServer::POA_var _component_poa;
      CosNaming::NamingContextExt_var _naming;
      SessionContext * _context;

    public:
      struct ComponentInfo {
	string home_short_name;
	string home_absolute_name;
	string home_id;
	string component_short_name;
	string component_absolute_name;
	string component_id;
	PortableServer::ServantBase_var home_skel;
      };

    private:
      ComponentInfo _info;
      bool _have_info;

      struct oidless : binary_function<const PortableServer::ObjectId &,
		                       const PortableServer::ObjectId &,
		                       bool> {
	bool operator() (const PortableServer::ObjectId & i1,
			 const PortableServer::ObjectId & i2)
	  {
	    if (i1.length() != i2.length()) {
	      return (i1.length() < i2.length());
	    }
	    return (memcmp (i1.get_buffer(), i2.get_buffer(),
			    i1.length()) < 0);
	  }
      };

      CORBA::Object_var _home_ref;
      typedef map<PortableServer::ObjectId,
	CORBA::LocalObject *, oidless> InstanceMap;
      InstanceMap active_components;
      InstanceMap active_facets;

    public:
      SessionContainer (CORBA::ORB_ptr orb);
      ~SessionContainer ();

      void load (const ComponentInfo & info);
      void activate ();
      void deactivate ();

      Components::SessionContext_ptr
      get_session_context ();

      Components::CCMHome_ptr
      get_reference_for_home ();

      Components::CCMObject_ptr
      activate_component (CORBA::LocalObject_ptr instance,
			  PortableServer::Servant skel);

      Components::CCMObject_ptr
      get_reference_for_component (PortableServer::Servant skel);

      PortableServer::Servant
      get_skeleton_for_reference (CORBA::Object_ptr ref);

      Components::CCMObject_ptr
      get_reference_for_instance (CORBA::LocalObject_ptr);

      CORBA::LocalObject_ptr
      get_instance_for_component (PortableServer::Servant skel);

      void
      deactivate_component (CORBA::Object_ptr ref);

      void
      deactivate_component (PortableServer::Servant skel);

      /*
       * Todo:
       *
       * - store relationship between components and facets for accurate
       *   reference counting
       * - reap facet reference count if a component is deactivated
       */

      CORBA::Object_ptr
      get_reference_for_facet (CORBA::LocalObject_ptr);

      CORBA::Object_ptr
      activate_facet (CORBA::LocalObject_ptr, PortableServer::Servant);
    };
  }
}

#endif
