#include "precomp__gen_ff.h"
#include "main.h"
#include "../Agave/Language/api_language.h"
#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
#include "../nu/AutoWide.h"
#include "../nu/AutoChar.h"
#include "../nu/AutoCharFn.h"
#include "wa2frontend.h"
#include "resource.h"
#include "../nu/ns_wc.h"
#include "../nu/refcount.h"
#include "api/skin/widgets/xuidownloadslist.h"

#include <shlobj.h>
#include <shlwapi.h>
#include <api/script/objects/systemobj.h>

extern wchar_t *INI_DIR;

static void createDirForFileW(wchar_t *str)
{
	wchar_t *p = str;
	if ((p[0] ==L'\\' || p[0] ==L'/') && (p[1] ==L'\\' || p[1] ==L'/'))
	{
		p += 2;
		while (p && *p && *p !=L'\\' && *p !=L'/') p++;
		if (!p || !*p) return ;
		p++;
		while (p && *p && *p !=L'\\' && *p !=L'/') p++;
	}
	else
	{
		while (p && *p && *p !=L'\\' && *p !=L'/') p++;
	}

	while (p && *p)
	{
		while (p && *p !=L'\\' && *p !=L'/' && *p) p = CharNextW(p);
		if (p && *p)
		{
			wchar_t lp = *p;
			*p = 0;
			CreateDirectoryW(str, NULL);
			*p++ = lp;
		}
	}
}

static wchar_t* defaultPathToStore()
{
	static wchar_t pathToStore[MAX_PATH] = {0};
	if(FAILED(SHGetFolderPathW(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, pathToStore)))
	{
		if(FAILED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, pathToStore)))
		{
			lstrcpynW(pathToStore, L"C:\\My Music", MAX_PATH);
		}
	}
	return pathToStore;
}

static void GetPathToStore(wchar_t path_to_store[MAX_PATH])
{
	GetPrivateProfileStringW( L"gen_ml_config", L"extractpath", defaultPathToStore(), path_to_store, MAX_PATH, (const wchar_t *)SendMessageW( plugin.hwndParent, WM_WA_IPC, 0, IPC_GETMLINIFILEW ) );
}

void Winamp2FrontEnd::getDownloadPath(wchar_t path2store[MAX_PATH])
{
	GetPathToStore(path2store);
}

void Winamp2FrontEnd::setDownloadPath(const wchar_t * path2store)
{
	WritePrivateProfileStringA( "gen_ml_config", "extractpath", AutoChar( path2store, CP_UTF8 ), (const char *)SendMessageW( plugin.hwndParent, WM_WA_IPC, 0, IPC_GETMLINIFILE ) );
}

class DownloadCallback : public Countable<ifc_downloadManagerCallback>
{
public:
	DownloadCallback( const wchar_t *destination_filepath, bool storeInMl = true, bool notifyDownloadsList = true )
	{
		WCSCPYN( this->destination_filepath, destination_filepath, MAX_PATH );

		this->storeInMl           = storeInMl;
		this->notifyDownloadsList = notifyDownloadsList;
	}


	void OnFinish( DownloadToken token );

	void OnError( DownloadToken token, int error )
	{
		if ( notifyDownloadsList )
			DownloadsList::onDownloadError( token, error );

		Release();
	}

	void OnCancel( DownloadToken token )
	{
		if ( notifyDownloadsList )
			DownloadsList::onDownloadCancel( token );

		Release();
	}
	void OnTick( DownloadToken token )
	{
		if ( notifyDownloadsList )
			DownloadsList::onDownloadTick( token );
	}

	const wchar_t *getPreferredFilePath()
	{
		return destination_filepath;
	}

	bool getStoreInML()
	{
		return storeInMl;
	}
	REFERENCE_COUNT_IMPLEMENTATION;

protected:
	RECVS_DISPATCH;

private:
	bool    storeInMl;
	bool    notifyDownloadsList;
	wchar_t destination_filepath[ MAX_PATH ];
};

// TODO: benski> this is a big hack. we should have a MIME manager in Winamp
struct
{
	const char *mime; const char *ext;
}
hack_mimes[] = { {"audio/mpeg", ".mp3"} };

