#include "Dispatcher.h"
#include "DispatcherFactory.h"
#include "DissectorFactory.h"
#include "Logger/Logger.h"

Dispatcher::Dispatcher(std::string sName) 
  :m_sName(sName) {
}

Dispatcher::~Dispatcher() {
}

const std::string& Dispatcher::GetName() const {
  return m_sName;
}

bool Dispatcher::Dispatch(DispatchInfo & info, ComponentFilter & filter) {

  // Dissect packet
  Dissector * pDissector = DissectorFactory::Instance().Create(GetName());
  
  if(!pDissector) {
    Logger::Instance().LogError("Unable to dispatch packet: No dissector for " + GetName());
    
    // Return what we have so far
    return false; 
  }

  DispatchInfo * pUnprocessed = 0;
  ComponentFilter * pUnprocessedFilter = 0;
  if (pDissector->CreatesUnprocessedData())
  {
	  // create copies (for handling unprocessed data)
	  pUnprocessed = new DispatchInfo();
	  ProtocolInfo * pProtocolInfo = info.GetProtocolInfo();
	  if (pProtocolInfo)
	  {
		int nCount = pProtocolInfo->Count();
		for (int i = 0; i < nCount; i++)
			pUnprocessed->AddProtocolInfo((*pProtocolInfo)[i]->Clone());
	  }
	  pUnprocessedFilter = new ComponentFilter(filter);
  }

  if (!pDissector->Dissect(info.GetData(), info.GetDataSize())) // no information for higher levels
  {
	  delete pDissector;
	  delete pUnprocessed;
	  delete pUnprocessedFilter;
	  return false;
  }

  // Reassembly
  bool bRes = true;
  if(pDissector->NeedReassembly()) {
    ProtocolInfo * pProtocolInfo = info.GetProtocolInfo();
    std::list<Dissector *>::iterator it; 
    for(it = m_fragments.begin(); (it != m_fragments.end()) && 
		!((*it)->Reassemble(pDissector, pProtocolInfo)); ++it);
    if(it == m_fragments.end()) {
      if (!pDissector->FinishDissection())
	  {
	    // Fragment belongs to a new packet
		info.DetachProtocolInfo();
		pDissector->SetProtocolInfo(pProtocolInfo);
		m_fragments.push_front(pDissector);
		pDissector = 0;
	  }
	  // otherwise the fragment has been assembled
    }
    else {
      // Fragment has been assembled. Pending packet may be complete
      if(!(*it)->NeedReassembly()) {
        pDissector = (*it);
        m_fragments.erase(it);
      }
      else {
	pDissector = 0;
      }
    }
    if (pDissector && !pDissector->IsPacketValid()) {
      delete pDissector;
      pDissector = 0;
      bRes = false;
    }
  }
  
  if(!pDissector) {
    // Not complete packet or error...
	delete pUnprocessed;
	delete pUnprocessedFilter;
    return bRes;
  }

  pDissector->SaveResult(info);
  // Check filter
  ProtocolInfoElement * pProtocol = pDissector->GetLayerInfo();
  if (pProtocol)
  {
	  if (!filter.Match(pProtocol))
	  {
		  Logger::Instance().LogDebug("Message discarded by filter");
		  delete pDissector;
		  delete pUnprocessed;
	      delete pUnprocessedFilter;
		  return false;
	  }
	  filter.MoveToNextLayer();
  }

  // Transfer to upper layer
  const EProtocolType upperLayerType = pDissector->GetUpperLayerType();
  
  bRes = false;
  if (upperLayerType == EProtocolType_None) // all headers have been removed
  {
	  filter.EnqueueMessage(info.GetData(), info.GetDataSize());
	  std::stringstream ss;
	  ss << "Captured message enqueued to the TE (len " << info.GetDataSize() << ")";
      Logger::Instance().LogInfo(ss.str());
	  bRes = true;
  }
  else
  {
	Dispatcher * pUpperLayer = DispatcherFactory::Instance().Get(upperLayerType);
	if(pUpperLayer)
		bRes = pUpperLayer->Dispatch(info, filter);
  }
  if (bRes && pDissector->ContainsUnprocessedData())
  {
	std::pair<const unsigned char *, ssize_t> res = pDissector->GetUnprocessedData();
	pUnprocessed->SetData(res.first, res.second);
    bRes = Dispatch(*pUnprocessed, *pUnprocessedFilter);
  }

  delete pDissector;
  delete pUnprocessed;
  delete pUnprocessedFilter;
  return bRes;
}

void Dispatcher::AddExplicitUpperLayer(EProtocolType upperLayer/*, Filter */) {
  Dispatcher * pUpperLayer = DispatcherFactory::Instance().Get(upperLayer);
  if(pUpperLayer) { 
    // TODO: store Upper Layer Filter
    m_upperLayers.push_back(pUpperLayer);
  }
  else {
    Logger::Instance().LogWarning("No dispatcher for requested upper layer");
  }
}
