/*
 * 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 "daemon/DaemonEngine.h"
#include "common/Buffer.h"
#include "DataStorage/IDataStorage.h"
#include "base/util/StringBuffer.h"
#include "daemon/Configuration.h"
#include "daemon/Profile.h"

#include "Logger/LoggerMacroses.h"
#include "ClientAdapter/ClientAdapterStub.h"

#include "Errors.h"

#include <memory>

using namespace NS_DM_Client;
using namespace NS_DM_Client::NS_Daemon;

static const char c_LoggerInstanceName[] = "DaemonEngine";

IDaemonEngine*	IDaemonEngine::GetInstance()
{
	static IDaemonEngine* engine = new DaemonEngine();
	return engine;
}


IDaemonEngine::IDaemonEngine()
{

}


DaemonEngine::DaemonEngine()
:
m_Started(false),
m_logger(NS_Logging::GetLogger(c_LoggerInstanceName)),
m_ConfStorage(0),
m_conf(m_logger),
m_clientAdapterStub(0)
{

}


DaemonEngine::~DaemonEngine()
{
    if (isRunning())
    {
        LWARN("Still running while being destructed. ");
        Stop();
    }

    if (m_ConfStorage)
    {
        m_ConfStorage->Release();
        m_ConfStorage = NULL;
    }

    if (m_clientAdapterStub)
    {
    	delete m_clientAdapterStub;
    	m_clientAdapterStub = NULL;
    }
}

bool DaemonEngine::Start()
{
    if (isRunning())
    {
        LWARN("Already running. ");
        return true;
    }

    NS_DM_Client::NS_DataStorage::IConfigurationStorage* pCS = getConfigurationStorage();
    if (!pCS)
    {
        LERROR("Failed to create ConfigurationStorage instance. ");
        return false;
    }
    Buffer buf;
    if (!pCS->LoadConfiguration(buf))
    {
        LERROR("Failed to load configuration. ");
        return false;
    }

	const size_t configSize = buf.Size();
	if (buf.GetPointer() == NULL || configSize == 0)
	{
        LERROR("Configuration is empty. ");
        return false;
	}

	LDEBUG("Configuration size: '%d'. ", configSize);
	String config(static_cast<const char*>(buf.GetPointer()), configSize);
    StringBuffer configData(config.c_str());
    if (!m_conf.Read(configData))
    {
        LERROR("Failed to read configuration. ");
        return false;
    }

    if (!m_clientAdapterStub)
    {
    	do
    	{
    		std::auto_ptr<ClientAdapterStub> cas(new ClientAdapterStub(m_conf));
			LDEBUG("ClientAdapaterStub=%p", cas.get());
			if (cas.get())
			{
				const bool initResult = cas->Init();
				LDEBUG("ClientAdapaterStub init result=%d", initResult);
				if (!initResult)
				{
					break;
				}

				const bool startResult = cas->Start();
				LDEBUG("ClientAdapaterStub started=%d", startResult);
				if (!startResult)
				{
					break;
				}

				m_clientAdapterStub = cas.get();
				cas.release();
			}
			else
			{
				LERROR("Failed to create ClientAdapaterStub");
			}
    	}while(false);
    }

    m_Started = true;
    return isRunning();
}

bool DaemonEngine::Stop()
{
    if (!isRunning())
    {
        LWARN("Asked to stop while not running. ");
        return true;
    }

	if (m_clientAdapterStub)
	{
		LDEBUG("Try to stop ClientAdapaterStub");
		const bool stopResult = m_clientAdapterStub->Stop();
		LDEBUG("ClientAdapaterStub stopped=%d", stopResult);
	}

    for(int i = 0; i < m_conf.GetProfilesCount(); ++i)
    {
        Profile* p = m_conf.GetProfile(i);
        if (p->IsEnabled())
        {
			LDEBUG("Try to stop profile '%s'", p->GetName().c_str());
            bool res = p->Enable(false);
            LINFO("Profile %s Enable=false status bRes=%d. ", p->GetName().c_str(), res);
        }
    }
    m_Started = false;

	return true;
}

StatusCode DaemonEngine::HandleClientRequest(const UIMessageType& type, const Buffer& input, StatusCode& response_res, Buffer& output)
{
	StatusCode res = e_Failed;
	response_res = e_Failed;
	switch(type)
	{
    	case e_GetTreeValue:
        {
        	res = execute_RequestGetValueFromTreeCommand(input, response_res, output);
    	    break;
        }
        case e_RequestUserInitiatedFirmwareUpdate:
        {
            res = execute_RequestFirmwareUpdate(true, input);
            break;
        }
        case e_RequestDeviceInitiatedFirmwareUpdate:
        {
            res = execute_RequestFirmwareUpdate(false, input);
            break;
        }
        case e_GetProfiles:
        {
        	res = execute_RequestGetProfiles(input, response_res, output);
    	    break;
        }
        case e_SetProfileState:
        {
        	res = execute_RequestSetProfileState(input, response_res, output);
    	    break;
        }
    	default:
        {
        	LERROR("no supported client request type");
		    response_res = e_ForbiddenCommand;
		    res = e_ForbiddenCommand;
        }
	};
	response_res = res;
	return res;
}

bool DaemonEngine::isRunning() const
{
    return m_Started;
}

NS_DM_Client::NS_DataStorage::IConfigurationStorage*   DaemonEngine::getConfigurationStorage()
{
    if (!m_ConfStorage)
    {
        m_ConfStorage = NS_DM_Client::NS_DataStorage::CreateConfigurationStorage();
    }

    return m_ConfStorage;
}
