/*
 * Copyright (C) 2009 - 2010 Funambol, Inc.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * 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 Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
 * 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * "Powered by Funambol" logo. If the display of the logo is not reasonably
 * feasible for technical reasons, the Appropriate Legal Notices must display
 * the words "Powered by Funambol".
 */


#include "ClientAdapterProxy.h"

#include "Logger/Logger.h"
#include "IFIFOWrapper.h"
#include "Message.h"
#include "MessageSerializer.h"
#include "MessageDeserializer.h"
#include "ManagementObjects/DevInfo.h"
#include "ManagementObjects/DevDetail.h"
#include "ManagementObjects/DMAcc.h"
#include "ManagementObjects/WiMAX.h"
#include "ManagementObjects/WiMAXSupp.h"
#include "ManagementObjects/WiMAX_Diagnostics.h"
#include "common/Buffer.h"
#include "CommonUtils.h"
#include "ClientAdapter/INotificationUpdater.h"
#include "ThreadHelper.h"
#include "CritSection.h"
#include "lock.h"
#include "Event.h"
#include "Utils.h"


static const char PARAMS_DELIMITER[] = "\n$DELIIM$\n";

namespace NS_DM_Client
{
	const int S_maxWaitForThreadFinished = 100000;

	NotificationStatus GetNotificationStatus(const String& strStatus)
	{
		if (strStatus == S_notificationStatusDescription[e_started])
			return e_started;
		if (strStatus == S_notificationStatusDescription[e_finished])
			return e_finished;
		if (strStatus == S_notificationStatusDescription[e_aborted])
			return e_aborted;
		return e_unknown;
	}

	//-------------------------------------------------------------------------------------------

	ClientAdapterProxy::ClientAdapterProxy(): m_stopRunning(false), m_inFIFOWrapper(0), m_outFIFOWrapper(0), m_logger(NS_Logging::GetLogger("ClientAdapter")),
		m_isOpened(false), m_receiver(0), m_lastMsgId(0)
	{
		CreateFIFOWrapperEx(m_inFIFOWrapper, S_inFifoName, true, false, false);
		CreateFIFOWrapperEx(m_outFIFOWrapper, S_outFifoName, false, false, false);
	}
	//-------------------------------------------------------------------------------------------

	ClientAdapterProxy::~ClientAdapterProxy()
	{
		m_stopRunning = true;
		if (m_receiver)
		{
			m_receiver->wait(S_maxWaitForThreadFinished);
			delete m_receiver;
		}
		m_inFIFOWrapper->Release();
		m_outFIFOWrapper->Release();
	}
	//-------------------------------------------------------------------------------------------

