#include "stdafx.h"
#include "BitcasaWrapper.h"

using namespace BitcasaDLL;

std::string BitcasaDLL::SystemStringToStdString(System::String^ str){
	using namespace Runtime::InteropServices;
	const char* chars =
		(const char*)(Marshal::StringToHGlobalAnsi(str)).ToPointer();
	std::string result = chars;
	Marshal::FreeHGlobal(IntPtr((void*)chars));
	return result;
}

std::wstring BitcasaDLL::SystemStringToStdWString(System::String^ str){
	using namespace Runtime::InteropServices;
	const wchar_t* chars =
		(const wchar_t*)(Marshal::StringToHGlobalUni(str)).ToPointer();
	std::wstring result = chars;
	Marshal::FreeHGlobal(IntPtr((void*)chars));
	return result;
}

jsonitemWrapper^ jsonitemWrapper::Create(pjson item)
{
	jsonitemWrapper^ selfitem;
	if (!item) return gcnew jsonitemWrapper;
	switch (item->type){
	case jsonitem::null:
		selfitem = gcnew jsonitemWrapper;
		break;
	case jsonitem::string:
		selfitem = gcnew json_stringWrapper;
		dynamic_cast<json_stringWrapper^>(selfitem)->SetValue(item->GetString());
		break;
	case jsonitem::number:
		selfitem = gcnew json_numberWrapper;
		dynamic_cast<json_numberWrapper^>(selfitem)->value = (item->GetDouble());
		break;
	case jsonitem::boolean:
		selfitem = gcnew jsonitemWrapper;
		break;
	case jsonitem::structure:
		if (typeid(*item) == typeid(json_object)){
			selfitem = gcnew json_objectWrapper;
			dynamic_cast<json_objectWrapper^>(selfitem)->SetMembers(&*item);
		}
		else {
			selfitem = gcnew json_arrayWrapper;
			dynamic_cast<json_arrayWrapper^>(selfitem)->SetArray(&*item);
		}
		break;
	default:
		return gcnew jsonitemWrapper;
	}
	selfitem->type = item->type;
	selfitem->json = gcnew System::String(item->json.c_str());
	return selfitem;
}

//////////////////////////////////////////////////////////////////////////////

System::Collections::Generic::List<ListLocalBitcasa^>^ ListLocalBitcasa::ListDir(System::String^ rootdir)
{
	System::Collections::Generic::List<ListLocalBitcasa^>^ diritem = gcnew System::Collections::Generic::List<ListLocalBitcasa^>;

	WIN32_FIND_DATA fd;
	std::wstring rootstr = SystemStringToStdWString(rootdir);
	HANDLE h = FindFirstFileExW((rootstr + L"*").c_str(),
		FindExInfoBasic,
		&fd,
		FindExSearchNameMatch, NULL, 0);
	if (h == INVALID_HANDLE_VALUE) {
		return diritem;
	}
	do {
		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
			if ((_tcscmp(fd.cFileName, _T(".")) == 0) ||
				(_tcscmp(fd.cFileName, _T("..")) == 0)) {
				continue;
			}
			std::wstring dirname = fd.cFileName;
			ListLocalBitcasa^ aDir = gcnew ListLocalBitcasa;
			dirname = rootstr + dirname;
			aDir->name = gcnew System::String(dirname.c_str());
			INT64 t = fd.ftCreationTime.dwLowDateTime | (INT64)fd.ftCreationTime.dwHighDateTime << 32;
			aDir->CreationTime = System::DateTime::FromFileTime(t);
			t = fd.ftLastWriteTime.dwLowDateTime | (INT64)fd.ftLastWriteTime.dwHighDateTime << 32;
			aDir->LastWriteTime = System::DateTime::FromFileTime(t);
			diritem->Add(aDir);
			diritem->AddRange(ListDir(aDir->name+"\\"));
		}
		else {
			std::wstring filename = fd.cFileName;
			ListLocalBitcasa^ aFile = gcnew ListLocalBitcasa;
			filename = rootstr + filename;
			aFile->name = gcnew System::String(filename.c_str());
			INT64 t = fd.ftCreationTime.dwLowDateTime | (INT64)fd.ftCreationTime.dwHighDateTime << 32;
			aFile->CreationTime = System::DateTime::FromFileTime(t);
			t = fd.ftLastWriteTime.dwLowDateTime | (INT64)fd.ftLastWriteTime.dwHighDateTime << 32;
			aFile->LastWriteTime = System::DateTime::FromFileTime(t);
			aFile->size = fd.nFileSizeLow | (INT64)fd.nFileSizeHigh << 32;
			diritem->Add(aFile);
		}
	} while (FindNextFile(h, &fd));
	FindClose(h);
	return diritem;
}

///////////////////////////////////////////////////////////////////////////

BitcasaWrapper::BitcasaWrapper()
{
	obj = new BitcasaAPI;
}

BitcasaWrapper::~BitcasaWrapper()
{
	delete obj;
}


//Authentication
//	Login
int BitcasaWrapper::authenticate()
{
	LoginBitcasa login;
	System::String^ authorization_code = login.Login();

	if (String::IsNullOrEmpty(authorization_code)) return 1;

	std::string authcode = SystemStringToStdString(authorization_code);

	if (authcode.empty()) return 2;

	return obj->Grant_token(authcode);
}

