/*
* Copyright 2009 Funambol, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/* $Id$ */

#include "MessengerDefs.h"
#include "Logger/Logger.h"

namespace NS_DM_Client
{

const char* c_messageRequestFifoName = "mrequest.fifo";
const char* c_messageResponseFifoName = "mresponse.fifo";

const char* c_IPCRequestFifoName = "ipc_request.fifo";
const char* c_IPCResponseFifoName = "ipc_response.fifo";

const char* const c_MessageListenerLog = "MessageListener";
const char* const c_DaemonIPCEngineLog = "DaemonIPCEngine";

////const char* c_pars_delimiter = "*&*";
const char* c_pars_delimiter = "&";
const char* c_null_delimiter = "\0";

const char* c_par_delimiter = "=";
const char* c_choises_delimiter = "-";

const char* c_min_dt_time = "10";
const char* c_max_dt_time = "200";

const char* c_StopRequest = "stop";
////const size_t c_StopRequestSize = 4; // sizeof (c_StopRequest)
const size_t c_StopRequestSize = strlen(c_StopRequest);

const char* c_optParamTypes[] =
{
    "MINDT",
    "MAXDT",
    "DR",
    "MAXLEN",
    "IT",
    "ET"
};

const char* c_MultipleChoises = "MC";

bool initializeFifos(IFIFOWrapper*& request, IFIFOWrapper*& response, bool stop_listening, bool serverSide)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    request = CreateFIFOWrapper(serverSide);
    if (!request)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't create fifo request wrapper");
        return false;
    }
    response = CreateFIFOWrapper(serverSide);
    if (!response)
    {
        request->Release();
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't create fifo response wrapper");
        return false;
    }

    if (request->Open(c_messageRequestFifoName, (stop_listening) ? false : true, true, true) != e_Ok)
    {
        releaseFifos(request, response);
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't open request fifo");
        return false;
    }

   // sleep(1);

    if (response->Open(c_messageResponseFifoName, (stop_listening) ? true : false, true, true) != e_Ok)
    {
        releaseFifos(request, response);
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't open response fifo");
        return false;
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}

bool initializeIPCFifos(IFIFOWrapper*& request, IFIFOWrapper*& response, bool stop_listening, bool ServerSide)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    request = CreateFIFOWrapper(ServerSide);
    if (!request)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't create fifo request wrapper");
        return false;
    }
    response = CreateFIFOWrapper(ServerSide);
    if (!response)
    {
        request->Release();
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't create fifo response wrapper");
        return false;
    }

    if (request->Open(c_IPCRequestFifoName, (stop_listening) ? false : true, true, true) != e_Ok)
    {
        releaseFifos(request, response);
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't open request fifo");
        return false;
    }

   // sleep(1);

    if (response->Open(c_IPCResponseFifoName, (stop_listening) ? true : false, true, true) != e_Ok)
    {
        releaseFifos(request, response);
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't open response fifo");
        return false;
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}

bool releaseFifos(IFIFOWrapper* request, IFIFOWrapper* response)
{
    request->Release();
    response->Release();
    return true;
}