void DownloadCallback::OnFinish(DownloadToken token)
{
	api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver(token);
	if (http)
	{
		const char *extension = 0;
		const char *headers = http->getallheaders();
		//  we're trying to figure out wtf kind of file this is

		if (!extension) // just adding this if to make this easier to re-arrange
		{
			//   1) check if there's a content-disposition, it might have an extension
			const char *content_disposition = http->getheader("content-disposition");
			if (content_disposition)
			{
				const char *content_filename = strstr(content_disposition, "filename=");
				if (content_filename && *content_filename)
				{
					content_filename+=9;
					extension = PathFindExtensionA(content_filename);
				}
			}
		}

		if (!extension)
		{
			//   2) check MIME type for something good
			const char *content_type = http->getheader("content-type");
			if (content_type)
			{
				for (int i=0;i!=sizeof(hack_mimes)/sizeof(hack_mimes[0]);i++)
				{
					if (!strcmp(hack_mimes[i].mime, content_type))
					{
						extension=hack_mimes[i].ext;
					}
				}
			}
		}

		const char *url = http->get_url();
		if (!extension)
		{
			//   3) check if URL has an extension
		
			if (url) // umm, we better have a URL :)  but worth a check
			{
				extension = PathFindExtensionA(url);
			}
		}

		if (!extension)
		{
			//   4) ask for winamp's default extension and use that (most likely mp3)
			extension=".mp3"; // TODO: actually use the setting and not hardcode this :)
		}

		// then we rename the file
		wchar_t temppath[MAX_PATH-14] = {0};
		wchar_t filename[MAX_PATH]    = {0};

		GetTempPathW(MAX_PATH-14, temppath);
		GetTempFileNameW(temppath, L"atf", 0, filename);
		PathRemoveExtensionW(filename);
		PathAddExtensionW(filename, AutoWide(extension));
		
		const wchar_t *downloadDest = WAC_API_DOWNLOADMANAGER->GetLocation(token);
		MoveFileW(downloadDest, filename);
		
		// then we build a filename with ATF
		wchar_t formatted_filename[MAX_PATH] = {0}, path_to_store[MAX_PATH] = {0};
		if (!WCSICMP(this->getPreferredFilePath(), L""))
			GetPathToStore(path_to_store);
		else
			WCSCPYN(path_to_store, this->getPreferredFilePath(), MAX_PATH);

		// TODO: benski> this is very temporary
		char temp_filename[MAX_PATH] = {0}, *t = temp_filename,
			 *tfn = PathFindFileNameA(url);

		while(tfn && *tfn)
		{
			if(*tfn == '%')
			{
				if(_strnicmp(tfn,"%20",3))
				{
					*t = *tfn;
				}
				else{
					*t = ' ';
					tfn = CharNextA(tfn);
					tfn = CharNextA(tfn);
				}
			}
			else
			{
				*t = *tfn;
			}
			tfn = CharNextA(tfn);
			t = CharNextA(t);
		}

		*t = 0;

		PathCombineW(formatted_filename, path_to_store, AutoWide(temp_filename));
		createDirForFileW(formatted_filename);

		// then move the file there
		if (!MoveFileW(filename, formatted_filename))
		{
			CopyFileW(filename, formatted_filename, FALSE);
			DeleteFileW(filename);
		}

		//Std::messageBox(formatted_filename, filename, 0);
		if (this->getStoreInML() && PathFileExistsW(formatted_filename))
		{
			// then add to the media library :)
			// TOOD: benski> use api_mldb because it's more thread-friendly than SendMessageW
			HWND library = wa2.getMediaLibrary();
			LMDB_FILE_ADD_INFOW fi = {const_cast<wchar_t *>(formatted_filename), -1, -1};
			SendMessageW(library, WM_ML_IPC, (WPARAM)&fi, ML_IPC_DB_ADDORUPDATEFILEW);
			PostMessage(library, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB);
		}

		if (notifyDownloadsList)
			DownloadsList::onDownloadEnd(token, const_cast<wchar_t *>(formatted_filename));
		
		SystemObject::onDownloadFinished(AutoWide(url), true, formatted_filename);

		Release();

		return;
	}

	if (notifyDownloadsList)
		DownloadsList::onDownloadEnd(token, NULL);

	Release();
}

int Winamp2FrontEnd::DownloadFile( const char *url, const wchar_t *destfilepath, bool addToMl, bool notifyDownloadsList )
{
	DownloadCallback *callback = new DownloadCallback( destfilepath, addToMl, notifyDownloadsList );
	DownloadToken     dt = WAC_API_DOWNLOADMANAGER->Download( url, callback );

	// Notify <DownloadsList/>
	if ( notifyDownloadsList )
		DownloadsList::onDownloadStart( url, dt );

	return 0;
	/*
	HTTPRETRIEVEFILEW func = (HTTPRETRIEVEFILEW) SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETHTTPGETTERW);
	if (func || func == (HTTPRETRIEVEFILEW)1)
		return func(NULL, url, destfilename, title);
	else
		return 0;
		*/
}

#define CBCLASS DownloadCallback
START_DISPATCH;
REFERENCE_COUNTED;
VCB( IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, OnFinish )
VCB( IFC_DOWNLOADMANAGERCALLBACK_ONTICK,   OnTick )
VCB( IFC_DOWNLOADMANAGERCALLBACK_ONERROR,  OnError )
VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCANCEL, OnCancel )
END_DISPATCH;
#undef CBCLASS