	ClientAdapterProxy::MessageId ClientAdapterProxy::getUniqueId(MessageId& mid)
	{
		NS_Common::Lock lock(m_mapMutex);

		// next ID - most dynamic part
		++m_lastMsgId;
		m_lastMsgId &= 0xFF;

		time_t t = 0;
		t = time(&t);
		
		// combine ID
		//
		mid = t & 0xFFFFFF; // 3 least significant bytes 

		mid <<= 8;
		mid |= m_lastMsgId; //  and dynamic part

		return mid;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::StartDMSession(INotificationUpdater* updater)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_startDMSession, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
		return executeRequestWithNotification(request, updater);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SubscribeToDMSessionNotif(INotificationUpdater* updater)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_notifyDMSession, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
		return executeRequestWithNotification(request, updater);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::CheckForFirmwareUpdate(INotificationUpdater* updater)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_checkFirmwareUpdate, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
		return executeRequestWithNotification(request, updater);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SubscribeToFirmwareUpdateNotif(INotificationUpdater* updater)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_notifyFirmwareUpdate, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
		return executeRequestWithNotification(request, updater);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SubscribeToProvisioningUpdateNotif(INotificationUpdater* updater)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_notifyProvisioningUpdate, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
		return executeRequestWithNotification(request, updater);
	}
	//-------------------------------------------------------------------------------------------
	bool ClientAdapterProxy::SubscribeToDRMDNotif(INotificationUpdater* updater)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_collectDRMD, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
		return executeRequestWithNotification(request, updater);
	}

	bool ClientAdapterProxy::UnsubscribeToDMSessionNotif(INotificationUpdater* updater)
	{
		return updater != 0;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::UnsubscribeToFirmwareUpdateNotif(INotificationUpdater* updater)
	{
		return updater != 0;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::UnsubscribeToProvisioningUpdateNotif(INotificationUpdater* updater)
	{
		return updater != 0;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::executeRequestWithNotification(const Message& request, INotificationUpdater* updater)
	{
		if (!m_isOpened)
		{
			LOG_ERROR_(m_logger, "Failed to send any messages before \"open\" request");
			return false;
		}
		// add message to synchronous queue
		NS_Common::EventEx notifyResponse;
		Message response;
		addMessageToQueue(request.m_id, response, notifyResponse);
		// add to asynchronous queue with the same id
		addMessageToQueue(request.m_id, updater);

		bool res = sendRequest(request);
		if (res)
		{
			LOG_(m_logger, "Waiting for event response notification...");
			notifyResponse.wait();
			LOG_(m_logger, "Received event response notification");
			res = handleStatusResponse(response);
		}
		else
		{
			LOG_(m_logger, "Failed to send request with notification");
		}

		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::Open()
	{
		if (m_isOpened)
		{
			LOG_WARNING_(m_logger, "Client Adapter is opened already");
			return true;
		}
		if (!m_receiver)
		{
			m_receiver = new ResponseReceiverThread(*this);
			m_receiver->start();
		}

		MessageId mid = 0;
		Message request(getUniqueId(mid), e_open, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);

		NS_Common::EventEx notifyResponse;
		Message response;
		addMessageToQueue(request.m_id, response, notifyResponse);

		bool res = sendRequest(request);
		if (res)
		{
			LOG_(m_logger, "Waiting for event response notification...");
			notifyResponse.wait();
			LOG_(m_logger, "Received event response notification");
			m_isOpened = handleStatusResponse(response);
		}
		else
		{
			LOG_(m_logger, "Failed to send open request");
		}
		return m_isOpened;

	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::Close()
	{
		if (!m_isOpened)
		{
			LOG_WARNING_(m_logger, "Failed to send any messages before \"open\" request");
			return true;
		}

		MessageId mid = 0;
		Message request(getUniqueId(mid), e_close, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);

		NS_Common::EventEx notifyResponse;
		Message response;
		addMessageToQueue(request.m_id, response, notifyResponse);

		bool res = sendRequest(request);
		if (res)
		{
			LOG_(m_logger, "Waiting for event response notification...");
			notifyResponse.wait();
			LOG_(m_logger, "Received event response notification");
			res = handleStatusResponse(response);
			m_isOpened = !res;
		}
		m_stopRunning = res;
		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::GetDeviceInfo(DevInfo& deviceInfo)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_get, e_devInfo);
		return executeGetRequest(&deviceInfo, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SetDeviceInfo(const DevInfo& deviceInfo)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, e_devInfo);
		return executeSetRequest(&deviceInfo, request);
	}
	//-------------------------------------------------------------------------------------------

	void ClientAdapterProxy::addMessageToQueue(MessageId msgId, Message& response, NS_Common::EventEx& notifyResponse)
	{
		NS_Common::Lock lock(m_mapMutex);
		m_synchMessages[msgId] = std::make_pair(&response, &notifyResponse);
		LOG_(m_logger, "Message with id = %d was added to synchQueue", msgId);
	}
	//-------------------------------------------------------------------------------------------

	void ClientAdapterProxy::addMessageToQueue(MessageId msgId, INotificationUpdater* updater)
	{
		NS_Common::Lock lock(m_mapMutex);
		m_asynchMessages[msgId] = updater;
		LOG_(m_logger, "Message with id = %d was added to asynchQueue", msgId);
	}
	//-------------------------------------------------------------------------------------------

	void ClientAdapterProxy::deleteMessageFromQueue(MessageId msgId)
	{
		NS_Common::Lock lock(m_mapMutex);
		if (isMessageSynchronous(msgId))
		{
			// delete message from synchronous queue
			m_synchMessages.erase(msgId);
		}
		else
		{
			AsynchMessages::iterator found = m_asynchMessages.find(msgId);
			if (found == m_asynchMessages.end())
			{
				LOG_ERROR_(m_logger, "Message with id = %d was doesn't exist", msgId);
				return;
			}
			m_asynchMessages.erase(found);
		}
		LOG_(m_logger, "Message with id = %d was deleted from queue", msgId);
	}
	//-------------------------------------------------------------------------------------------

	void ClientAdapterProxy::Release()
	{
		delete this;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SetDeviceDetail(const DevDetail& deviceDetail)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, e_devDetail);
		return executeSetRequest(&deviceDetail, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SetDMAccount(const DMAcc& dmAccount)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, e_DMAcc);
		return executeSetRequest(&dmAccount, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SetWiMAX(const WiMAX& wimax)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, e_WiMAX);
		return executeSetRequest(&wimax, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SetWiMAXSupp(const WiMAXSupp& wimaxSupp)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, e_WiMAXSupp);
		return executeSetRequest(&wimaxSupp, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SetWiMAXDiagnostics(const NS_DM_Diagnostics::WiMAX_Diagnostics& wimaxDiagnostics)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, e_WiMAX_Diagnostics);
		return executeSetRequest(&wimaxDiagnostics, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::DRMDReady()
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_readyDRMD, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
		return executeRequestWithNotification(request, NULL);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::GetDeviceDetail(DevDetail& deviceDetail)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_get, e_devDetail);
		return executeGetRequest(&deviceDetail, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::GetDMAccount(DMAcc& dmAccount)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_get, e_DMAcc);
		return executeGetRequest(&dmAccount, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::GetWiMAX(WiMAX& wimax)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_get, e_WiMAX);
		return executeGetRequest(&wimax, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::GetWiMAXSupp(WiMAXSupp& wimaxSupp)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_get, e_WiMAXSupp);
		return executeGetRequest(&wimaxSupp, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::GetClientProfiles(std::vector<ClientProfileInfo>& profiles)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_clientProfiles, e_none);
		LOG_(m_logger, "Message \"%s\" is created", S_msgSubTypeDescription[request.m_type]);

		NS_Common::EventEx notifyResponse;
		Message response;
		addMessageToQueue(request.m_id, response, notifyResponse);

		bool res = sendRequest(request);
		if (res)
		{
			LOG_(m_logger, "Waiting for event response notification...");
			notifyResponse.wait();
			// retrieve profiles' count
			size_t profiliesCount = 0; //atoi(response.m_data.c_str());
			if (response.m_data.size() != 0)
				memcpy(&profiliesCount, &(response.m_data[0]), sizeof(size_t));
			LOG_(m_logger, "Profiles count = %d", profiliesCount);
			for (size_t i = 0; i != profiliesCount; ++i)
			{
				notifyResponse.wait();
				LOG_(m_logger, "Waiting for event response notification...");

				ClientProfileInfo operatorProfile;
				res = handleGetClientProfileResponse(operatorProfile, response);
				profiles.push_back(operatorProfile);
			}
		}
		deleteMessageFromQueue(request.m_id);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::handleGetClientProfileResponse(ClientProfileInfo& operatorProfile, const Message& response)
	{
		if (response.m_data.empty())
		{
			LOG_ERROR_(m_logger, "Received empty data in GetClientProfile response");
			return false;
		}
		String profile;
		BytesToString(response.m_data, profile);
		size_t pos = profile.find_first_of(S_profileInfoSeperator);
		operatorProfile.m_name = profile.substr(0, pos);
		operatorProfile.m_description = profile.substr(pos + S_profileInfoSeperator.size());
		LOG_(m_logger, "Profile Info in response: name = %s, description = %s", operatorProfile.m_name.c_str(), operatorProfile.m_name.c_str());
		return true;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SwitchClientProfile(const String& profileName)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, e_clientProfileName, profileName);
		return executeSetRequest(0, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::executeGetRequest(IMgmtObject* mgmtObject, const Message& request)
	{
		Message response;
		bool res = executeGetRequestHelper(request, response);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to execute GetRequestHelper");
			return res;
		}

		res = handleGetResponse(mgmtObject, response);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::executeGetRequest(String& mgmtTree, const Message& request)
	{
		Message response;
		bool res = executeGetRequestHelper(request, response);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to execute GetRequestHelper");
			return res;
		}
		BytesToString(response.m_data, mgmtTree);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::executeGetRequestHelper(const Message& request, Message& response)
	{
		if (!m_isOpened)
		{
			LOG_WARNING_(m_logger, "Failed to send any messages before \"open\" request");
			return false;
		}
		// nothing to do with message request - it is ready to send "as is"
		NS_Common::EventEx notifyResponse;
		addMessageToQueue(request.m_id, response, notifyResponse);

		bool res = sendRequest(request);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to send request");
			return res;
		}

		LOG_(m_logger, "Waiting for event response notification...");
		notifyResponse.wait();

		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::executeSetRequest(const IMgmtObject* mgmtObject, Message& request)
	{
		if (!m_isOpened)
		{
			LOG_WARNING_(m_logger, "Failed to send any messages before \"open\" request");
			return false;
		}
		bool res = true;
		if (mgmtObject)
		{
			res = createSetRequest(*mgmtObject, request);
			if (!res)
			{
				LOG_ERROR_(m_logger, "Failed to create request");
				return res;
			}
		}

		NS_Common::EventEx notifyResponse;
		Message response;
		addMessageToQueue(request.m_id, response, notifyResponse);

		res = sendRequest(request);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to send request");
			return res;
		}

		LOG_(m_logger, "Waiting for event response notification...");
		notifyResponse.wait();
		return handleStatusResponse(response);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::handleGetResponse(IMgmtObject* mgmtObject, const Message& response)
	{
		if (response.m_data.empty())
		{
			LOG_ERROR_(m_logger, "Received empty data in get response");
			return false;
		}
		bool res = false;
		if (mgmtObject)
		{
			String xmlData;
			BytesToString(response.m_data, xmlData);
			//// debug 2011.05.04
			LOG_ERROR_(m_logger, "ClientAdapterProxy::handleGetResponse(): xmlDtata");
			res = mgmtObject->Deserialize(xmlData);
			if (res)
			{
				LOG_(m_logger, "Deserialized management object from xml: \n%s", xmlData.c_str());
			}
		}
		else
		{
			LOG_ERROR_(m_logger, "Management object is NULL");

		}
		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::handleStatusResponse(const Message& response)
	{
		if (response.m_type != e_status)
		{
			LOG_WARNING_(m_logger, "Expected status, received = %s", S_msgTypeDescription[response.m_type]);
			return false;
		}
		return response.m_subType == e_okStatus;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::createSetRequest(const IMgmtObject& mgmtObject, Message& request)
	{
		String mgmtObjectXml;
		bool res = mgmtObject.Serialize(mgmtObjectXml);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to serialize management object to xml");
			return res;
		}
		LOG_(m_logger, "Serialized devInfo to xml: \n%s", mgmtObjectXml.c_str());
		StringToBytes(mgmtObjectXml, request.m_data);
		LOG_(m_logger, "Message is created: type = %s, subtype = %s", S_msgTypeDescription[request.m_subType],
			S_msgSubTypeDescription[request.m_subType]);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::sendRequest(const Message& request)
	{
		LOG_(m_logger, "Trying to send request id = %d, type = %s, subtype = %s", request.m_id,
			S_msgTypeDescription[request.m_type], S_msgSubTypeDescription[request.m_subType]);
		MessageSerializer serializer;
		const MessageSerializer::PlainData& data = serializer(request);
		if (data.empty())
		{
			LOG_ERROR_(m_logger, "Failed to serialize message");
			return false;
		}
		LOG_(m_logger, "Message was serialized successfully. Plain data size = %d", data.size());
		bool res = (m_outFIFOWrapper->Write(&data[0], data.size()) == e_Ok);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to write to fifo");
			return res;
		}
		LOG_(m_logger, "Message was sent to fifo");
		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::isMessageSynchronous(MessageId msgId)
	{
		SynchMessages::iterator found = m_synchMessages.find(msgId);
		if (found == m_synchMessages.end())
		{
			LOG_(m_logger, "Message with id = %d isn't synchronous", msgId);
			return false;
		}
		else
		{
			LOG_(m_logger, "Message with id = %d is synchronous", msgId);
			return true;
		}
	}
	//-------------------------------------------------------------------------------------------

	void ClientAdapterProxy::notifyResponse(Message& response)
	{
		NS_Common::Lock lock(m_mapMutex);
		SynchMessages::iterator found = m_synchMessages.find(response.m_id);
		if (found == m_synchMessages.end())
		{
			LOG_WARNING_(m_logger, "Response for unknown id");
			return;
		}
		LOG_(m_logger, "Message with id %d found in synchQueue", response.m_id);
		*(found->second.first) = response;
		found->second.second->signal();
		LOG_(m_logger, "Event was signaled");
	}
	
	//-------------------------------------------------------------------------------------------
	void ClientAdapterProxy::notifyAllResponsesWithFail()
	{
		NS_Common::Lock lock(m_mapMutex);
		for (SynchMessages::iterator ptr = m_synchMessages.begin(); ptr != m_synchMessages.end(); ++ptr)
		{
			if (ptr->second.first)
			{
				ptr->second.first->m_type = e_status;
				ptr->second.first->m_subType = e_failedStatus;
				if (ptr->second.second)
				{
					ptr->second.second->signal();
				}
			}
		}
	}
	//-------------------------------------------------------------------------------------------

	void ClientAdapterProxy::notifyUpdater(Message& response)
	{
		NS_Common::Lock lock(m_mapMutex);
		AsynchMessages::iterator found = m_asynchMessages.find(response.m_id);
		if (found == m_asynchMessages.end())
		{
			LOG_WARNING_(m_logger, "Response for unknown id");
			return;
		}
		LOG_(m_logger, "Message with id %d found in asynchQueue", response.m_id);
		LOG_(m_logger, "Message type = %s", S_msgTypeDescription[response.m_type]);

		INotificationUpdater* updater = found->second;
		if (updater)
		{
			LOG_(m_logger, "Notification is required");
			String tmpData;
			BytesToString(response.m_data, tmpData);
			if (e_startDMSession == response.m_type)
			{
				updater->StartDMSessionStatus(tmpData == S_true);
			}
			else if (e_notifyDMSession == response.m_type)
			{
				updater->DMSessionNotif(GetNotificationStatus(tmpData));
			}
			else if (e_checkFirmwareUpdate == response.m_type)
			{
				updater->CheckFirmwareUpdateStatus(tmpData == S_true);
			}
			else if (e_notifyFirmwareUpdate == response.m_type)
			{
				EnumFirmwareOperation fwo = e_FWOError;
				if (response.m_subType == e_FirmwareDownload)
				{
					fwo = e_FWODownload;
				}
				else if (response.m_subType == e_FirmwareUpdate)
				{
					fwo = e_FWOUpdate;
				}
				else if (response.m_subType == e_FirmwareDownloadAndUpdate)
				{
					fwo = e_FWODownloadAndUpdate;
				}
				else
				{
					LOG_ERROR_(m_logger, "Unknown firmware operation %d", response.m_subType);
				}
				updater->FirmwareUpdateNotif(fwo, GetNotificationStatus(tmpData));
			}
			else if (e_notifyProvisioningUpdate == response.m_type)
			{
				updater->ProvisioningUpdateNotif(tmpData.c_str());
			}
			else if (e_collectDRMD == response.m_type)
			{
				int duration = atoi(tmpData.c_str());
				updater->StartDRMDCollecting(duration);
			}
		}
		else
			LOG_(m_logger, "Notification isn't required");

	}
	//-------------------------------------------------------------------------------------------

	void ClientAdapterProxy::receiveResponse()
	{
		LOG_(m_logger, "Waiting for response...");
		Message response;
		bool res = receiveResponse(response);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to receive response");
			notifyAllResponsesWithFail();
			m_outFIFOWrapper->Close();
			m_stopRunning = true;
			return;
		}

		// find appropriate message in queue
		if (isMessageSynchronous(response.m_id))
		{
			notifyResponse(response);
			if (response.m_type != e_clientProfiles)
				deleteMessageFromQueue(response.m_id);
		}
		else
		{
			notifyUpdater(response);
		}

		if ((response.m_type == e_status) && (response.m_subType == e_stopWaitingStatus))
		{
			m_stopRunning = true;
		}
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::receiveResponse(Message& response)
	{
		Buffer buffer;
		bool res = ReadMessageFromFIFO(m_inFIFOWrapper, buffer, m_logger, m_criticalSection);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to read response from fifo");
			return res;
		}
		if (buffer.Size() == 0)
		{
			LOG_WARNING_(m_logger, "Empty buffer received from inFIFOWrapper");
			return false;
		}
		LOG_(m_logger, "Response was received");
		MessageDeserializer deserilizer;
		deserilizer(buffer.GetPointer(), buffer.Size(), response);
		LOG_(m_logger, "Response was deserialized: id = %d, type = %s, subtype = %s", response.m_id,
			S_msgTypeDescription[response.m_type], S_msgSubTypeDescription[response.m_subType]);
		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::HandleNetworkEntry(int homeNspID, const char* operatorName)
	{
		if (!operatorName)
		{
			operatorName = "";
		}

		String data;
		data += NS_DM_Client::ToString(homeNspID);
		data += PARAMS_DELIMITER;
		data += operatorName;

		MessageId mid = 0;
		Message request(getUniqueId(mid), e_networkEntry, e_none, data);
		NS_Common::EventEx notifyResponse;
		Message response;
		addMessageToQueue(request.m_id, response, notifyResponse);

		bool res = sendRequest(request);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to send request");
			return res;
		}

		LOG_(m_logger, "Waiting for event response notification...");
		notifyResponse.wait();
		return handleStatusResponse(response);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SetMgmtTree(const String& mgmtTree)
	{
		const MOType& moType = e_NotWellKnownMO;
		size_t objType = getMsgSubType(moType);
		IMgmtObject* mgmtObject = GetMgmtObject(objType);
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, objType, mgmtTree);
		return executeSetRequest(mgmtObject, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::GetMgmtTree(const String& uri, String& mgmtTree)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_get, e_none, uri);
		return executeGetRequest(mgmtTree, request);
	}	
	//-------------------------------------------------------------------------------------------

	size_t ClientAdapterProxy::getMsgSubType(const MOType& moType)
	{
		switch (moType)
		{
		case e_MODevInfo:
			return e_devInfo;
		case e_MODevDetail:
			return e_devDetail;
		case e_MODMAcc:
			return e_DMAcc;
// 		case e_MOWiMAX:
// 			return e_WiMAX;
// 		case e_MOWiMAXSupp:
// 			return e_WiMAXSupp;
		default:
			return e_none;
		}
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SetEMSK(const void* buffer, size_t count)
	{
		std::vector<byte> tmpBuffer;
		tmpBuffer.resize(count);
		memcpy(&tmpBuffer[0], buffer, count);
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, e_EMSK, tmpBuffer);
		return executeSetRequest(0, request);
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::GetEMSK(void*& buffer, size_t& count)
	{
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_get, e_EMSK);
		Message response;
		bool res = executeGetRequestHelper(request, response);
		if (!res)
		{
			LOG_ERROR_(m_logger, "Failed to execute GetRequestHelper");
			return res;
		}
		count = response.m_data.size();
		if (count)
		{
			buffer = new char[count];
			memset(buffer, 0, count);
			memcpy(buffer, &response.m_data[0], count);
		}
		return res;
	}
	//-------------------------------------------------------------------------------------------

	bool ClientAdapterProxy::SetDeviceID(const void* buffer, size_t count)
	{
		std::vector<byte> tmpBuffer;
		tmpBuffer.resize(count);
		memcpy(&tmpBuffer[0], buffer, count);
		MessageId mid = 0;
		Message request(getUniqueId(mid), e_set, e_DeviceID, tmpBuffer);
		return executeSetRequest(0, request);
	}
	//-------------------------------------------------------------------------------------------
}
