#include <rtt/Logger.hpp>
#include <rtt/transports/corba/TaskContextProxy.hpp>
#include <rtt/transports/corba/TaskContextServer.hpp>
#include <rtt/transports/corba/CorbaTemplateProtocol.hpp>
#include <rtt/typekit/StdTypeInfo.hpp>

#include <rtt/types/TypekitRepository.hpp>
#include <rtt/types/TypeInfoRepository.hpp>
#include <rtt/typekit/RealTimeTypekit.hpp>

#include <rtt/types/TransportPlugin.hpp>

#include <rtt/TaskContext.hpp>
#include <rtt/Activity.hpp>
#include <rtt/Port.hpp>

// CorbaLibPlugin does not seem to be declared nor loaded
class CorbaFallBackProtocol
  : public RTT::corba::CorbaTypeTransporter
{
public:
  CorbaFallBackProtocol() {}
  virtual CORBA::Any* createAny(RTT::base::DataSourceBase::shared_ptr source) const { return new CORBA::Any(); }
  virtual bool updateAny( RTT::base::DataSourceBase::shared_ptr source, CORBA::Any& any) const { return false; }
  virtual RTT::base::DataSourceBase::shared_ptr createDataSource(const CORBA::Any* any) const { return RTT::base::DataSourceBase::shared_ptr(); }
  virtual bool updateFromAny(const CORBA::Any* blob, RTT::base::DataSourceBase::shared_ptr target) const { return false; }
  virtual RTT::base::ChannelElementBase* createStream(RTT::base::PortInterface* port, const RTT::ConnPolicy& policy, bool is_sender) const { return 0; }
  virtual RTT::base::ChannelElementBase* buildDataStorage(RTT::ConnPolicy const& policy) const { return 0; }
  virtual RTT::corba::CRemoteChannelElement_i* createChannelElement_i(RTT::interface::DataFlowInterface*, ::PortableServer::POA* poa, bool) const { return 0; }
  virtual RTT::base::ChannelElementBase* buildChannelOutput(RTT::base::InputPortInterface& port, RTT::ConnPolicy const& policy) const { return 0; }
  virtual RTT::base::ChannelElementBase* buildChannelInput(RTT::base::OutputPortInterface& port, RTT::ConnPolicy const& policy) const { return 0; }
  virtual RTT::base::DataSourceBase::shared_ptr createPropertyDataSource(RTT::corba::CServiceProvider_ptr serv, const std::string& vname) { 
    CORBA::String_var tname = serv->getPropertyTypeName( CORBA::string_dup(vname.c_str()));
    return RTT::base::DataSourceBase::shared_ptr( );
  }
  virtual RTT::base::DataSourceBase::shared_ptr createAttributeDataSource(RTT::corba::CServiceProvider_ptr serv, const std::string& vname) {
    CORBA::String_var tname = serv->getAttributeTypeName( CORBA::string_dup( vname.c_str()));
    return RTT::base::DataSourceBase::shared_ptr( );
  }
};

struct CorbaLibPlugin: public RTT::types::TransportPlugin
{
  bool registerTransport(std::string name, RTT::types::TypeInfo* ti)
  {
    assert( name == ti->getTypeName() );
    if ( name == "string" )
      return ti->addProtocol(ORO_CORBA_PROTOCOL_ID, new RTT::corba::CorbaTemplateProtocol<std::string>() );
    if ( name == "void" )
      return ti->addProtocol(ORO_CORBA_PROTOCOL_ID, new CorbaFallBackProtocol());
    return false;
  }
  std::string getTransportName() const { return "CORBA"; }
  std::string getTypekitName() const { return "rtt-types"; }
  std::string getName() const { return "rtt-corba-types"; }
};

class Test : public RTT::TaskContext{
protected:
  bool svr;
  RTT::Activity act;

  void ReadConstRefStr(const std::string& message) {
    std::cout << "pass by reference : " << message << std::endl;
  }
  
  void ReadStr(std::string message) {
    std::cout << "pass by value : " << message << std::endl;
  }

public:
  Test::Test(const std::string& name, double ts = 1)
    : TaskContext(name) 
    , svr(false)
    , act(RTT::os::IncreasePriority, ts) // 10 ms
  {
    this->setActivity(&act);
    this->addOperation("ReadConstRefStr", &Test::ReadConstRefStr, this, RTT::OwnThread).arg("string", "new message");
    this->addOperation("ReadStr", &Test::ReadStr, this, RTT::OwnThread).arg("string", "new message");
  };

  void server() { svr = true;}

  virtual void updateHook() {
    // check keyboard
    if(!svr) {
      std::string test = "test";

      RTT::Method<void(const std::string&)> SendConstRefStr = this->getPeer(this->getPeerList()[0])->getOperation("ReadConstRefStr");
      SendConstRefStr(test); // send to remote user
      RTT::Method<void(std::string)> SendStr = this->getPeer(this->getPeerList()[0])->getOperation("ReadStr");
      SendStr(test); // send to remote user
    }
  }
};

int main(int argc, char* argv[]) {
  if (argc < 2)
  {
    cout << "Usage : " << argv[0] << " name" << endl;
    exit(1);
  }

  RTT::types::RealTimeTypekitPlugin plugin;
  RTT::types::TypekitRepository::Import(&plugin);
  CorbaLibPlugin cplugin;
  RTT::types::TypekitRepository::Import(&cplugin);

  std::string name = argv[1];
  
  Test test(name);

  RTT::corba::TaskContextServer::InitOrb(argc, argv);

  if(name == "server") {
    test.server();
    // Setup Corba
    RTT::corba::TaskContextServer::Create(&test);
    RTT::corba::TaskContextServer::RunOrb();

  } else {
    RTT::corba::TaskContextServer::ThreadOrb();

    // Create proxy
    bool is_ior = false;
    RTT::corba::TaskContextProxy* mtask = RTT::corba::TaskContextProxy::Create("server" , is_ior);
    mtask->configure();

    RTT::connectPeers(&test, mtask);
    mtask->start();
  }

  test.start();
  while(true) sleep(5); // orocos sleep : 5s

	return 0;
}