/*
 * 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 "MessageListenerActionThread.h"
#include "DaemonDM/MessageListener.h"
#include "DaemonDM/IMessageHandler.h"
#include "MessengerDefs.h"
#include "common/Buffer.h"
#include "Logger/Logger.h"

#include "platform.h"

namespace NS_DM_Client
{

//------------------------------------------------------------------------------------------------------
MessageListenerActionThread::MessageListenerActionThread()
{
}
//------------------------------------------------------------------------------------------------------
MessageListenerActionThread::~MessageListenerActionThread()
{
}
//------------------------------------------------------------------------------------------------------
bool MessageListenerActionThread::Init(MessageListener* listener)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> MessageListenerActionThread::Init");
    m_listener = listener;
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << MessageListenerActionThread::Init, Return status: %d", true);
    return true;
}
//------------------------------------------------------------------------------------------------------
StatusCode perform_Display(const Buffer& buffer, IMessageHandler* handler);
StatusCode respond_Display(StatusCode status, IFIFOWrapper* response_fifo);

StatusCode perform_Confirmation(const Buffer& buffer, IMessageHandler* handler);
StatusCode respond_Confirmation(StatusCode status, IFIFOWrapper* response_fifo);

StatusCode perform_userInput(const Buffer& buffer, IMessageHandler* handler, String& input);
StatusCode respond_userInput(StatusCode status, IFIFOWrapper* response_fifo, const String& input);

StatusCode perform_userChoises(const Buffer& buffer, IMessageHandler* handler, UserChoiseSet& choise);
StatusCode respond_userChoises(StatusCode status, IFIFOWrapper* response_fifo, const UserChoiseSet& choise);

StatusCode perform_progressNotification(const Buffer& buffer, IMessageHandler* handler);
StatusCode respond_progressNotification(StatusCode status, IFIFOWrapper* response_fifo);
//------------------------------------------------------------------------------------------------------
bool MessageListenerCheckStopCondition(const Buffer& buffer)
{
    char stop_request[/*c_StopRequestSize*/4 + 1];
    stop_request[c_StopRequestSize] = '\0';
    memcpy(stop_request, buffer.GetPointer(), c_StopRequestSize);
    if (strcmp(stop_request, c_StopRequest) == 0)
    {
        return true;
    }
    return false;
}
//------------------------------------------------------------------------------------------------------
void MessageListenerActionThread::run()
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> MessageListenerActionThread::run");

    IFIFOWrapper* fifo_request, *fifo_response;
    while (true)
    {
        if (!initializeFifos(fifo_request, fifo_response, false, true))
        {
            LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << MessageListenerActionThread::run with fail, reason: initialize fifos failed");
            return;
        }
        while (true)
        {
            Buffer buffer(sizeof(UIExchangeHead));
            UIExchangeHead request_head;
            if (fifo_request->Read(buffer) == e_Ok)
            {
                if (MessageListenerCheckStopCondition(buffer))
                {   // stop thread
                    releaseFifos(fifo_request, fifo_response);
                    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << MessageListenerActionThread::run, reason: arrive stop request");
                    return;
                }
                memcpy(&request_head, buffer.GetPointer(), sizeof(UIExchangeHead));

                LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "Request head readed. Request type: %d, Payload size: %d", request_head.m_type, request_head.m_payload_size);

                buffer.Allocate(request_head.m_payload_size);
                if (fifo_request->Read(buffer) == e_Ok)
                {
                    String message, pars;
                    switch(request_head.m_type)
                    {
                    case e_Display:
                    {
                        StatusCode res = perform_Display(buffer, m_listener->GetMessageHandler());
                        respond_Display(res, fifo_response);
                        break;
                    }
                    case e_Confirmation:
                    {
                        StatusCode res = perform_Confirmation(buffer, m_listener->GetMessageHandler());
                        respond_Confirmation(res, fifo_response);
                        break;
                    }
                    case e_UserInput:
                    {
                        String user_input;
                        StatusCode res = perform_userInput(buffer, m_listener->GetMessageHandler(), user_input);
                        respond_userInput(res, fifo_response, user_input);
                        break;
                    }
                    case e_UserChoice:
                    {
                        UserChoiseSet user_choises;
                        StatusCode res = perform_userChoises(buffer, m_listener->GetMessageHandler(), user_choises);
                        respond_userChoises(res, fifo_response, user_choises);
                        break;
                    }
                    case e_ProgressNotification:
                    {
                        StatusCode res = perform_progressNotification(buffer, m_listener->GetMessageHandler());
                        respond_progressNotification(res, fifo_response);
                        break;
                    }
                    default:
                    {
                        break;
                    }
                    };
                }
            }
            else
            {
                releaseFifos(fifo_request, fifo_response);
                LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "read from request fifo failed");
                break;
            }
        }
    }
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << MessageListenerActionThread::run with fail");
    return;
}
//------------------------------------------------------------------------------------------------------
StatusCode perform_Display(const Buffer& buffer, IMessageHandler* handler)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> perform_Display");

    String message, pars;
    UIOptionalParameters opt_pars;
    if (!extractComplexRequestPayload(buffer.GetPointer(), buffer.Size(), message, pars, 0, 0))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract request payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_Display. Return status: %d", e_Failed);
        return e_Failed;
    }

    if (!formOptParsFromPayload(pars, opt_pars))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract optional parameters from payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_Display. Return status: %d", e_Failed);
        return e_Failed;
    }

    long min_dt = -1, max_dt = -1;
    get_MINDT(opt_pars, min_dt);
    get_MAXDT(opt_pars, max_dt);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "BEFORE CALL IMessageHandler::Display: message= %s, min time = %ld, max time = %ld", message.c_str(), min_dt, max_dt);
    StatusCode input_res = handler->Display(message.c_str(), min_dt, max_dt);
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "AFTER CALL IMessageHandler::Display: result status = %d", input_res);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_Display. Return status: %d", input_res);
    return input_res;
}
//------------------------------------------------------------------------------------------------------
StatusCode respond_Display(StatusCode status, IFIFOWrapper* response_fifo)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> respond_Display. Status code for response = %d", status);

    UIResponseExchangeHead head(status, 0 /* payload size for display == 0 */);
    Buffer response(sizeof(head) + head.m_payload_size);
    memcpy(response.GetPointer(), &head, sizeof(head));

	bool response_res = (response_fifo->Write(response) == e_Ok) ? true : false;
    if (!response_res)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "write to response fifo failed");
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << respond_Display. Return status: %d", response_res ? e_Ok : e_Failed);
    return (response_res ? e_Ok : e_Failed);
}
//------------------------------------------------------------------------------------------------------
StatusCode perform_Confirmation(const Buffer& buffer, IMessageHandler* handler)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> perform_Confirmation");

    String message, pars;
    UIOptionalParameters opt_pars;
    if (!extractComplexRequestPayload(buffer.GetPointer(), buffer.Size(), message, pars, 0, 0))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract request payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_Confirmation. Return status: %d", e_Failed);
        return e_Failed;
    }

    if (!formOptParsFromPayload(pars, opt_pars))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract optional parameters from payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_Confirmation. Return status: %d", e_Failed);
        return e_Failed;
    }

    long min_dt = -1, max_dt = -1;
    get_MINDT(opt_pars, min_dt);
    get_MAXDT(opt_pars, max_dt);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "BEFORE CALL IMessageHandler::Confirmation: message= %s, min time = %ld, max time = %ld", message.c_str(), min_dt, max_dt);
    StatusCode input_res = handler->Confirmation(message.c_str(), min_dt, max_dt);
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "AFTER CALL IMessageHandler::Confirmation: result status = %d", input_res);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_Confirmation. Return status: %d", input_res);
    return input_res;
}
//------------------------------------------------------------------------------------------------------
StatusCode respond_Confirmation(StatusCode status, IFIFOWrapper* response_fifo)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> respond_Confirmation. Status code for response = %d", status);

    UIResponseExchangeHead head(status, 0 /* payload size for confirmation == 0 */);
    Buffer response(sizeof(head) + head.m_payload_size);
    memcpy(response.GetPointer(), &head, sizeof(head));

    bool response_res = (response_fifo->Write(response) == e_Ok) ? true : false;
    if (!response_res)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "write to response fifo failed");
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << respond_Confirmation. Return status: %d", response_res ? e_Ok : e_Failed);
    return (response_res ? e_Ok : e_Failed);
}
//------------------------------------------------------------------------------------------------------
StatusCode perform_userInput(const Buffer& buffer, IMessageHandler* handler, String& input)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> perform_userInput");

    String message, pars;
    UIOptionalParameters opt_pars;
    if (!extractComplexRequestPayload(buffer.GetPointer(), buffer.Size(), message, pars, 0, 0))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract request payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_userInput. Return status: %d", e_Failed);
        return e_Failed;
    }

    if (!formOptParsFromPayload(pars, opt_pars))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract optional parameters from payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_userInput. Return status: %d", e_Failed);
        return e_Failed;
    }

    long min_dt = -1, max_dt = -1, max_len = -1;
    bool max_len_present = false;
    bool echo_password;
    bool echo_password_present = false;

    get_MINDT(opt_pars, min_dt);
    get_MAXDT(opt_pars, max_dt);
    if (get_MAXLEN(opt_pars, max_len))
    {
        max_len_present = true;
    }
    if (get_ET(opt_pars, echo_password))
    {
        echo_password_present = true;
    }

    char* user_input = 0;


    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog),
        "BEFORE CALL IMessageHandler::UserInput: message= %s, min time = %ld, max time = %ld, max length = %d, echo mode =%d",
        message.c_str(), min_dt, max_dt, max_len_present ? (&max_len) : 0, echo_password_present ? echo_password : 0);

    StatusCode input_res = handler->UserInput(message.c_str(), min_dt, max_dt, user_input,
        max_len_present ? (&max_len) : 0, echo_password_present ? echo_password : 0);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog),
        "AFTER CALL IMessageHandler::UserInput: user input = %s, result status = %d", user_input ? user_input : "no input", input_res);

    input = user_input;
    handler->ReleaseString(user_input);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_userInput. Return status: %d", input_res);
    return input_res;
}
//------------------------------------------------------------------------------------------------------
StatusCode respond_userInput(StatusCode status, IFIFOWrapper* response_fifo, const String& input)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> respond_userInput. Status code for response = %d", status);

    UIResponseExchangeHead head(status, (status == e_Ok) ? (input.length() + 1) : 0);
    Buffer response(sizeof(head) + head.m_payload_size);
    memcpy(response.GetPointer(), &head, sizeof(head));
    memcpy( ((char*)response.GetPointer()) + sizeof(head), input.c_str(), input.length());
    ((char*)response.GetPointer())[sizeof(head) + input.length()] = '\0';

	bool response_res = (response_fifo->Write(response) == e_Ok) ? true : false;
    if (!response_res)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "write to response fifo failed");
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << respond_userInput. Return status: %d", response_res ? e_Ok : e_Failed);
    return (response_res ? e_Ok : e_Failed);
}
//------------------------------------------------------------------------------------------------------
StatusCode perform_userChoises(const Buffer& buffer, IMessageHandler* handler, UserChoiseSet& choise)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> perform_userChoises");

    String message, pars;
    UIOptionalParameters opt_pars;
    String choises;
    if (!extractComplexRequestPayload(buffer.GetPointer(), buffer.Size(), message, pars, &choises, 0))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract request payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_userChoises. Return status: %d", e_Failed);
        return e_Failed;
    }

    bool allowMultipleChoises = false;
    if (!formOptParsFromPayload(pars, opt_pars, &allowMultipleChoises))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract optional parameters from payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_userChoises. Return status: %d", e_Failed);
        return e_Failed;
    }

    long min_dt = -1, max_dt = -1;
    get_MINDT(opt_pars, min_dt);
    get_MAXDT(opt_pars, max_dt);

    char* user_choise = 0;

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog),
        "BEFORE CALL IMessageHandler::UserChoice: message= %s, min time = %ld, max time = %ld, avialable choise set = %s, allow multiple choises =%d",
        message.c_str(), min_dt, max_dt, choises.c_str(), allowMultipleChoises);

    StatusCode input_res = handler->UserChoice(message.c_str(), min_dt, max_dt, choises.c_str(), user_choise, allowMultipleChoises);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog),
        "AFTER CALL IMessageHandler::UserChoice: user choise = %s, result status = %d", user_choise ? user_choise : "no choise", input_res);

    if (input_res == e_Ok)
    {
        String usr_choise = user_choise;
        if (!getNumericUserChoise(choises, usr_choise, choise))
        {
            LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't get numeric user choises from string representation");
            input_res = e_Failed;
        }
    }
    handler->ReleaseString(user_choise);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_userChoises. Return status: %d", input_res);
    return input_res;
}
//------------------------------------------------------------------------------------------------------
StatusCode respond_userChoises(StatusCode status, IFIFOWrapper* response_fifo, const UserChoiseSet& choise)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> respond_userChoises. Status code for response = %d", status);

    String str_choises;
    formStringFromNumericUserChoise(choise, str_choises);

    UIResponseExchangeHead head(status, (status == e_Ok) ? (str_choises.length() + 1) : 0);
    Buffer response(sizeof(head) + head.m_payload_size);
    memcpy(response.GetPointer(), &head, sizeof(head));
    memcpy( ((char*)response.GetPointer()) + sizeof(head), str_choises.c_str(), str_choises.length());
    ((char*)response.GetPointer())[sizeof(head) + str_choises.length()] = '\0';

	bool response_res = (response_fifo->Write(response) == e_Ok) ? true : false;
    if (!response_res)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "write to response fifo failed");
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << respond_userChoises. Return status: %d", response_res ? e_Ok : e_Failed);
    return (response_res ? e_Ok : e_Failed);
}
//------------------------------------------------------------------------------------------------------
StatusCode perform_progressNotification(const Buffer& buffer, IMessageHandler* handler)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> perform_progressNotification");

    String message, pars;
    UIOptionalParameters opt_pars;
    String progress;
    if (!extractComplexRequestPayload(buffer.GetPointer(), buffer.Size(), message, pars, 0, &progress))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract request payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_progressNotification. Return status: %d", e_Failed);
        return e_Failed;
    }

    if (!formOptParsFromPayload(pars, opt_pars))
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "%s", "can't extract optional parameters from payload");
        LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_progressNotification. Return status: %d", e_Failed);
        return e_Failed;
    }

    long min_dt = -1, max_dt = -1;
    get_MINDT(opt_pars, min_dt);
    get_MAXDT(opt_pars, max_dt);

    long long_progress = 0;

	#if defined PLATFORM_WINDOWS
		sscanf_s(progress.c_str(), "%ld", &long_progress);
	#else
		sscanf(progress.c_str(), "%ld", &long_progress);
	#endif

	LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog),
        "BEFORE CALL IMessageHandler::ProgressNotification: message= %s, min time = %ld, max time = %ld, progress =%ld",
        message.c_str(), min_dt, max_dt, long_progress);

    StatusCode progress_res = handler->ProgressNotification(message.c_str(), min_dt, max_dt, long_progress);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog),
        "AFTER CALL IMessageHandler::ProgressNotification: result status = %d", progress_res);

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << perform_progressNotification. Return status: %d", progress_res);
    return progress_res;
}
//------------------------------------------------------------------------------------------------------
StatusCode respond_progressNotification(StatusCode status, IFIFOWrapper* response_fifo)
{
    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "ENTER >> respond_progressNotification. Status code for response = %d", status);

    UIResponseExchangeHead head(status, 0 /* payload size for notification == 0 */);
    Buffer response(sizeof(head) + head.m_payload_size);
    memcpy(response.GetPointer(), &head, sizeof(head));

	bool response_res = (response_fifo->Write(response) == e_Ok) ? true : false;
    if (!response_res)
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_MessageListenerLog), "write to response fifo failed");
    }

    LOG_DEBUG_(NS_Logging::GetLogger(c_MessageListenerLog), "LEAVE << respond_progressNotification. Return status: %d", response_res ? e_Ok : e_Failed);
    return (response_res ? e_Ok : e_Failed);
}
//------------------------------------------------------------------------------------------------------

}
