/*
 * 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".
 */

/* $Id$ */

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

namespace NS_DM_Client
{

const char* c_messageRequestFifoName = "message_request.fifo";
const char* c_messageResponseFifoName = "message_response.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_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 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)
{
    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;
    }
    return true;
}

bool initializeIPCFifos(IFIFOWrapper*& request, IFIFOWrapper*& response, bool stop_listening, bool ServerSide)
{
    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;
    }
    return true;
}

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

//------------------------------------------------------------------------------------------------------
bool formPayloadFromOptPars(const UIOptionalParameters& pars, String& payload, bool allowMultipleChoise)
{
    payload = "";
    bool first_par = true;
    bool min_dt_present = false;
    bool max_dt_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;
            break;
        case e_MAXLEN:
            payload += c_optParamTypes[e_MAXLEN];
            payload += c_par_delimiter;
            break;
        case e_IT:
            // skip (under construction)
            break;
        case e_ET:
            payload += c_optParamTypes[e_ET];
            payload += c_par_delimiter;
            break;
        default:
            // log warning
            break;
        };

        payload += ptr->m_data;
    }

    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");

    return true;
}
//------------------------------------------------------------------------------------------------------
bool formOptParFromStringRepresentation(const String& par, UIOptionalParameters& pars)
{
    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);

    return true;
}
//------------------------------------------------------------------------------------------------------
bool get_MINDT(const UIOptionalParameters& pars, long& min_dt)
{
    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

            return true;
        }
    }
    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_MAXDT(const UIOptionalParameters& pars, long& max_dt)
{
    for (UIOptionalParameters::const_iterator ptr = pars.begin(); ptr != pars.end(); ++ptr)
    {
        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

            return true;
        }
    }
    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_DR(const UIOptionalParameters& pars, String& def_val)
{
    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_MAXLEN(const UIOptionalParameters& pars, long& max_len)
{
    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

            return true;
        }
    }
    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_IT(const UIOptionalParameters& pars, String& input_type)
{
    // under construction
    return false;
}
//------------------------------------------------------------------------------------------------------
bool get_ET(const UIOptionalParameters& pars, bool& echo_password)
{
    for (UIOptionalParameters::const_iterator ptr = pars.begin(); ptr != pars.end(); ++ptr)
    {
        if (ptr->m_parameter == e_ET)
        {
            char type;

			#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 == 'y') ? true : false;
            return true;
        }
    }
    return false;
}
//------------------------------------------------------------------------------------------------------
bool formOptParsFromPayload(const String& payload, UIOptionalParameters& pars, bool* allowMultipleChoise)
{
    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);
    }
    return true;
}
//------------------------------------------------------------------------------------------------------
bool formComplexRequestPayload(
    const String& message, const String& pars, const String& choises, const String& downloads, void*& payload, size_t& payload_size)
{
    // 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);

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

    return true;
}
//------------------------------------------------------------------------------------------------------
bool formComplexRequest(UIMessageType type, void* payload, size_t payload_size, void*& request, size_t& request_size)
{
    request_size = sizeof(type) + sizeof(size_t) + payload_size;
    request = malloc(request_size);

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

    return true;
}
//------------------------------------------------------------------------------------------------------
bool extractComplexRequestPayload(const void* payload, size_t payload_size, String& message, String& pars,
    String* choises, String* downloads)
{
    char* str = (char*)payload;
    message = 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;
        }
    }
    return true;
}
//------------------------------------------------------------------------------------------------------
void deleteMemory(void* memory)
{
    if (memory)
    {
        free(memory);
    }
}
//------------------------------------------------------------------------------------------------------
bool formChoiseSet(const UserAvialableChoiseSet& choise_set, String& choises)
{
    choises = "";
    bool first = true;
    for (UserAvialableChoiseSet::const_iterator ptr = choise_set.begin(); ptr != choise_set.end(); ++ptr)
    {
        if (first)
        {
            first = false;
        }
        else
        {
            choises = choises + c_choises_delimiter;
        }
        choises = choises + (*ptr);
    }

    return true;
}
//------------------------------------------------------------------------------------------------------
bool extractChoiseSet(const String& choises, UserAvialableChoiseSet& choise_set)
{
    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);
    }

    return true;
}
//------------------------------------------------------------------------------------------------------
bool getNumericUserChoise(const String& avialable_choises, const String& user_choises, UserChoiseSet& choise_set)
{
    UserAvialableChoiseSet av_choises, us_choises;
    if (extractChoiseSet(avialable_choises, av_choises) && extractChoiseSet(user_choises, us_choises))
    {
        for (UserAvialableChoiseSet::const_iterator i = us_choises.begin(); i != us_choises.end(); ++i)
        {
            size_t av_cnt = 0;
            for (UserAvialableChoiseSet::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;
    }
    return false;
}
//------------------------------------------------------------------------------------------------------
bool formStringFromNumericUserChoise(const UserChoiseSet& choise_set, String& str_choise)
{
    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;
    }
    return true;
}
//------------------------------------------------------------------------------------------------------
bool formNumericUserChoiseFromString(const String& str_choise, UserChoiseSet& choise_set)
{
    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);
    }

    return true;
}

}