//------------------------------------------------------------------------------------------------------
bool formPayloadFromOptPars(const UIOptionalParameters& pars, String& payload, bool allowMultipleChoise)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    payload = "";
    bool first_par = true;
    bool min_dt_present = false;
    bool max_dt_present = false;
    bool dr_present = false;
    bool maxlen_present = false;
    bool it_present = false;
    bool et_present = false;

    for (UIOptionalParameters::const_iterator ptr = pars.begin(); ptr != pars.end(); ++ptr)
    {
        if (first_par)
        {
            first_par = false;
        }
        else
        {
            payload += c_pars_delimiter;
        }

        switch (ptr->m_parameter)
        {
        case e_MINDT:
            payload += c_optParamTypes[e_MINDT];
            payload += c_par_delimiter;
            min_dt_present = true;
            break;
        case e_MAXDT:
            payload += c_optParamTypes[e_MAXDT];
            payload += c_par_delimiter;
            max_dt_present = true;
            break;
        case e_DR:
            payload += c_optParamTypes[e_DR];
            payload += c_par_delimiter;
            dr_present = true;
            break;
        case e_MAXLEN:
            payload += c_optParamTypes[e_MAXLEN];
            payload += c_par_delimiter;
            maxlen_present = true;
            break;
        case e_IT:
            payload += c_optParamTypes[e_IT];
            payload += c_par_delimiter;
            it_present = true;
            break;
        case e_ET:
            payload += c_optParamTypes[e_ET];
            payload += c_par_delimiter;
            et_present = true;
            break;
        default:
            // log warning
            break;
        };

        payload += ptr->m_data;
    }

    // Add default if not exist
    if (!min_dt_present)
    {
        if (first_par)
        {
            first_par = false;
        }
        else
        {
            payload += c_pars_delimiter;
        }
        payload += (String)c_optParamTypes[e_MINDT] + (String)c_par_delimiter + c_min_dt_time;
    }

    if (!max_dt_present)
    {
        if (first_par)
        {
            first_par = false;
        }
        else
        {
            payload += c_pars_delimiter;
        }
        payload += (String)c_optParamTypes[e_MAXDT] + (String)c_par_delimiter + c_max_dt_time;
    }

    // allow many choises
    if (first_par)
    {
        first_par = false;
    }
    else
    {
        payload += c_pars_delimiter;
    }
    payload += (String)c_MultipleChoises + (String)c_par_delimiter + (allowMultipleChoise ? (String)"1" : (String)"2");

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: %s",payload.c_str());

    return true;
}
//------------------------------------------------------------------------------------------------------
bool formOptParFromStringRepresentation(const String& par, UIOptionalParameters& pars)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    size_t separator = par.find(c_par_delimiter, 0);

    String par_name = par.substr(0, separator);
    String par_value = par.substr(separator + 1, par.length() - separator);

    UIOptionalParameterData parameter;
    if (par_name.compare(c_optParamTypes[e_MINDT]) == 0)
    {
        parameter.m_parameter = e_MINDT;
    }
    else if (par_name.compare(c_optParamTypes[e_MAXDT]) == 0)
    {
        parameter.m_parameter = e_MAXDT;
    }
    else if (par_name.compare(c_optParamTypes[e_DR]) == 0)
    {
        parameter.m_parameter = e_DR;
    }
    else if (par_name.compare(c_optParamTypes[e_MAXLEN]) == 0)
    {
        parameter.m_parameter = e_MAXLEN;
    }
    else if (par_name.compare(c_optParamTypes[e_IT]) == 0)
    {
        parameter.m_parameter = e_IT;
    }
    else if (par_name.compare(c_optParamTypes[e_ET]) == 0)
    {
        parameter.m_parameter = e_ET;
    }

    parameter.m_data = par_value;

    pars.push_back(parameter);

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}
//------------------------------------------------------------------------------------------------------
bool get_MINDT(const UIOptionalParameters& pars, long& min_dt)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    for (UIOptionalParameters::const_iterator ptr = pars.begin(); ptr != pars.end(); ++ptr)
    {
        if (ptr->m_parameter == e_MINDT)
        {
            #if defined PLATFORM_WINDOWS
                sscanf_s(ptr->m_data.c_str(), "%ld", &min_dt);
            #else
                sscanf(ptr->m_data.c_str(), "%ld", &min_dt);
            #endif
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: true");
            return true;
        }
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: false");

    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_MAXDT(const UIOptionalParameters& pars, long& max_dt)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    for (UIOptionalParameters::const_iterator ptr = pars.begin(); ptr != pars.end(); ++ptr)
    {
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "UIOptionalParameters: %d, %s",ptr->m_parameter,ptr->m_data.c_str());
        if (ptr->m_parameter == e_MAXDT)
        {
            #if defined PLATFORM_WINDOWS
                sscanf_s(ptr->m_data.c_str(), "%ld", &max_dt);
            #else
                sscanf(ptr->m_data.c_str(), "%ld", &max_dt);
            #endif
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: true");
            return true;
        }
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: false");

    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_DR(const UIOptionalParameters& pars, String& def_val)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");
    for (UIOptionalParameters::const_iterator ptr = pars.begin(); ptr != pars.end(); ++ptr)
    {
        if (ptr->m_parameter == e_IT)
        {
            def_val = ptr->m_data;

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: true");
            return true;
        }
    }
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");
    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_MAXLEN(const UIOptionalParameters& pars, long& max_len)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    for (UIOptionalParameters::const_iterator ptr = pars.begin(); ptr != pars.end(); ++ptr)
    {
        if (ptr->m_parameter == e_MAXLEN)
        {
            #if defined PLATFORM_WINDOWS
                sscanf_s(ptr->m_data.c_str(), "%ld", &max_len);
            #else
                sscanf(ptr->m_data.c_str(), "%ld", &max_len);
            #endif
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: true");
            return true;
        }
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: false");

    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_IT(const UIOptionalParameters& pars, String& input_type)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    for (UIOptionalParameters::const_iterator ptr = pars.begin(); ptr != pars.end(); ++ptr)
    {
        if (ptr->m_parameter == e_IT)
        {
            input_type = ptr->m_data; // IT=A,N,D,T,P,I

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: true");
            return true;
        }
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_ET(const UIOptionalParameters& pars, bool& echo_password)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    for (UIOptionalParameters::const_iterator ptr = pars.begin(); ptr != pars.end(); ++ptr)
    {
        if (ptr->m_parameter == e_ET)
        {
            char type = '\0'; // ET=T,P

            #if defined PLATFORM_WINDOWS
                sscanf_s(ptr->m_data.c_str(), "%c", &type);
            #else
                sscanf(ptr->m_data.c_str(), "%c", &type);
            #endif

            echo_password = (type == 'T') ? true : false;
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: true");
            return true;
        }
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE: false");

    return false;
}
//------------------------------------------------------------------------------------------------------
bool formOptParsFromPayload(const String& payload, UIOptionalParameters& pars, bool* allowMultipleChoise)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    size_t pos_beg = 0;
    size_t pos_end = 0;
    const String pars_delimited = payload + c_pars_delimiter;
    String par;
    while ((pos_end = pars_delimited.find(c_pars_delimiter, pos_beg)) != String::npos )
    {
        par = pars_delimited.substr(pos_beg, pos_end - pos_beg);

        if ((par.substr(0, strlen(c_MultipleChoises))).compare(c_MultipleChoises) == 0)
        {
            if (allowMultipleChoise != 0)
            {
                *allowMultipleChoise = (par[3] == '1') ? true : false;
            }
        }
        else
        {
            formOptParFromStringRepresentation(par, pars);
        }
        pos_beg = pos_end + strlen(c_pars_delimiter);
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}
//------------------------------------------------------------------------------------------------------
bool formComplexRequestPayload(
    const String& message, const String& pars, const String& choises, const String& downloads, void*& payload, size_t& payload_size)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    // calc payload length
    payload_size = message.length() + 1 + pars.length() + 1;
    if (!choises.empty())
    {
        payload_size += choises.length() + 1;
    }

    if (!downloads.empty())
    {
        payload_size += downloads.length() + 1;
    }

    payload = malloc(payload_size);
    if(payload == NULL)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "malloc");
        return false;
    }
    memset(payload, 0, payload_size);

    ptrdiff_t pos = 0;
    memcpy((char*)payload + pos, message.c_str(), message.length());
    pos += message.length();
    memcpy((char*)payload + pos, c_null_delimiter, 1);
    pos += 1;
    memcpy((char*)payload + pos, pars.c_str(), pars.length());
    pos += pars.length();
    memcpy((char*)payload + pos, c_null_delimiter, 1);
    pos += 1;

    if (!choises.empty())
    {
        memcpy((char*)payload + pos, choises.c_str(), choises.length());
        pos += choises.length();
    }
    if (!downloads.empty())
    {
        memcpy((char*)payload + pos, downloads.c_str(), downloads.length());
        pos += downloads.length();
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}
//------------------------------------------------------------------------------------------------------
bool formComplexRequest(UIMessageType type, void* payload, size_t payload_size, void*& request, size_t& request_size)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    request_size = sizeof(UIMessageType) + sizeof(size_t) + payload_size;
    request = malloc(request_size);
    if(request == NULL)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "malloc");
        return false;
    }
    memset(request, 0, request_size);

    memcpy(request, &type, sizeof(UIMessageType));
    memcpy((char*)request + sizeof(UIMessageType), &payload_size, sizeof(size_t));
    memcpy((char*)request + sizeof(UIMessageType) + sizeof(size_t), payload, payload_size);

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");
    return true;
}
//------------------------------------------------------------------------------------------------------
bool extractComplexRequestPayload(const void* payload, size_t payload_size, String& message, String& pars,
    String* choises, String* downloads)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER: payload = %s", (char*)payload);

    char* str = (char*)payload;
    message = str;

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "message = %s", message.c_str());

    str = (char*)payload + strlen(str) + 1;
    pars = str;

    if (choises || downloads)
    {
        str = (char*)payload + message.size() + 1 + pars.size() + 1;
        if (choises)
        {
            *choises = str;
        }
        if (downloads)
        {
            *downloads = str;
        }
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}
//------------------------------------------------------------------------------------------------------
void deleteMemory(void* memory)
{
    if (memory)
    {
        free(memory);
    }
}
//------------------------------------------------------------------------------------------------------
bool formChoiseSet(const UserAvailableChoiseSet& choise_set, String& choises)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    choises = "";
    bool first = true;
    for (UserAvailableChoiseSet::const_iterator ptr = choise_set.begin(); ptr != choise_set.end(); ++ptr)
    {
        if (first)
        {
            first = false;
        }
        else
        {
            choises = choises + c_choises_delimiter;
        }
        choises = choises + (*ptr);
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}
//------------------------------------------------------------------------------------------------------
bool extractChoiseSet(const String& choises, UserAvailableChoiseSet& choise_set)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    size_t pos_beg = 0;
    size_t pos_end = 0;
    String choises_with_end_separator = choises + c_choises_delimiter;
    String choise;
    choise_set.clear();
    while ((pos_end = choises_with_end_separator.find(c_choises_delimiter, pos_beg)) != String::npos )
    {
        choise = choises_with_end_separator.substr(pos_beg, pos_end - pos_beg);
        choise_set.push_back(choise);
        pos_beg = pos_end + strlen(c_choises_delimiter);
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}
//------------------------------------------------------------------------------------------------------
bool getNumericUserChoise(const String& available_choises, const String& user_choises, UserChoiseSet& choise_set)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    UserAvailableChoiseSet av_choises, us_choises;
    if (extractChoiseSet(available_choises, av_choises) && extractChoiseSet(user_choises, us_choises))
    {
        for (UserAvailableChoiseSet::const_iterator i = us_choises.begin(); i != us_choises.end(); ++i)
        {
            size_t av_cnt = 0;
            for (UserAvailableChoiseSet::const_iterator j = av_choises.begin(); j != av_choises.end(); ++j)
            {
                if ( (*i).compare(*j) == 0 )
                {
                    choise_set.push_back(av_cnt);
                    break;
                }
                ++av_cnt;
            }
        }
        return true;
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return false;
}
//------------------------------------------------------------------------------------------------------
bool formStringFromNumericUserChoise(const UserChoiseSet& choise_set, String& str_choise)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    char digit[12];
    bool first = true;
    for (UserChoiseSet::const_iterator i = choise_set.begin(); i != choise_set.end(); ++i)
    {
        if (first)
        {
            first = false;
        }
        else
        {
            str_choise = str_choise + c_choises_delimiter;
        }

        #if defined PLATFORM_WINDOWS
            sprintf_s(digit, "%d", *i);
        #else
            sprintf(digit, "%d", *i);
        #endif

        str_choise = str_choise + digit;
LOG_INFO_(NS_Logging::GetLogger(c_MessageListenerLog), "str_choise2: %s", str_choise.c_str());
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}
//------------------------------------------------------------------------------------------------------
bool formNumericUserChoiseFromString(const String& str_choise, UserChoiseSet& choise_set)
{
LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER");

    size_t pos_beg = 0;
    size_t pos_end = 0;
    String choises_with_end_separator = str_choise + c_choises_delimiter;
    String choise;
    choise_set.clear();
    while ((pos_end = choises_with_end_separator.find(c_choises_delimiter, pos_beg)) != String::npos )
    {
        choise = choises_with_end_separator.substr(pos_beg, pos_end - pos_beg);

        int int_choise;

        #if defined PLATFORM_WINDOWS
            sscanf_s(choise.c_str(), "%d", &int_choise);
        #else
            sscanf(choise.c_str(), "%d", &int_choise);
        #endif

        choise_set.push_back(int_choise);

        pos_beg = pos_end + strlen(c_choises_delimiter);
    }

LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE");

    return true;
}

}
