/*******************************************************************************
 * Copyright (c) 2012 - 2014 AIT, fortiss GmbH
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Filip Andren, Alois Zoitl - initial API and implementation and/or initial documentation
 *******************************************************************************/
#include "opceventhandler.h"
#include "../core/devexec.h"
#include <commfb.h>

#include <ObjBase.h>

DEFINE_HANDLER(COpcEventHandler);

COpcEventHandler::TCallbackDescriptor COpcEventHandler::m_nCallbackDescCount = 0;

COpcEventHandler::COpcEventHandler(CDeviceExecution& pa_poDeviceExecution) : CExternalEventHandler(pa_poDeviceExecution)  {
  this->start();
  // Sleep to allow new thread to start
  CThread::sleep(100);
}

COpcEventHandler::~COpcEventHandler(){
  this->end();
}

void COpcEventHandler::sendCommand(ICmd *pa_pCmd){
  m_oSync.lock();
  m_lCommandQueue.pushBack(pa_pCmd);
  m_oSync.unlock();
}

void COpcEventHandler::run(){
  HRESULT result = CoInitializeEx(NULL, COINIT_MULTITHREADED);

  if(result == S_OK){
    while(isAlive()){
      ICmd* nextCommand = getNextCommand();
      if(nextCommand != NULL) {
        nextCommand->runCommand();
      }

      MSG msg;
      while(PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
    }
  }

  CoUninitialize();
}

COpcEventHandler::TCallbackDescriptor COpcEventHandler::addComCallback(forte::com_infra::CComLayer* pa_pComCallback){
  m_oSync.lock();
  m_nCallbackDescCount++;
  TComContainer stNewNode = { m_nCallbackDescCount, pa_pComCallback };
  m_lstComCallbacks.pushBack(stNewNode);
  m_oSync.unlock();

  return m_nCallbackDescCount;
}

void COpcEventHandler::removeComCallback(COpcEventHandler::TCallbackDescriptor pa_nCallbackDesc){
  m_oSync.lock();

  TCallbackList::Iterator itRunner(m_lstComCallbacks.begin());

  if(itRunner->m_nCallbackDesc == pa_nCallbackDesc){
    m_lstComCallbacks.popFront();
  }
  else{
    TCallbackList::Iterator itLastPos(itRunner);
    TCallbackList::Iterator itEnd(m_lstComCallbacks.end());
    ++itRunner;
    while(itRunner != itEnd){
      if(itRunner->m_nCallbackDesc == pa_nCallbackDesc){
        m_lstComCallbacks.eraseAfter(itLastPos);
        break;
      }
      itLastPos = itRunner;
      ++itRunner;
    }
  }
  m_oSync.unlock();
}

void COpcEventHandler::executeComCallback(COpcEventHandler::TCallbackDescriptor pa_nCallbackDesc){
  m_oSync.lock();
  TCallbackList::Iterator itEnd(m_lstComCallbacks.end());
  for(TCallbackList::Iterator itCallback = m_lstComCallbacks.begin(); itCallback != itEnd; ++itCallback){
    if(itCallback->m_nCallbackDesc == pa_nCallbackDesc){
      //FIX
      TComContainer comCon = (*itCallback);
      m_oSync.unlock();
      if(forte::com_infra::e_Nothing != comCon.m_pCallback->recvData(0,0)){
        startNewEventChain(comCon.m_pCallback->getCommFB());
      }
      m_oSync.lock();
      break;
    }
  }
  m_oSync.unlock();
}

ICmd* COpcEventHandler::getNextCommand(){
  ICmd* command;

  m_oSync.lock();
  TCommandQueue::Iterator itBegin = m_lCommandQueue.begin();
  if(itBegin != m_lCommandQueue.end()){
    command = (*itBegin);
    m_lCommandQueue.popFront();
  } else {
    command = NULL;
  }
  m_oSync.unlock();

  return command;
}
