#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"
#include "ClientAdapter/ProvisionInfo.h"


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

namespace NS_DM_Client
{
    const int S_maxWaitForThreadFinished = 10000;

    NotificationStatus GetNotificationStatus(const String& strStatus)
    {
        if (strStatus == S_notificationStatusDescription[e_start])
            return e_start;
        if (strStatus == S_notificationStatusDescription[e_end])
            return e_end;
        if (strStatus == S_notificationStatusDescription[e_abort])
            return e_abort;
        return e_unknown;
    }
 
    //-------------------------------------------------------------------------------------------

    ClientAdapterProxy::ClientAdapterProxy(): m_stopRunning(false), m_inFIFOWrapper(0), m_outFIFOWrapper(0), m_logger(NS_Logging::GetLogger("ClientAdapterProxy")),
        m_isOpened(false), m_receiver(0), m_lastMsgId(0), m_confirmation(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)
        {
            LOG_DEBUG_(m_logger, "Waiting for receiver thread to stop");
            const bool waitResult = m_receiver->wait(S_maxWaitForThreadFinished);
            LOG_DEBUG_(m_logger, "Waiting for receiver thread to stop finished. Result=%d", waitResult);
            delete m_receiver;
            m_receiver = NULL;
        }
        LOG_DEBUG_(m_logger, "Waiting for input FIFO to stop");
        m_inFIFOWrapper->Release();
        LOG_DEBUG_(m_logger, "Waiting for output FIFO to stop");
        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;
    //}
    ////-------------------------------------------------------------------------------------------

