/*
* Copyright (c) 1995-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: 
*
*/

#include <stdlib.h>
#include <string>
#include <queue>
#include <new>

#include "e32image.h"

#include <filesystem.hpp>
#include <thread/thread.hpp>
#include <thread/mutex.hpp>
#include <thread/condition_variable.hpp>

#include "cache/cacheexception.hpp"
#include "cache/cacheentry.hpp"
#include "cache/cache.hpp"
#include "cache/cachegenerator.hpp"
#include "cache/cachevalidator.hpp"
#include "cache/cacheablelist.hpp"
#include "cache/cachemanager.hpp"

using namespace std;


CacheManager* CacheManager::Only = (CacheManager*)0;
boost::mutex  CacheManager::creationlock;


CacheManager* CacheManager::GetInstance(void) throw (CacheException)
{
	if(! CacheManager::Only)
	{
		boost::mutex::scoped_lock lock(CacheManager::creationlock);
		if(! CacheManager::Only)
		{
			CacheManager::Only = new (nothrow) CacheManager();
			if(!CacheManager::Only)
				throw CacheException(CacheException::RESOURCE_ALLOCATION_FAILURE);
			CacheManager::Only->InitializeCache();
		}
	}

	return CacheManager::Only;
}


E32ImageFile* CacheManager::GetE32ImageFile(char* OriginalFilename, int CurrentCompressionID)
{
	this->NormalizeFilename(OriginalFilename);
	CacheEntry* validatedfile =CacheValidator::GetInstance()->Validate(OriginalFilename, CurrentCompressionID);
	if(! validatedfile)
		return (E32ImageFile*)0;

	printf("%s is validated in cache.\r\n", OriginalFilename);
	E32ImageFile* cachedimagefile = new (nothrow) E32ImageFile();
	if(! cachedimagefile)
		return (E32ImageFile*)0;
	boost::filesystem::path cachedfile(validatedfile->GetCachedFilename());
	ifstream filecontentreader(cachedfile.file_string().c_str(), ios_base::in | ios_base::binary);
	if(! filecontentreader.is_open())
		return (E32ImageFile*)0;
	filecontentreader.seekg(0, ios_base::end);
	int contentlength = filecontentreader.tellg();
	cachedimagefile->iData = (char*)malloc(sizeof(char)*contentlength);
	filecontentreader.seekg(0, ios_base::beg);
	filecontentreader.read(cachedimagefile->iData, contentlength);
	cachedimagefile->iHdr->iUncompressedSize = contentlength - cachedimagefile->iHdr->TotalSize();

	return cachedimagefile;
}


CacheEntry* CacheManager::GetE32ImageFileRepresentation(char* OriginalFilename, int CurrentCompressionID)
{
	this->NormalizeFilename(OriginalFilename);

	return CacheValidator::GetInstance()->Validate(OriginalFilename, CurrentCompressionID);
}


void CacheManager::Invalidate(char* Filename, CacheEntry* EntryRef) throw (CacheException)
{
	//update cache meta file.
	this->NormalizeFilename(Filename);
	Cache::GetInstance()->AddEntry(Filename, EntryRef);

	//update cache content.
	CacheableList::GetInstance()->AddCacheable(EntryRef);

	printf("Caching %s\r\n", Filename);

	return;
}


void CacheManager::CleanCache(void) throw (CacheException)
{
	//check if the cache is present in the current system.
	boost::filesystem::path cacherootdir(this->cacheroot);
	if(! exists(cacherootdir))
		throw CacheException(CacheException::CACHE_NOT_FOUND);

	//remove files iteratively from cache root directory.
	if(remove_all(cacherootdir) <= 0)
		throw CacheException(CacheException::CACHE_IS_EMPTY);

	return;
}


const char* CacheManager::GetCacheRoot(void)
{
	return this->cacheroot;
}


CacheManager::~CacheManager(void)
{
	CacheEntry* newentryref = new (nothrow) CacheEntry();
	CacheableList::GetInstance()->AddCacheable(newentryref);
	Cache::GetInstance()->CloseCache();
	CacheGenerator::GetInstance()->join();

	delete CacheValidator::GetInstance();
	delete Cache::GetInstance();
	delete CacheGenerator::GetInstance();
	delete CacheableList::GetInstance();

	return;
}


void CacheManager::InitializeCache(void) throw (CacheException)
{
	//assume the root directory is EPOCROOT/epoc32/build/.cache
	char* epocroot = getenv("EPOCROOT");
	if(! epocroot)
		throw CacheException(CacheException::EPOCROOT_NOT_FOUND);

	//initialize cacheroot member variable.
	int cacherootstrlen = sizeof(char)*(strlen(epocroot)+strlen("/epoc32/build/.cache")+1);
	this->cacheroot = (char*)malloc(cacherootstrlen);
	if(! this->cacheroot)
		throw CacheException(CacheException::RESOURCE_ALLOCATION_FAILURE);
	memset(this->cacheroot, 0, cacherootstrlen);
	sprintf(this->cacheroot, "%s%s", epocroot, "/epoc32/build/.cache");

	//normalize cache root path.
	this->NormalizeFilename(this->cacheroot);

	//create cache root directory if it is not present.
	boost::filesystem::path cacherootdir(this->cacheroot);
	if(! exists(cacherootdir))
		create_directories(cacherootdir);
	printf("Using %s as cache root directory.\r\n", cacherootdir.file_string().c_str());

	//create cache instance.
	Cache* cacheref = Cache::GetInstance();
	if(! cacheref)
		throw CacheException(CacheException::RESOURCE_ALLOCATION_FAILURE);

	//create cache validator instance.
	if(! CacheValidator::GetInstance())
		throw CacheException(CacheException::RESOURCE_ALLOCATION_FAILURE);

	//create cacheable list instance
	if(! CacheableList::GetInstance())
		throw CacheException(CacheException::RESOURCE_ALLOCATION_FAILURE);

	//initialize cache container.
	cacheref->Initialize();

	//start cache generator.
	CacheGenerator::GetInstance();

	return;
}


void CacheManager::NormalizeFilename(char* Filename)
{
	if(!Filename)
		return;

	//convert back slashes into forward slashes.
	char* normalizedfilename = Filename;
	while(*normalizedfilename)
	{
		if(*normalizedfilename == '\\')
			*normalizedfilename = '/';

		normalizedfilename++;
	}

	//remove redundant slashes.
	char* redundantfilename = Filename;
	while(*redundantfilename)
	{
		if((*redundantfilename=='/') && (*(redundantfilename+1)=='/'))
		{
			redundantfilename++;
			continue;
		}
		*Filename++ = *redundantfilename++;
	}
	*Filename = 0;

	return;
}


CacheManager::CacheManager(void)
{
	return;
}
