/* SECU-3  - An open source, free engine control unit
   Copyright (C) 2007 Alexey A. Shabelnikov. Ukraine, Kiev

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.

   contacts:
              http://secu-3.org
              email: shabelnikov@secu-3.org
*/

/** \file ControlAppAdapter.cpp
 * \author Alexey A. Shabelnikov
 */

#include "stdafx.h"
#include "ControlAppAdapter.h"

#define WM_THREAD_ON_CONNECTON        (WM_USER + 1)
#define WM_THREAD_ON_PACKET_RECEIVED  (WM_USER + 2)

//////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CControlAppAdapter,CWnd)
 ON_MESSAGE(WM_THREAD_ON_CONNECTON, msgOnConnection)
 ON_MESSAGE(WM_THREAD_ON_PACKET_RECEIVED, msgOnPacketReceived)
END_MESSAGE_MAP()

//////////////////////////////////////////////////////////////////////
CControlAppAdapter::CControlAppAdapter()
: m_switch_on(true)
, m_switch_on_thread_side(false)
{
 //empty
}

CControlAppAdapter::~CControlAppAdapter()
{
 //empty
}

BOOL CControlAppAdapter::Create(CWnd* pParentWnd)
{
 ASSERT(pParentWnd);
 if (::IsWindow(m_hWnd))
  return TRUE; //already created - do nothing
 return CWnd::Create(NULL,_T("CControlApp_Adapter_Wnd"),0,CRect(0,0,0,0),pParentWnd,0);
}

//////////////////////Thread side/////////////////////////////////////
void CControlAppAdapter::OnPacketReceived(const BYTE i_descriptor, SECU3IO::SECU3Packet* ip_packet)
{
 if (IsWindow(m_hWnd) && m_switch_on_thread_side)
 {
  PostMessage(WM_THREAD_ON_PACKET_RECEIVED,i_descriptor,(LPARAM)ip_packet);
  Sleep(0); //<-- This is because we have to prevent dead locks (deadlock appears at high data rate)
 }
}

void CControlAppAdapter::OnConnection(const bool i_online)
{
 if (IsWindow(m_hWnd) && m_switch_on_thread_side)
  PostMessage(WM_THREAD_ON_CONNECTON,i_online,0);
}

/////////////////////Application side/////////////////////////////////
LRESULT CControlAppAdapter::msgOnConnection(WPARAM wParam, LPARAM lParam)
{
 //We should not spam receiver with packets when he is unsubscribed, at the time when he is unsubscribed some packets may still remain in the queue.
 if (!m_switch_on_thread_side)
  return 0;

 ObserversIterator it;
 for(it = m_observers.begin(); it != m_observers.end(); ++it)
 {
  IAPPEventHandler* p_observer = (*it).second;
  _ASSERTE(p_observer);
  p_observer->OnConnection(wParam ? true : false);
 }
 return 0;
}

LRESULT CControlAppAdapter::msgOnPacketReceived(WPARAM wParam, LPARAM lParam)
{
 if (!m_switch_on_thread_side)
  return 0;

 //WARNING! object is in stack!
 SECU3IO::SECU3Packet recepted_packet;

 ////////////////////////////////////////////////////////////////////////
 //exclusive access, copy data, then free access to resource
 EnterCriticalSection();
 memcpy(&recepted_packet,(SECU3IO::SECU3Packet*)lParam,sizeof(SECU3IO::SECU3Packet));
 LeaveCriticalSection();
 ////////////////////////////////////////////////////////////////////////

 ObserversIterator it;
 for(it = m_observers.begin(); it != m_observers.end(); ++it)
 {
  IAPPEventHandler* p_observer = (*it).second;
  _ASSERTE(p_observer);
  p_observer->OnPacketReceived((BYTE)wParam, &recepted_packet);
 }
 return 0;
}

//////////////////////////////////////////////////////////////////////
bool CControlAppAdapter::AddEventHandler(IAPPEventHandler* i_pEventHandler, const _TSTRING &i_observer_key)
{
 m_observers[i_observer_key] = i_pEventHandler;
 _UpdateInternalState();
 return true;
}

bool CControlAppAdapter::RemoveEventHandler(const _TSTRING &i_observer_key)
{
 if (m_observers.find(i_observer_key)==m_observers.end())
  return false; //invalid key
 m_observers.erase(i_observer_key);
 _UpdateInternalState();
 return true;
}

void CControlAppAdapter::SwitchOn(bool state)
{
 m_switch_on = state;
 _UpdateInternalState();
}

void CControlAppAdapter::_UpdateInternalState(void)
{
 m_switch_on_thread_side = m_switch_on && m_observers.size() > 0;
}
