/*
 * 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 "CMemoryDataStorage.h"
#include "MemoryDataStorageDefs.h"

#include <algorithm>

#include "Logger/Logger.h"
#include "common/Buffer.h"

namespace NS_DM_Client
{
namespace NS_DataStorage
{

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

IStorageContent::IStorageContent(const String& name)
	: m_name(name)
{ }

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

const String& IStorageContent::Name() const
{
	return m_name;
}

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

LeafStorageContent::LeafStorageContent(const String& name, const void* value, size_t size)
	: IStorageContent(name), m_value(0), m_size(size)
{ 
	if (!((value == 0) || (m_size <= 0)))
	{
		m_value = malloc(m_size);
		memcpy(m_value, value, m_size);		
	}
}

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

LeafStorageContent::~LeafStorageContent()
{ 
	if (m_value)
	{
		free(m_value);
	}
}
	
//------------------------------------------------------------------------------------------------------

IStorageContent* LeafStorageContent::Clone()
{
	return (new LeafStorageContent(m_name, m_value, m_size));
}

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

bool LeafStorageContent::GetValue(void*& value, size_t& size) 
{ 
	value = m_value;
	size = m_size;
	return true;
} 

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

bool LeafStorageContent::GetSize(size_t& size)
{
	size = m_size;
	return true;
}

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

bool LeafStorageContent::SetValue(const void* value, size_t size) 
{
	if (m_value)
	{
		free(m_value);
		m_value = 0;
		m_size = 0;
	}
	
	if (!((value == 0) || (m_size <= 0)))
	{
		m_size = size;
		m_value = malloc(m_size);
		memcpy(m_value, value, m_size);
	}

	return true;
} 

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

DirectoryStorageContent::DirectoryStorageContent(const String& name)
	: IStorageContent(name)
{ 
	m_current = m_unders.end();
}

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

DirectoryStorageContent::~DirectoryStorageContent()
{
	for (Unders::iterator iter = m_unders.begin(); iter != m_unders.end(); ++iter)
	{
		delete (*iter);
	}
}

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

IStorageContent* DirectoryStorageContent::Clone()
{
	IStorageContent* clon = new DirectoryStorageContent(m_name);
	for (Unders::iterator iter = m_unders.begin(); iter != m_unders.end(); ++iter)
	{
		clon->AddUnder((*iter)->Clone());
	}
	return clon;
}

//------------------------------------------------------------------------------------------------------
	
IStorageContent* DirectoryStorageContent::GetFirstChildren()
{
	for (m_current = m_unders.begin(); m_current != m_unders.end(); ++m_current)
	{
		if (!((*m_current)->IsLeaf()))
		{
			return (*m_current);
		}
	}
	return 0;
}

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

IStorageContent* DirectoryStorageContent::GetNextChildren()
{
	for (++m_current; m_current != m_unders.end(); ++m_current)
	{
		if (!((*m_current)->IsLeaf()))
		{
			return (*m_current);
		}
	}
	StopGetChildren();
	return 0;
}

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

void DirectoryStorageContent::StopGetChildren()
{
	m_current = m_unders.end();
}

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

IStorageContent* DirectoryStorageContent::GetUnderByName(const String& name)
{
	for (Unders::iterator iter = m_unders.begin(); iter != m_unders.end(); ++iter)
	{
		if ((*iter)->Name().compare(name) == 0)
		{
			return (*iter);
		}
	}
	return 0;
}

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

bool DirectoryStorageContent::AddUnder(IStorageContent* children)
{
	if (children)
	{
		m_unders.push_back(children);	
		return true;
	}
	return false;
}

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

bool DirectoryStorageContent::DeleteUnder(const String& name)
{
	for (Unders::iterator iter = m_unders.begin(); iter != m_unders.end(); ++iter)
	{
		if ((*iter)->Name().compare(name) == 0)
		{
			delete (*iter);
			m_unders.erase(iter); // TODO optimize with remove(erase)
			return true;
		}
	}
	return false;
}

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

CMemoryDataStorage::CMemoryDataStorage(const String& profile) : m_base_path(""), m_content(0)
{	// profile - no need profile in memory realization
}

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

bool CMemoryDataStorage::Init(const String& base_path)
{	// base path need only for GetBasePath() member method

	m_base_path = base_path.empty() ? c_privateDataPath : base_path;
	return loadInitialTree();
}

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

void CMemoryDataStorage::Release()
{
	delete m_content;
	delete this;
}

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

IStreamHandler* CMemoryDataStorage::CreateStream()
{
    return 0;
}

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

bool CMemoryDataStorage::SavePrivateData(const String& key, const void* buffer, size_t size, bool profileSpecific)
{
    bool res = ((key.length() > 0));
    if (res)
    {
        if (createPrivateDataPath(key.c_str(), profileSpecific))
        {
            if (!(res = savePrivateData(key.c_str(), buffer, size)))
            {
                LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "save private data to memory is failed. key: %s", key.c_str());
            }
        }
        else
        {
            LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "can't create directories in memory from key path");
        }
    }
    else
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "key lenght, input buffer or buffer size = 0. key: %s", key.c_str());
    }
    return res;
}

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

bool CMemoryDataStorage::SavePrivateData(const String& key, const Buffer& buffer, bool profileSpecific)
{
    return SavePrivateData(key, buffer.GetPointer(), buffer.Size(), profileSpecific);
}

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

bool CMemoryDataStorage::LoadPrivateData(const String& key, Buffer& buffer, bool profileSpecific)
{
    bool res = (key.length() > 0);
    if (res)
    {
        void* tmpbuff = 0;
        size_t sizebuff = 0;

        if((res = loadPrivateData(key.c_str(), tmpbuff, sizebuff)) == true)
        {
            if (sizebuff == 0)
            {
                buffer.Allocate(0);
            }
            else
            {
                if ((res = buffer.Allocate(sizebuff)))
                {
                    memcpy(buffer.GetPointer(), tmpbuff, sizebuff);
                }
                else
                {
                    LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "can't allocate buffer. key: %s", key.c_str());
                }
                free(tmpbuff);
            }
        }
    }
    else
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "key lenght = 0. key: %s", key.c_str());
    }
    return res;
}

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

bool CMemoryDataStorage::GetChildList(const String& key, StringArray& list, bool profileSpecific)
{
	bool res = (key.length() > 0);
    if (res)
    {	
		IStorageContent* key_node = getNodeByFullKeyPath(key.c_str());
		if (key_node && (!key_node->IsLeaf()))
		{
			res = true;				
			IStorageContent* current = key_node->GetFirstChildren();
			while (current)
			{
				list.push_back(current->Name());
				current = key_node->GetNextChildren();
			}
			key_node->StopGetChildren();
		}
		else
		{
			LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "can't get node by path. key: %s", key.c_str());
			res = false;
		}
	}
	else
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "key lenght = 0. key: %s", key.c_str());
    }

    if (res)
    {
	sort(list.begin(), list.end());
    }

    return res;
}

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

bool CMemoryDataStorage::Exist(const String& key, bool profileSpecific)
{
	bool res = (key.length() > 0);
    if (res)
    {
		IStorageContent* key_node = getNodeByFullKeyPath(key.c_str());
		res = (key_node && (!key_node->IsLeaf())) ? true : false;
	}
    else
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "key lenght = 0. key: %s", key.c_str());
    }
    return res;
}

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

bool CMemoryDataStorage::RemovePrivateData(const String& key)
{
    bool res = (key.length() > 0);
    if (res)
    {
		IStorageContent* key_node = getParentNodeOfKeyByFullKeyPath(key.c_str());
		if (key_node)
		{
			res = key_node->DeleteUnder(getKeyNameFromPath(key.c_str()));
		}
		else
		{
			LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "can't get parent node of key. key: %s", key.c_str());
			res = false;
		}
	}
    else
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "key lenght = 0. key: %s", key.c_str());
    }
    return res;
}
//------------------------------------------------------------------------------------------------------

IStorageContent* CMemoryDataStorage::getParentNodeOfKeyByFullKeyPath(const char* key)
{
	String Key = key;
	size_t pos_beg = 0;
	size_t pos_end = 0;
	String dir_name;

	IStorageContent* current = m_content;
	IStorageContent* under = 0;
	
	while ( (pos_end = Key.find(c_pathSeparator, pos_beg)) != String::npos )
	{
		dir_name = Key.substr(pos_beg, pos_end - pos_beg);
		if (m_content->Name().compare(dir_name) != 0)
		{
			under = current->GetUnderByName(dir_name);
			if (under && (!under->IsLeaf())) 
			{	// directory sub-path exist
				current = under;
			}
			else 
			{	// directory sub-path not exist. Error
				return 0;
			}
		}
		pos_beg = pos_end + 1;
	}

	return current;
}

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

const char* CMemoryDataStorage::getKeyNameFromPath(const char* key)
{
	const char* res = key + strlen(key);
	while (*(--res) != '/')
	{
		if (res == key)
		{	
			return 0;
		}
	}
	return ++res;
}

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

bool CMemoryDataStorage::savePrivateData(const char* key, const void* buffer, size_t size)
{
	bool res = false;

	IStorageContent* directory = getParentNodeOfKeyByFullKeyPath(key);
	if (directory)
	{
		IStorageContent* leaf_node = new LeafStorageContent(getKeyNameFromPath(key), buffer, size);
		if (leaf_node)
		{
			res = directory->AddUnder(leaf_node);
		}
		else
		{
			LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "failed to create leaf node for key. key: %s", key);
		}
	}
	else
	{
		LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "can't get parent node of key. key: %s", key);
	}

    return res;
}

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

bool CMemoryDataStorage::loadPrivateData(const char* key, void*& buffer, size_t& size)
{
    bool res = false;

    if (readBufferSize(key, size))
    {
        if (size == 0)
        {
            res = true;
        }
        else
        {
			IStorageContent* leaf_node = getLeafNodeByFullKeyPath(key);
			if (leaf_node)
			{
				void* content = 0;
				if (leaf_node->GetValue(content, size))
				{
					if ((buffer = malloc(size)) != 0)
					{
						memcpy(buffer, content, size);
						res = true;
					}
					else
					{
						LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "can't allocate buffer. key : %s", key);
					}
				}
				else
				{
					LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "can't get value of leaf node of key. key: %s", key);
				}
			}
			else
			{
				LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "can't get leaf node of key. key: %s", key);
			}
        }
    }
    else
    {
        LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "failed to read buffer size. key : %s", key);
    }

    return res;
}

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

IStorageContent* CMemoryDataStorage::getNodeByFullKeyPath(const char* key)
{	
	IStorageContent* parent_node = getParentNodeOfKeyByFullKeyPath(key);
	if (parent_node)
	{
		IStorageContent* key_node = parent_node->GetUnderByName(getKeyNameFromPath(key));
		if (key_node)
		{
			return key_node;
		}
	}
	return 0;
}

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

IStorageContent* CMemoryDataStorage::getLeafNodeByFullKeyPath(const char* key)
{
	IStorageContent* key_node = getNodeByFullKeyPath(key);
	if (key_node && key_node->IsLeaf())
	{
		return key_node;
	}
	return 0;
}

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

bool CMemoryDataStorage::readBufferSize(const char* key, size_t& size)
{
	IStorageContent* leaf_node = getLeafNodeByFullKeyPath(key);
	if (leaf_node)
	{
		return leaf_node->GetSize(size);
	}
	else
	{
		LOG_ERROR_(NS_Logging::GetLogger(c_DataStorageLog), "can't get leaf node of key. key: %s", key);
	}

	return false;
}

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

bool CMemoryDataStorage::createPrivateDataPath(const char* key, bool profileSpecific)
{
	String Key = key;
    size_t pos_beg = 0;
    size_t pos_end = 0;
    String dir_name;
	
	IStorageContent* current = m_content;
	IStorageContent* under = 0;
	IStorageContent* directory = 0;
	while ( (pos_end = Key.find(c_pathSeparator, pos_beg)) != String::npos )
    {
		dir_name = Key.substr(pos_beg, pos_end - pos_beg);
		if (m_content->Name().compare(dir_name) != 0)
		{
			under = current->GetUnderByName(dir_name);
			if (under && (!under->IsLeaf())) 
			{	// directory already exist
				current = under;
			}
			else if (under && under->IsLeaf())
			{	// node already exist but not directory. Error
				return false;
			}
			else if (!under)
			{	// directory not exist. Create
				directory = new DirectoryStorageContent(dir_name); 
				if (directory)
				{
					if (current->AddUnder(directory))
					{
						current = directory;
					}
					else
					{
						delete directory;
						return false;
					}
				}
			}
		}
		pos_beg = pos_end + 1;
    }
    return true;
}

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

const char* CMemoryDataStorage::GetBasePath()
{
    return m_base_path.c_str();
}

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

bool CMemoryDataStorage::GetPrivateDataSize(const String& key, size_t& size, bool profileSpecific)
{
    return readBufferSize(key.c_str(), size);
}

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

bool CMemoryDataStorage::loadInitialTree()
{
	/*
	IStorageContent* DMAcc_0_AppAddr_moon_Addr_node = new DirectoryStorageContent("Addr");
	DMAcc_0_AppAddr_moon_Addr_node->AddUnder(new LeafStorageContent("ACL", "", 0));
	DMAcc_0_AppAddr_moon_Addr_node->AddUnder(new LeafStorageContent("Format", "chr", 3));
	DMAcc_0_AppAddr_moon_Addr_node->AddUnder(new LeafStorageContent("Type", "text/plain", 10));
	DMAcc_0_AppAddr_moon_Addr_node->AddUnder(new LeafStorageContent("content", "http://idovgaliuk-pc:8080/funambol/dm", 37));

	IStorageContent* DMAcc_0_AppAddr_moon_AddrType_node = new DirectoryStorageContent("AddrType");
	DMAcc_0_AppAddr_moon_Addr_node->AddUnder(new LeafStorageContent("ACL", "", 0));
	DMAcc_0_AppAddr_moon_Addr_node->AddUnder(new LeafStorageContent("Format", "chr", 3));
	DMAcc_0_AppAddr_moon_Addr_node->AddUnder(new LeafStorageContent("Type", "text/plain", 10));
	DMAcc_0_AppAddr_moon_Addr_node->AddUnder(new LeafStorageContent("content", "URI", 3));

	IStorageContent* DMAcc_0_AppAddr_moon_node = new DirectoryStorageContent("moon");
	DMAcc_0_AppAddr_moon_node->AddUnder(new LeafStorageContent("ACL", "", 0));
	DMAcc_0_AppAddr_moon_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	DMAcc_0_AppAddr_moon_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	DMAcc_node->AddUnder(DMAcc_0_AppAddr_moon_Addr_node);
	DMAcc_node->AddUnder(DMAcc_0_AppAddr_moon_AddrType_node);
	
*/
	IStorageContent* DMAcc_bomba_node = new DirectoryStorageContent("0");
	DMAcc_bomba_node->AddUnder(new LeafStorageContent("ACL", "", 0));
	DMAcc_bomba_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	DMAcc_bomba_node->AddUnder(new LeafStorageContent("Type", "node", 4));

	IStorageContent* DMAcc_node = new DirectoryStorageContent("DMAcc");
	DMAcc_node->AddUnder(new LeafStorageContent("ACL", "", 0));
	DMAcc_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	DMAcc_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	DMAcc_node->AddUnder(new LeafStorageContent("permanent", "true", 4));
	DMAcc_node->AddUnder(DMAcc_bomba_node);
	
	
	
	
	
	
	
	
	IStorageContent* DevDetail_node = new DirectoryStorageContent("DevDetail");
	DevDetail_node->AddUnder(new LeafStorageContent("ACL", "Get=*", 5));
	DevDetail_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	DevDetail_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	DevDetail_node->AddUnder(new LeafStorageContent("permanent", "true", 4));
	
	IStorageContent* DevInfo_node = new DirectoryStorageContent("DevInfo");
	DevInfo_node->AddUnder(new LeafStorageContent("ACL", "Get=*", 5));
	DevInfo_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	DevInfo_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	DevInfo_node->AddUnder(new LeafStorageContent("permanent", "true", 4));
	
	IStorageContent* WiMAX_node = new DirectoryStorageContent("WiMAX");
	WiMAX_node->AddUnder(new LeafStorageContent("ACL", "", 0));
	WiMAX_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	WiMAX_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	WiMAX_node->AddUnder(new LeafStorageContent("permanent", "true", 4));
	
	IStorageContent* WiMAX_Diagnostics_Generic_node = new DirectoryStorageContent("Generic");
	WiMAX_Diagnostics_Generic_node->AddUnder(new LeafStorageContent("ACL", "Get=*", 5));
	WiMAX_Diagnostics_Generic_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	WiMAX_Diagnostics_Generic_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	WiMAX_Diagnostics_Generic_node->AddUnder(new LeafStorageContent("permanent", "true", 4));
	
	IStorageContent* WiMAX_Diagnostics_Start_node = new DirectoryStorageContent("Start");
	WiMAX_Diagnostics_Start_node->AddUnder(new LeafStorageContent("ACL", "Exec=*", 6));
	WiMAX_Diagnostics_Start_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	WiMAX_Diagnostics_Start_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	WiMAX_Diagnostics_Start_node->AddUnder(new LeafStorageContent("permanent", "true", 4));

	IStorageContent* WiMAX_Diagnostics_WiMAX_node = new DirectoryStorageContent("WiMAX");
	WiMAX_Diagnostics_WiMAX_node->AddUnder(new LeafStorageContent("ACL", "Get=*", 5));
	WiMAX_Diagnostics_WiMAX_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	WiMAX_Diagnostics_WiMAX_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	WiMAX_Diagnostics_WiMAX_node->AddUnder(new LeafStorageContent("permanent", "true", 4));

	IStorageContent* WiMAX_Diagnostics_node = new DirectoryStorageContent("WiMAX_Diagnostics");
	WiMAX_Diagnostics_node->AddUnder(new LeafStorageContent("ACL", "Get=*", 5));
	WiMAX_Diagnostics_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	WiMAX_Diagnostics_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	WiMAX_Diagnostics_node->AddUnder(new LeafStorageContent("permanent", "true", 4));
	WiMAX_Diagnostics_node->AddUnder(WiMAX_Diagnostics_Generic_node);
	WiMAX_Diagnostics_node->AddUnder(WiMAX_Diagnostics_Start_node);
	WiMAX_Diagnostics_node->AddUnder(WiMAX_Diagnostics_WiMAX_node);

	IStorageContent* root_node = new DirectoryStorageContent(c_root_key);
	root_node->AddUnder(new LeafStorageContent("ACL", "Add=*&Get=*", 11));
	root_node->AddUnder(new LeafStorageContent("Format", "node", 4));
	root_node->AddUnder(new LeafStorageContent("Type", "node", 4));
	root_node->AddUnder(new LeafStorageContent("permanent", "true", 4));
	root_node->AddUnder(DMAcc_node);
	root_node->AddUnder(DevDetail_node);
	root_node->AddUnder(DevInfo_node);
	root_node->AddUnder(WiMAX_node);
	root_node->AddUnder(WiMAX_Diagnostics_node);

	m_content = root_node;
	return true;
}

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

}
}
