#include "UpperTestAdapter.h"
#include <utility>
#include <iostream>

#include "Logger/Logger.h"

#include "Processors/Processor.h"

using namespace t3devlib;
using namespace boost;
using namespace std;

namespace UpperTestAdapter {

using namespace Processors;

UpperTestAdapter::UpperTestAdapter()
{
  std::clog << "UpperTestAdapter ctor() not implemented" << endl;
}

UpperTestAdapter::~UpperTestAdapter()
{
  stringstream ss;
  Logger::Instance().LogDebug("UpperTestAdapter dtor()");
  
  // Delete not umapped ports.
  for (map<const ComponentId *, pair<PortId *, shared_ptr<ProcessorsImpl> > >::iterator it = m_mapRegisteredPorts.begin(); it != m_mapRegisteredPorts.end(); ++it)
  {
    ss.str("");
    ss << "UpperTestAdapter::~UpperTestAdapter: " << it->first;
    Logger::Instance().LogInfo(ss.str());
    (it->second).second.reset();
  }
  Logger::Instance().LogInfo("UpperTestAdapter::~UpperTestAdapter");
  m_connectedComponents.clear();
  m_mapRegisteredPorts.clear();
}

bool UpperTestAdapter::Initialize(boost::shared_ptr<GeneralConfigurationParams> configParams) throw(UpperTestAdapterException)
{
  stringstream ss;
  Logger::Instance().LogDebug("UpperTestAdapter::Initialize");
  
  TriggerCommandsStrategy::Strategies strategy = TriggerCommandsStrategy::UEError;
  if (configParams.get()->Mode() == ECaptureMode_Live)
  {
    Logger::Instance().LogDebug("UpperTestAdapter::Initialize: ECaptureMode_Live");
    strategy = TriggerCommandsStrategy::UEUserGuide;
    // Process te list of the EUT interfaces
    if (configParams.get()->EutIpInterfaceList().size() == 0)
    {
      Logger::Instance().LogError("UpperTestAdapter::Initialize: Invalid Euts Ip Interface desciption");
      throw UpperTestAdapterException("UpperTestAdapter::Initialize: Invalid Euts Ip Interface desciption");
    }
  }
  else if (configParams.get()->Mode() == ECaptureMode_Offline)
  {
    Logger::Instance().LogDebug("UpperTestAdapter::Initialize: ECaptureMode_Offline");
    strategy = TriggerCommandsStrategy::UEOffLine;
  }
  else
  {
    Logger::Instance().LogError("UpperTestAdapter::Initialize: Undefined strategy");
    throw UpperTestAdapterException("UpperTestAdapter::Initialize: Undefined strategy");
  }
  // Set communication strategy.
  ss.str("");
  ss << "UpperTestAdapter::Initialize: m_mapRegisteredPorts size=" << m_mapRegisteredPorts.size();
  Logger::Instance().LogDebug(ss.str());
  ss.str("");
  ss << "UpperTestAdapter::Initialize: EutIpInterfaceList=" << configParams.get()->EutIpInterfaceList().size();
  Logger::Instance().LogDebug(ss.str());
  if (m_mapRegisteredPorts.size() != 0)
  {
    vector<shared_ptr<EutIPInterface> >::iterator eutit = configParams.get()->EutIpInterfaceList().begin();
    if (eutit != configParams.get()->EutIpInterfaceList().end())
    {
      list<const ComponentId *>::iterator componentItem = m_connectedComponents.begin();
      for ( ; componentItem != m_connectedComponents.end(); ++componentItem)
      {
        // Find component into the registerd components map.
        map<const ComponentId *, pair<PortId *, shared_ptr<ProcessorsImpl> > >::iterator it = m_mapRegisteredPorts.find(*componentItem);
        if (it != m_mapRegisteredPorts.end())
        {
          // Commponent was found.
          ss.str("");
          ss << "UpperTestAdapter::Initialize: Process key =" << it->first << " - value = " << (it->second).first << "/" << (it->second).second.get();
          Logger::Instance().LogDebug(ss.str());
          // Set the strategy to apply
          (it->second).second.get()->SetImplMode(strategy);
          // Process the list of the EUT interfaces
          /*
           * Problem: I'm unable to retrieve the TTCN-3 name of the eaPort ("User A" and "User B"), worth, these port could be unnamed.
           * As work-around I'll use the index, but the order should be respected in eutInfoList and mapping prot operations
          string str = ((PortId *)it->second.first)->GetName();
          clog << "UpperTestAdapter::Initialize: Port" << str << " - " << *((PortId *)it->second.first) << " - " << ((PortId *)it->second.first)->GetIndex() << endl;
          clog << "UpperTestAdapter::Initialize: Component" <<  *(it->first) << " - " << *((it->first)->GetTriComponentId()) << endl;
          if (eutIpInterfaceList.find(str) != eutIpInterfaceList.end())
          {
            (it->second).second.get()->SetEutIpInterface(eutIpInterfaceList[str]);
          }
          else
          {
            Logger::Instance().LogError("UpperTestAdapter::Initialize: EUT IP interface not registered");
            throw UpperTestAdapterException("UpperTestAdapter::Initialize: EUT IP interface not registered");
          }*/
          (it->second).second.get()->SetEutIpInterface(*eutit);
        }
        else
        {
          // Commponent was not found.
          ss.str("");
          ss << "UpperTestAdapter::Initialize: Key =" << *componentItem << " not found";
          Logger::Instance().LogError(ss.str());
        }
        eutit++;
      } // 'for' loop.
      
      m_connectedComponents.clear();
    }
    else
    {
      Logger::Instance().LogInfo("UpperTestAdapter::Initialize: no EUT interface provided.");
      map<const ComponentId *, pair<PortId *, shared_ptr<ProcessorsImpl> > >::iterator it = m_mapRegisteredPorts.begin();
      for ( ; it != m_mapRegisteredPorts.end(); ++it)
      {
        ss.str("");
        ss << "UpperTestAdapter::Initialize: Process key =" << it->first << " - value = " << (it->second).first << "/" << (it->second).second.get();
        Logger::Instance().LogDebug(ss.str());
        // Set the strategy to apply
        (it->second).second.get()->SetImplMode(strategy);
      }
    }
  }
  else
  {
    Logger::Instance().LogInfo("UpperTestAdapter::Initialize: no EUT port registered.");
  }
  
  return true;
}

void UpperTestAdapter::RegisterEquipmentAccessPort(PortId *pPort, const ComponentId *pComponent)
{
  stringstream ss;
  ss << "UpperTestAdapter::RegisterEquipmentAccessPort: " << pComponent << "/" << pPort;
  Logger::Instance().LogInfo(ss.str());

  // Check if port is already registered.
  map<const ComponentId *, pair<PortId *, shared_ptr<ProcessorsImpl> > >::iterator it = m_mapRegisteredPorts.find(pComponent);
  if(it == m_mapRegisteredPorts.end())
  {
    // No, register it.
    shared_ptr<ProcessorsImpl> ptr = shared_ptr<ProcessorsImpl>(new Processor);
    m_mapRegisteredPorts.insert(
        make_pair<const ComponentId *, pair<PortId *, shared_ptr<ProcessorsImpl> > >(
          pComponent,
          pair<PortId *, shared_ptr<ProcessorsImpl> >
            (pPort, ptr)));
    ss.str("");
    ss << "UpperTestAdapter::RegisterEquipmentAccessPort: insert key=" << *pComponent << " - value = " << pPort << "/" << ptr;
    Logger::Instance().LogDebug(ss.str());
    // Add it into a temporary vector.
    m_connectedComponents.push_back(pComponent);
  }
  else
  {
    // Yes, override it.
    ss.str("");
    ss << "UpperTestAdapter::RegisterEquipmentAccessPort: overriding registration port.";
    Logger::Instance().LogError(ss.str());

    m_mapRegisteredPorts[pComponent] = pair<PortId *, shared_ptr<ProcessorsImpl> >(pPort, (it->second).second);
  }
}

PortId * UpperTestAdapter::GetEquipmentAccessPortIdFromComponent(const ComponentId& from)
{
  stringstream ss;
  ss << "UpperTestAdapter::GetEquipmentAccessPortIdFromComponent: " << from << " - " << "UpperTestAdapter::GetEquipmentAccessPortIdFromComponent: " << &from;
  Logger::Instance().LogInfo(ss.str());

  map<const ComponentId *, pair<PortId *, shared_ptr<ProcessorsImpl> > >::iterator it = m_mapRegisteredPorts.find(&from);
  if(it != m_mapRegisteredPorts.end())
  {
    ss.str("");
    ss << "UpperTestAdapter::GetEquipmentAccessPortIdFromComponent: " << (it->second).first << " - " << *(it->second).first;
    Logger::Instance().LogDebug(ss.str());
    return (it->second).first;
  }
  
  ss.str("");
  ss << "UpperTestAdapter::GetEquipmentAccessPortIdFromComponent: Component not found - " << from << endl << "UpperTestAdapter::GetEquipmentAccessPortIdFromComponent: " << &from << endl;
  Logger::Instance().LogError(ss.str());
  return NULL;
}

int UpperTestAdapter::ProcessTriggerCommand(const ComponentId &from, const string &trigger, const vector<string> &parameterList)
{
  stringstream ss;
  ss << "UpperTestAdapter::ProcessTriggerCommand: " << from << " - " << trigger;
  Logger::Instance().LogInfo(ss.str());
  ss.str("");
  ss << "UpperTestAdapter::ProcessTriggerCommand: " << &from;
  Logger::Instance().LogDebug(ss.str());

  // Sanity check: Find component into the map.
  map<const ComponentId *, pair<PortId *, shared_ptr<ProcessorsImpl> > >::iterator it = m_mapRegisteredPorts.find(&from);
  if(it == m_mapRegisteredPorts.end())
  {
    ss.str("");
    ss << "UpperTestAdapter::ProcessTriggerCommand: component " << from << " not found.";
    Logger::Instance().LogError(ss.str());
    return -1;
  }

  // Retrieve the message processor instance.
  ss.str("");
  ss << "UpperTestAdapter::ProcessTriggerCommand: key found: " << *(it->second).first << " - " << (it->second).second.get();
  Logger::Instance().LogInfo(ss.str());
  ProcessorsImpl *processor = (it->second).second.get();
  
  // Execute the TriggerCommand message.
#ifndef __THREADED_MODE__
  ss.str("");
  ss << "UpperTestAdapter::ProcessTriggerCommand: Call Process for " << trigger;
  Logger::Instance().LogDebug(ss.str());
  int result = processor->Process(trigger, parameterList);
  ss.str("");
  ss << "UpperTestAdapter::RegisterEutConfigPort: return code: " << result;
  Logger::Instance().LogError(ss.str());
  return result;
#else // __THREADED_MODE__
  ss.str("");
  ss << "UpperTestAdapter::ProcessTriggerCommand: Call BeginProcess for " << trigger;
  Logger::Instance().LogDebug(ss.str());
  if (!processor->BeginProcess(trigger, parameterList))
  {
    ss.str("");
    ss << "UpperTestAdapter::RegisterEutConfigPort: Failed to process the message " << trigger << endl;
    Logger::Instance().LogError(ss.str());
    return -1;
  }
  
  // Wait for the execution terminated.
  Logger::Instance().LogDebug("UpperTestAdapter::ProcessTriggerCommand: Before Wait");
  processor->Wait();
  Logger::Instance().LogDebug("UpperTestAdapter::ProcessTriggerCommand: After Wait");
#endif // __THREADED_MODE__

  return result;
}

} // namespace