bool BitcasaWrapper::prepare()
{
	if (!obj->access_token.empty()) {
		auto root = obj->GetUserProfile();
		if (root && (*root)["error"]->type == jsonitem::null) return true;
	}
	if (authenticate() == 0) return true;
	return false;
}

jsonitemWrapper^ BitcasaWrapper::GetUserProfile()
{
	return jsonitemWrapper::Create(obj->GetUserProfile());
}

jsonitemWrapper^ BitcasaWrapper::ListFolder(System::String^ encoded_path)
{
	return jsonitemWrapper::Create(obj->ListFolder(SystemStringToStdString(encoded_path)));
}

jsonitemWrapper^ BitcasaWrapper::ListFolder(System::String^ encoded_path, bool deep)
{
	return jsonitemWrapper::Create(obj->ListFolder(SystemStringToStdString(encoded_path), deep));
}

bool BitcasaWrapper::AddFolder(System::String^ encoded_path, System::String^ newfoldername)
{
	return obj->AddFolder(SystemStringToStdString(encoded_path), SystemStringToStdWString(newfoldername));
}

bool BitcasaWrapper::DeleteFolder(System::String^ encoded_path)
{
	return obj->DeleteFolder(SystemStringToStdString(encoded_path));
}

bool BitcasaWrapper::RenameFolder(System::String^ encoded_path_old, System::String^ newfoldername)
{
	return obj->RenameFolder(SystemStringToStdString(encoded_path_old), SystemStringToStdWString(newfoldername));
}

bool BitcasaWrapper::CopyFolder(System::String^ encoded_path_old, System::String^ encoded_path_new, System::String^ newfoldername)
{
	return obj->CopyMoveFolder(SystemStringToStdString(encoded_path_old), SystemStringToStdString(encoded_path_new), SystemStringToStdWString(newfoldername),BitcasaAPI::copy);
}
bool BitcasaWrapper::MoveFolder(System::String^ encoded_path_old, System::String^ encoded_path_new, System::String^ newfoldername)
{
	return obj->CopyMoveFolder(SystemStringToStdString(encoded_path_old), SystemStringToStdString(encoded_path_new), SystemStringToStdWString(newfoldername), BitcasaAPI::move);
}

static System::Runtime::InteropServices::GCHandle gch;

bool BitcasaWrapper::UploadFile(System::String^ encoded_path, System::String^ infilename)
{
	return obj->UploadFile(SystemStringToStdString(encoded_path), SystemStringToStdWString(infilename));
}
bool BitcasaWrapper::UploadFile(System::String^ encoded_path, System::String^ infilename, filetrans^ callback)
{
	try{
		if (!gch.Equals(callback)) gch.Free();
	}
	catch (InvalidOperationException^){}
	gch = System::Runtime::InteropServices::GCHandle::Alloc(callback);
	IntPtr delegatePointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(callback);
	return obj->UploadFile(SystemStringToStdString(encoded_path), SystemStringToStdWString(infilename), (void(CALLBACK *)(INT64))delegatePointer.ToPointer());
}

bool BitcasaWrapper::DownloadFile(System::String^ encoded_file_id, System::String^ filename, Int64 filesize)
{
	return obj->DownloadFile(SystemStringToStdString(encoded_file_id), SystemStringToStdWString(filename), filesize);
}
bool BitcasaWrapper::DownloadFile(System::String^ encoded_file_id, System::String^ filename, Int64 filesize, filetrans^ callback)
{
	try{
		if (!gch.Equals(callback)) gch.Free();
	}
	catch (InvalidOperationException^){}
	gch = System::Runtime::InteropServices::GCHandle::Alloc(callback);
	IntPtr delegatePointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(callback);
	return obj->DownloadFile(SystemStringToStdString(encoded_file_id), SystemStringToStdWString(filename), filesize, (void(CALLBACK *)(INT64))delegatePointer.ToPointer());
}

bool BitcasaWrapper::_DeleteFile(System::String^ encoded_path)
{
	return obj->_DeleteFile(SystemStringToStdString(encoded_path));
}

bool BitcasaWrapper::RenameFile(System::String^ encoded_path_old, System::String^ newfoldername)
{
	return obj->RenameFolder(SystemStringToStdString(encoded_path_old), SystemStringToStdWString(newfoldername));
}

bool BitcasaWrapper::CopyFile(System::String^ encoded_path_old, System::String^ encoded_path_new, System::String^ newfilename)
{
	return obj->CopyMoveFolder(SystemStringToStdString(encoded_path_old), SystemStringToStdString(encoded_path_new), SystemStringToStdWString(newfilename), BitcasaAPI::copy);
}
bool BitcasaWrapper::MoveFile(System::String^ encoded_path_old, System::String^ encoded_path_new, System::String^ newfilename)
{
	return obj->CopyMoveFolder(SystemStringToStdString(encoded_path_old), SystemStringToStdString(encoded_path_new), SystemStringToStdWString(newfilename), BitcasaAPI::move);
}
