#include <CosTradingC.h>
#include <CosTradingS.h>
#include <CosTradingReposC.h>
#include <CosTradingReposS.h>

#include "Trading/Mortgage/Mortgage.h"

#include <tao/corba.h>
#include <tao/PortableServer/PortableServer.h>
#include <orbsvcs/Shutdown_Utilities.h>

class Service_Shutdown_Functor : public Shutdown_Functor
{
public:
  Service_Shutdown_Functor (CORBA::ORB_ptr orb)
    : orb_ (CORBA::ORB::_duplicate (orb))
  {
  }

  void operator() (int which_signal)
  {
    ACE_DEBUG ((LM_DEBUG,
                ACE_TEXT ("shutting down on signal %d\n"),
                which_signal));
    (void) this->orb_->shutdown ();
  }

private:
  CORBA::ORB_var orb_;
};

MortgageImpl*
createMortgage (CosTrading::Register_ptr reg,
                const char *name,
                CORBA::Float rate)
{
  ACE_DEBUG ((LM_DEBUG,
              ACE_TEXT ("Exporting offer for %s with APR %.2f\n"),
              name, rate));

  MortgageImpl *pMortgageImpl = 0;
  ACE_NEW_RETURN (pMortgageImpl,
                  MortgageImpl (name, rate),
                  0);
  PortableServer::ServantBase_var owner_transfer (pMortgageImpl);

  CORBA::Object_var mortgageObjVar = pMortgageImpl->_this ();
  Mortgage_var mortgageVar = Mortgage::_narrow (mortgageObjVar.in ());

  CosTrading::PropertySeq properties;
  properties.length (2);

  properties[0].name = CORBA::string_dup (ACE_TEXT ("financial_organisation"));
  properties[0].value <<= name;
  properties[1].name = CORBA::string_dup (ACE_TEXT ("rate"));
  properties[1].value <<= rate;

  CORBA::String_var tmp =
    reg->_cxx_export (mortgageVar.in (), ACE_TEXT ("IDL:Mortgage:1.0"), properties);

  owner_transfer._retn ();
  return pMortgageImpl;
}

int
main (int argc, char** argv)
{
  try
    {
      // Get a reference to the ORB.
      CORBA::ORB_var orbVar = CORBA::ORB_init (argc, argv);

      // Cleaning guard.
      Service_Shutdown_Functor killer (orbVar.in ());
      Service_Shutdown kill_contractor (killer);

      // Resolve to the trading service.
      CORBA::Object_var objVar =
        orbVar->resolve_initial_references (ACE_TEXT ("TradingService"));
      CosTrading::Lookup_var lookupVar =
        CosTrading::Lookup::_narrow (objVar.in ());

      if (CORBA::is_nil (lookupVar.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("Error: nil reference.\n")),
                            1);
        }

      CosTrading::TypeRepository_var typeReposVar =
        lookupVar->type_repos ();
      CosTradingRepos::ServiceTypeRepository_var serviceTypeReposVar =
        CosTradingRepos::ServiceTypeRepository::_narrow (typeReposVar.in ());

      if (CORBA::is_nil (serviceTypeReposVar.in ()))
        {
          ACE_ERROR_RETURN ((LM_ERROR,
                             ACE_TEXT ("Error: nil reference.\n")),
                            1);
        }

      // Add the service type to the trading service.
      CosTradingRepos::ServiceTypeRepository::PropStructSeq properties;
      properties.length (2);

      properties[0].name =
        CORBA::string_dup (ACE_TEXT ("financial_organisation"));
      properties[0].value_type = CORBA::_tc_string;
      properties[0].mode =
        CosTradingRepos::ServiceTypeRepository::PROP_MANDATORY_READONLY;
      properties[1].name =
        CORBA::string_dup (ACE_TEXT ("rate"));
      properties[1].value_type = CORBA::_tc_float;
      properties[1].mode =
        CosTradingRepos::ServiceTypeRepository::PROP_NORMAL;

      CosTrading::Register_var registerVar = lookupVar->register_if ();

      CosTradingRepos::ServiceTypeRepository::ServiceTypeNameSeq superTypes;

      try
        {
          serviceTypeReposVar->add_type (ACE_TEXT ("IDL:Mortgage:1.0"),
                                         ACE_TEXT ("IDL:Mortgage:1.0"),
                                         properties,
                                         superTypes);
        }
      catch (const CosTradingRepos::ServiceTypeRepository::ServiceTypeExists &)
        {
          // The service type already exists. Assume it has been added from a
          // previous run of this example. Remove the dead offers from it
          // because they are transient and inactive, and export the offers again.
          ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Service type Mortgage already exists.\n")));
          ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Purging dead offers.\n")));

          CosTrading::Admin_var adminVar = lookupVar->admin_if ();

          CosTrading::OfferIdSeq_var idsSeqVar;
          CosTrading::OfferIdIterator_var idsIterVar;
          adminVar->list_offers (100, idsSeqVar.out (), idsIterVar.out ());

          for (CORBA::ULong count = 0; count < idsSeqVar->length (); ++count)
            {
              registerVar->withdraw (idsSeqVar[count]);
              ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Withdrawing %d\n"), count));
            }
        }

      // Get a reference to the root POA, and activate the manager.
      objVar =
        orbVar->resolve_initial_references (ACE_TEXT ("RootPOA"));
      PortableServer::POA_var rootPoaVar =
        PortableServer::POA::_narrow (objVar.in ());
      PortableServer::POAManager_var rootManagerVar =
        rootPoaVar->the_POAManager ();
      rootManagerVar->activate ();

      // Create/activate the mortgage CORBA objects.
      PortableServer::ServantBase_var owner_impl1 =
        createMortgage (registerVar.in (), ACE_TEXT ("LloydsTSB"), 5.9f);
      PortableServer::ServantBase_var owner_impl2 =
        createMortgage (registerVar.in (), ACE_TEXT ("Halifax"), 6.0f);
      PortableServer::ServantBase_var owner_impl3 =
        createMortgage (registerVar.in (), ACE_TEXT ("ABBEY"), 4.5f);
      PortableServer::ServantBase_var owner_impl4 =
        createMortgage (registerVar.in (), ACE_TEXT ("NationWide"), 4.0f);
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Successfully exported mortgage offers\n")));

      // Wait for requests.
      ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Waiting for incoming requests\n")));
      orbVar->run ();

      // Complete.
      orbVar->destroy ();
    }
  catch (const CORBA::Exception &ex)
    {
      ex._tao_print_exception (ACE_TEXT ("ERROR: Exception in MortgageServer.cpp:"));
      return 1;
    }

  return 0;
}
