/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
----------------------------------------------------------------------------- */


		#include "filesystem/io_crypto.h"

		using namespace GS::IO;


//------------------------------------------------------------------------------
uint			Crypto::GetCaps() const
{	return wrapped_io->GetCaps();	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
void			Crypto::Encrypt(GS::Array <char> &data)
{
	size_t len = key.Len();
	for	(size_t n = 0; n < data.GetSize(); ++n)
		data[(int)n] = data[(int)n] ^ key[(int)(n % len)];
}
void			Crypto::Decrypt(GS::Array <char> &data)
{
	size_t len = key.Len();
	for	(size_t n = 0; n < data.GetSize(); ++n)
		data[(int)n] = data[(int)n] ^ key[(int)(n % len)];
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
Handle			*Crypto::Open(const char *path, Mode mode)
{
	/*
		When opening a file in read mode, the wrapped io file is decrypted and
		stored in the cached io file system for further access.
		When opening a file in write mode, the file is first created in clear
		on the cached io then encrypted and committed to the wrapped io.
	*/
	Handle *h = NULL;

	switch (mode)
	{
		case ModeRead:
		{
			// Load wrapped IO file.
			AutoPtr <Handle> _h(wrapped_io->Open(path, mode));
			if	(_h.IsNull())
				break;

			size_t size = _h->GetSize();
			Array <char> data(size);
			if	(data.IsNull())
				break;
			if	(_h->Read(data.c_ptr(), size) != size)
				break;

			// Decrypt buffer.
			Decrypt(data);

			// Write decrypted content to cache IO.
			if	((h = cache_io->Open(path, ModeWrite)) != NULL)
				h->Write(data.c_ptr(), size);
			_safe_delete(h);

			h = cache_io->Open(path, mode);
		}
		break;

		case ModeWrite:
			h = cache_io->Open(path, mode);
			break;

		default: break;
	}

	return h ? new CryptoHandle(this, h, path, mode) : NULL;
}
void			Crypto::Close(Handle *_h)
{
	if	(CryptoHandle *h = (CryptoHandle *)_h)
	{
		h->cached_h = NULL;

		switch (h->io_mode)
		{
			case ModeRead:
				cache_io->Delete(h->path);
				break;

			case ModeWrite:
			{
				// Retrieve the whole cached file content and drop it from the cache IO.
				h->cached_h = cache_io->Open(h->path);

				size_t size = h->cached_h->GetSize();
				Array <char> data(size);

				if	(data.IsValid())
					h->cached_h->Read(data.c_ptr(), size);

				h->cached_h = NULL;
				cache_io->Delete(h->path);

				// Encrypt buffer.
				Encrypt(data);

				// Commit file to the wrapped IO.
				AutoPtr <Handle> _h(wrapped_io->Open(h->path, ModeWrite));
				if	(_h.IsValid())
					_h->Write(data.c_ptr(), size);
			}
			break;

			default: break;
		}
	}
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool			Crypto::Delete(const char *path)
{	return wrapped_io->Delete(path);	}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
size_t			Crypto::Tell(Handle *_h)
{
	if	(CryptoHandle *h = (CryptoHandle *)_h)
		return h->cached_h->Tell();
	return (size_t)-1;
}
size_t			Crypto::Seek(Handle *_h, ptrdiff_t offset, SeekRef seek)
{
	if	(CryptoHandle *h = (CryptoHandle *)_h)
		return h->cached_h->Seek(offset, seek);
	return (size_t)-1;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
size_t			Crypto::Read(Handle *_h, void *p, size_t s)
{
	if	(CryptoHandle *h = (CryptoHandle *)_h)
		return h->cached_h->Read(p, s);
	return 0;
}
size_t			Crypto::Write(Handle *_h, const void *p, size_t s)
{
	if	(CryptoHandle *h = (CryptoHandle *)_h)
		return h->cached_h->Write(p, s);
	return 0;
}
//------------------------------------------------------------------------------

//------------------------------------------------------------------------------
bool			Crypto::MkDir(const char *path)
{	return wrapped_io->MkDir(path);	}
//------------------------------------------------------------------------------

Crypto::Crypto(Base *_io, const char *_key) : wrapped_io(_io)
{
	cache_io = new Memory;
	key = _key;
}

//------------------------------------------------------------------------------
CryptoHandle::CryptoHandle(Base *io, Handle *h, const char *_path, Mode mode) : Handle(io), cached_h(h), path(_path), io_mode(mode)
{}
CryptoHandle::~CryptoHandle()
{	GetIOSystem()->Close(this);	}
//------------------------------------------------------------------------------