    size_t& ClientAdapterProxy::getUniqueId()
    {
        NS_Common::Lock lock(m_mapMutex);
        return ++m_lastMsgId;
    }
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::StartDMSession(const String& alertMessage, IStatusCallback* statusCallback)
    {
        Message request(getUniqueId(), e_startDMSession, e_none, alertMessage);
        LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
        return executeRequestWithNotification(request, statusCallback);
    }
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::StartBootstrapRequest()
    {
        if (!m_isOpened)
        {
            LOG_WARNING_(m_logger, "Failed to send any messages before \"open\" request");
            return false;
        }
        String data;
        Message response;
        Message request(getUniqueId(), e_startBootstrap, e_none, data);
        NS_Common::EventEx notifyResponse;
        addMessageToQueue(request.m_id, response, notifyResponse);

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

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

    bool ClientAdapterProxy::StartDRMD()
    {
        if (!m_isOpened)
        {
            LOG_WARNING_(m_logger, "Failed to send any messages before \"open\" request");
            return false;
        }
        String data;
        Message response;
        Message request(getUniqueId(), e_startDRMD, e_none, data);
        NS_Common::EventEx notifyResponse;
        addMessageToQueue(request.m_id, response, notifyResponse);

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

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

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

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

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

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

    bool ClientAdapterProxy::SubscribeToNotification(const ProvisionInfoRequest& provInfo, INotificationUpdater* updater)
    {
        Message request(getUniqueId(), e_notifyProvisioningUpdate, e_none);
        String strProvision;
        provInfo.Serialize(strProvision);
        StringToBytes(strProvision, request.m_data);
        LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
        return executeRequestWithNotification(request, updater);
    }
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::UnsubscribeToDMSessionNotif()
    {
        Message request(getUniqueId(), e_unsubscribeDMSession, e_none);
        LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
        return executeUnsubscribeRequest(request);
    }
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::UnsubscribeToFirmwareUpdateNotif()
    {
        Message request(getUniqueId(), e_unsubscribeFirmwareUpdate, e_none);
        LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
        return executeUnsubscribeRequest(request);
    }
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::UnsubscribeToProvisioningUpdateNotif()
    {
        Message request(getUniqueId(), e_unsubscribeProvisioningUpdate, e_none);
        LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
        return executeUnsubscribeRequest(request);
    }
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::SubscribeToConfirmation(IConfirmation* confirmation)
    {
        m_confirmation = confirmation;
        Message request(getUniqueId(), e_confirmationRequired, e_none);
        LOG_(m_logger, "Message \"%s\" is created", S_msgTypeDescription[request.m_type]);
        return executeRequestWithNotification(request, confirmation);
    }
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::SendConfirmationResponse(const String& requestMessage, ResponseCode confCode)
    {
        if (!m_isOpened)
        {
            LOG_WARNING_(m_logger, "Failed to send any messages before \"open\" request");
            return false;
        }
        String data;
        SerializeConfirmationResponse(requestMessage, confCode, data);
        NS_Common::EventEx notifyResponse;
        Message response;
        Message request(getUniqueId(), e_confirmationResponse, e_none, data);
        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::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(std::nothrow) ResponseReceiverThread(*this);
            if(m_receiver == (Thread*)NULL)
            {
                LOG_ERROR_(m_logger, "new ResponseReceiverThread");
                return false;
            }
            m_receiver->start();
        }

        Message request(getUniqueId(), 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;
        }

        Message request(getUniqueId(), 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::ChangeDefaultAccountPassword(const String& oldPassword, const String& newPassword)
    {
        if (!m_isOpened)
        {
            LOG_WARNING_(m_logger, "Failed to send any messages before \"open\" request");
            return false;
        }
        String data;
        SerializePasswords(oldPassword, newPassword, data);
        Message response;
        Message request(getUniqueId(), e_changePassword, e_none, data);
        NS_Common::EventEx notifyResponse;
        addMessageToQueue(request.m_id, response, notifyResponse);

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

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

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

    bool ClientAdapterProxy::ChangeAccountPassword(const String& serverID, const String &oldpassword, const String &newpassword)
    {
        if (!m_isOpened)
        {
            LOG_WARNING_(m_logger, "Failed to send any messages before \"open\" request");
            return false;
        }
        String data;
        SerializePasswordsAndServerID(serverID, oldpassword, newpassword, data);
        Message response;
        Message request(getUniqueId(), e_changePasswordByServerID, e_none, data);
        NS_Common::EventEx notifyResponse;
        addMessageToQueue(request.m_id, response, notifyResponse);

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

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

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

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

    bool ClientAdapterProxy::SetDeviceInfo(const DevInfo& deviceInfo)
    {
        Message request(getUniqueId(), 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()
    {
        Close();
        delete this;
    }
    //-------------------------------------------------------------------------------------------

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

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

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

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

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

    bool ClientAdapterProxy::DRMDReady()
    {
        Message request(getUniqueId(), 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)
    {
        Message request(getUniqueId(), e_get, e_devDetail);
        return executeGetRequest(&deviceDetail, request);
    }
    //-------------------------------------------------------------------------------------------

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

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

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

    bool ClientAdapterProxy::GetClientProfiles(ClientProfiles& profiles)
    {
        Message request(getUniqueId(), e_clientProfiles, 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();
            // retrieve profiles' count
            String strProfileCount;
            BytesToString(response.m_data, strProfileCount);
            int profilesCount = atoi(strProfileCount.c_str());
            LOG_(m_logger, "Profiles count = %d", profilesCount);
            for (size_t i = 0; i != profilesCount; ++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::GetProfileActivationStatus(bool& isActive)
    {
        Message request(getUniqueId(), e_profileActivationStatus, 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();
            // retrieve profiles' count
            String strIsActive;
            BytesToString(response.m_data, strIsActive);
            isActive = !(atoi(strIsActive.c_str()) == 0);
            LOG_(m_logger, "Current profile activation status: %d", isActive);
        }
        deleteMessageFromQueue(request.m_id);
        return res;
    }
    
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::GetProfileActivationStatus(const String& serverID, bool& isActive)
    {
        Message request(getUniqueId(), e_profileActivationStatusByServerID, e_none, serverID);
        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();
            // retrieve profiles' count
            String strIsActive;
            BytesToString(response.m_data, strIsActive);
            isActive = !(atoi(strIsActive.c_str()) == 0);
            LOG_(m_logger, "Current profile activation status: %d", isActive);
        }
        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);
        DeserializeOperatorProfile(profile, operatorProfile.m_name, operatorProfile.m_type, operatorProfile.m_description);
        LOG_(m_logger, "Profile Info in response: name = %s, type = %s, description = %s", operatorProfile.m_name.c_str(), 
            operatorProfile.m_type.c_str(), operatorProfile.m_description.c_str());
        return true;
    }
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::SwitchClientProfile(const String& profileName)
    {
        Message request(getUniqueId(), 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::executeUnsubscribeRequest(const Message& request)
    {
        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"
        Message response;
        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, "\"Unsubcsribe\" request was sent");
        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);
            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 id = %d not found in asynchQueue", response.m_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)
            {
                EventType eventType;
                Initiator initiator;
                NotificationStatus status;
                DeserializeDMSessionNotification(tmpData, eventType, initiator, status);
                updater->NotifyDMSessionStatus(eventType, initiator, status);
            }
            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 if (response.m_subType == e_FirmwareInfo)
                {
                    String firmwareInfo;
                    BytesToString(response.m_data, firmwareInfo);
                    String localpath, filename;
                    int size;
                    DeserializeFirmwareInfo(firmwareInfo, localpath, filename, size);
                    updater->FirmwareInfo(localpath, filename, size);
                    return;
                }
                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());
                handleNotifyProvisioningUpdate(tmpData, updater);
            }
            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::handleNotifyProvisioningUpdate(const String& data, INotificationUpdater* updater)
    {
        ProvisionInfoResponse provisionInfoResponse;
        bool res = provisionInfoResponse.Deserialize(data);
        if (!res)
        {
            LOG_ERROR_(m_logger, "Failed to deserialize ProvisionInfoResponse. No updater is notified");
        }

        updater->ProvisioningUpdate(provisionInfoResponse.m_URIs, provisionInfoResponse.m_command, 
            provisionInfoResponse.m_event, provisionInfoResponse.m_statusCode);
    }
    //-------------------------------------------------------------------------------------------

    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;
        }

        if ((response.m_type == e_status) && (response.m_subType == e_stopWaitingStatus))
        {
            m_stopRunning = true;
        }
        // find appropriate message in queue
        else if (isMessageSynchronous(response.m_id))
        {
            notifyResponse(response);
            if (response.m_type != e_clientProfiles)
                deleteMessageFromQueue(response.m_id);
        }
        else if (response.m_type == e_confirmationRequired)
        {
            LOG_(m_logger, "Received \"Confirmation Required\" request");
            if (m_confirmation)
            {
                String strData;
                BytesToString(response.m_data, strData);
                m_confirmation->ConfirmationRequired(strData);
                LOG_(m_logger, "Confirmation was sent to user");
            }
        }
        else
        {
            notifyUpdater(response);
        }

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

    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;

        Message request(getUniqueId(), 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;
        MsgSubType objType = getMsgSubType(moType);
        IMgmtObject* mgmtObject = GetMgmtObject(objType);
        Message request(getUniqueId(), e_set, objType, mgmtTree);
        return executeSetRequest(mgmtObject, request);
    }
    //-------------------------------------------------------------------------------------------

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

    MsgSubType 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, (byte)0);
        Message request(getUniqueId(), e_set, e_EMSK, tmpBuffer);
        return executeSetRequest(0, request);
    }
    //-------------------------------------------------------------------------------------------

    bool ClientAdapterProxy::GetEMSK(void*& buffer, size_t& count)
    {
        Message request(getUniqueId(), 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];
            if(buffer == NULL)
            {
                LOG_ERROR_(m_logger, "new buffer");
                return false;
            }
            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, (byte)0);
        Message request(getUniqueId(), e_set, e_DeviceID, tmpBuffer);
        return executeSetRequest(0, request);
    }
    //-------------------------------------------------------------------------------------------
    bool ClientAdapterProxy::SetManufacturer(const void* buffer, size_t count)
    {
        std::vector<byte> tmpBuffer;
        tmpBuffer.resize(count, (byte)0);
        Message request(getUniqueId(), e_set, e_manufacturer, tmpBuffer);
        return executeSetRequest(0, request);
    }
    //-------------------------------------------------------------------------------------------
    bool ClientAdapterProxy::SetModel(const void* buffer, size_t count)
    {
        std::vector<byte> tmpBuffer;
        tmpBuffer.resize(count, (byte)0);
        Message request(getUniqueId(), e_set, e_model, tmpBuffer);
        return executeSetRequest(0, request);
    }
    //-------------------------------------------------------------------------------------------

}
