#include "./common.h"
#include "./api__ml_online.h"
#include "./main.h"

#include "../winamp/wa_ipc.h"

#include <strsafe.h>

LPWSTR Plugin_MallocString(size_t cchLen)
{
	return (LPWSTR)calloc(cchLen, sizeof(WCHAR));
}

void Plugin_FreeString(LPWSTR pszString)
{
	if (NULL != pszString)
	{
		free(pszString);
		pszString = NULL;
	}
}

LPWSTR Plugin_ReAllocString(LPWSTR pszString, size_t cchLen)
{
	return (LPWSTR)realloc(pszString, sizeof(WCHAR) * cchLen);
}

LPWSTR Plugin_CopyString(LPCWSTR pszSource)
{
	if (NULL == pszSource)
		return NULL;

	INT cchSource = lstrlenW(pszSource) + 1;
		
	LPWSTR copy = Plugin_MallocString(cchSource);
	if (NULL != copy)
	{
		CopyMemory(copy, pszSource, sizeof(WCHAR) * cchSource);
	}
	return copy;
}

LPSTR Plugin_MallocAnsiString(size_t cchLen)
{
	return (LPSTR)calloc(cchLen, sizeof(CHAR));
}

LPSTR Plugin_CopyAnsiString(LPCSTR pszSource)
{
	if (NULL == pszSource)
		return NULL;

	INT cchSource = lstrlenA(pszSource) + 1;
		
	LPSTR copy = Plugin_MallocAnsiString(cchSource);
	if (NULL != copy)
	{
		CopyMemory(copy, pszSource, sizeof(CHAR) * cchSource);
	}
	return copy;

}
void Plugin_FreeAnsiString(LPSTR pszString)
{
	Plugin_FreeString((LPWSTR)pszString);
}

LPSTR Plugin_WideCharToMultiByte(UINT codePage, DWORD dwFlags, LPCWSTR lpWideCharStr, INT cchWideChar, LPCSTR lpDefaultChar, LPBOOL lpUsedDefaultChar)
{
	INT cchBuffer = WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, NULL, 0, lpDefaultChar, lpUsedDefaultChar);
	if (0 == cchBuffer) return NULL;
	
	LPSTR buffer = Plugin_MallocAnsiString(cchBuffer);
	if (NULL == buffer) return NULL; 
		
	if (0 == WideCharToMultiByte(codePage, dwFlags, lpWideCharStr, cchWideChar, buffer, cchBuffer, lpDefaultChar, lpUsedDefaultChar))
	{
		Plugin_FreeAnsiString(buffer);
		return NULL;
	}
	return buffer;
}

LPWSTR Plugin_MultiByteToWideChar(UINT codePage, DWORD dwFlags, LPCSTR lpMultiByteStr, INT cbMultiByte)
{
	if (NULL == lpMultiByteStr) return NULL;
	INT cchBuffer = MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, NULL, 0);
	if (NULL == cchBuffer) return NULL;
	
	if (cbMultiByte > 0) cchBuffer++;
	
	LPWSTR buffer = Plugin_MallocString(cchBuffer);
	if (NULL == buffer) return NULL;

	if (0 == MultiByteToWideChar(codePage, dwFlags, lpMultiByteStr, cbMultiByte, buffer, cchBuffer))
	{
		Plugin_FreeString(buffer);
		return NULL;
	}

	if (cbMultiByte > 0)
	{
		buffer[cchBuffer - 1] = L'\0';
	}
	return buffer;
}

LPWSTR Plugin_DuplicateResString(LPCWSTR pszResource)
{
	return (IS_INTRESOURCE(pszResource)) ? 
			(LPWSTR)pszResource : 
			Plugin_CopyString(pszResource);
}

void Plugin_FreeResString(LPWSTR pszResource)
{
	if (!IS_INTRESOURCE(pszResource))
		Plugin_FreeString(pszResource);
}

HRESULT Plugin_CopyResString(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszString)
{
	if (NULL == pszBuffer)
		return E_INVALIDARG;

	HRESULT hr = S_OK;

	if (NULL == pszString)
	{
		pszBuffer[0] = L'\0';
	}
	else if (IS_INTRESOURCE(pszString))
	{
		if (NULL == WASABI_API_LNG)
			hr = E_FAIL;
		else
			WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszString, pszBuffer, cchBufferMax);
	}
	else
	{
		hr = StringCchCopy(pszBuffer, cchBufferMax, pszString);
	}
	return hr;
}

void Plugin_SafeRelease(IUnknown *pUnk)
{
	if (NULL != pUnk)
		pUnk->Release();
}

HRESULT Plugin_MakeResourcePath(LPWSTR pszBuffer, INT cchBufferMax, LPCWSTR pszType, LPCWSTR pszName, UINT flags)
{
	HINSTANCE hInstance = WASABI_API_LNG_HINST;
	if (NULL == hInstance || NULL == FindResource(hInstance, pszName, pszType))
		hInstance = Plugin_GetInstance();

	if (NULL == OMUTILITY)
		return E_UNEXPECTED;

	return OMUTILITY->MakeResourcePath(pszBuffer, cchBufferMax, hInstance, pszType, pszName, flags);
}

HWND Plugin_GetDialogOwner(void)
{
	HWND hOwner= Plugin_GetLibrary();
	if (NULL == hOwner || FALSE == IsWindowVisible(hOwner) ||
		FALSE == IsWindowEnabled(hOwner))
	{
		hOwner = Plugin_GetWinamp();
		if (NULL != hOwner)
		{
			HWND hDlgParent = (HWND)SENDWAIPC(hOwner, IPC_GETDIALOGBOXPARENT, 0L);
			if (NULL != hDlgParent) 
				hOwner = hDlgParent;
		}
	}
	return hOwner;
}

HRESULT Plugin_AppendFileFilter(LPTSTR pszBuffer, size_t cchBufferMax, LPCTSTR pName, LPCTSTR pFilter, LPTSTR *ppBufferOut, size_t *pRemaining, BOOL bShowFilter)
{
	HRESULT hr;
	
	LPTSTR pCursor = pszBuffer;

	if (NULL != ppBufferOut)
		*ppBufferOut = pszBuffer;

	if (NULL != pRemaining)
		*pRemaining = cchBufferMax;

	if (NULL == pszBuffer || NULL == pName || NULL == pFilter)
		return E_INVALIDARG;

	pszBuffer[0] = TEXT('\0');

	hr = StringCchCopyEx(pCursor, cchBufferMax, pName, &pCursor, &cchBufferMax, 
			STRSAFE_IGNORE_NULLS | STRSAFE_NULL_ON_FAILURE);
	if (bShowFilter && SUCCEEDED(hr))
	{		
		LPTSTR p = pCursor;
		hr = StringCchPrintfEx(pCursor, cchBufferMax, &pCursor, &cchBufferMax, 
				STRSAFE_IGNORE_NULLS | STRSAFE_NULL_ON_FAILURE, TEXT(" (%s)"), pFilter);
		if (SUCCEEDED(hr) && p != pCursor)
			CharLowerBuff(p, (INT)(INT_PTR)(pCursor - p)); 
	}
	if (SUCCEEDED(hr))
	{
		pCursor++;
		cchBufferMax--;
		hr = StringCchCopyEx(pCursor, cchBufferMax, pFilter, &pCursor, &cchBufferMax, 
			STRSAFE_IGNORE_NULLS | STRSAFE_NULL_ON_FAILURE);
	}

	if (cchBufferMax < 1)
		hr = STRSAFE_E_INSUFFICIENT_BUFFER;
	
	pCursor++;
	cchBufferMax--;

	if (SUCCEEDED(hr))
	{
		pCursor[0] = TEXT('\0');
		if (NULL != ppBufferOut)
			*ppBufferOut = pCursor;
		if (NULL != pRemaining)
			*pRemaining = cchBufferMax;
	}
	else 
	{
		pszBuffer[0] = TEXT('\0');
		pszBuffer[1] = TEXT('\0');
	}

	return hr;
}

HRESULT Plugin_BuildActionUrl(LPWSTR *ppStringOut, LPCWSTR pszAction, UINT *pServiceUid, size_t cchServiceUid)
{
	if (NULL == ppStringOut) 
		return E_POINTER;
	
	*ppStringOut = NULL;

	if (NULL == pszAction || L'\0' == *pszAction || 
		NULL == pServiceUid || 0 == cchServiceUid)
	{
		return E_INVALIDARG;
	}
	
	const WCHAR szPrefix[] = L"http://services.winamp.com/svc/action?action=%s\0";
	const WCHAR szService[] = L"&svc_id=%u\0";
	const WCHAR szClient[] = L"&unique_id=%s\0";

	size_t cchBuffer = ARRAYSIZE(szPrefix) + ARRAYSIZE(szService) + ARRAYSIZE(szClient);
	cchBuffer += lstrlen(pszAction);
	cchBuffer += (cchServiceUid * 11);
	cchBuffer += 32; // unique id
	
	LPWSTR buffer = Plugin_MallocString(cchBuffer);
	if (NULL == buffer)
		return E_OUTOFMEMORY;

	HRESULT hr;
	LPWSTR cursor = buffer;
	size_t remaining = cchBuffer;


	hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, 
							szPrefix, pszAction);
	
	for (size_t i = 0; i < cchServiceUid && SUCCEEDED(hr); i++)
	{
		hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, 
							((0 == i) ? szService : L",%u"), pServiceUid[i]);
	}

	if (SUCCEEDED(hr))
	{
		WCHAR szTemp[128] = {0};
		hr = OMBROWSERMNGR->GetClientId(szTemp, ARRAYSIZE(szTemp));
		if (SUCCEEDED(hr))
		{
			hr = StringCchPrintfEx(cursor, remaining, &cursor, &remaining, STRSAFE_NULL_ON_FAILURE, 	szClient, szTemp);
		}
	}
	
	if (FAILED(hr))
	{
		Plugin_FreeString(buffer);
		hr = E_FAIL;
	}
	else
	{
		*ppStringOut = buffer;
	}
	return hr;
}

INT Plugin_ParseKeywords(LPCWSTR input, INT cchInput, WCHAR separator, BOOL eatSpace, KWPARSERPROC callback, ULONG_PTR user)
{
	if (NULL == input)
		return 0;

	if (cchInput < 0)
		cchInput = lstrlen(input);
	
	if (cchInput <= 0)
		return 0;

	LPCWSTR end  = (input + cchInput);

	if(eatSpace) 
		while(input < end && L' ' == *input) input++;

	if (L'\0' == *input)
		return 0;

	INT found = 0;
	
	for (;;)
	{
		LPCWSTR pBlock = input;
		while(input <= end && separator != *input) input++;
		LPCWSTR last = (input - 1);
		if (eatSpace)
			while(last >= pBlock && L' ' == *last) last--;
		
		if (last >= pBlock)
		{
			UINT code = callback(pBlock, (INT)(INT_PTR)(last - pBlock) + 1, user);
			if (KWPARSER_FOUND & code) found++;
			if (KWPARSER_ABORT == (0x01 & code)) 
				return found;
		}

		if (input >= end || L'\0' == *input) 
			return found;

		input++;
		if(eatSpace) 
			while(input < end && L' ' == *input) input++;
	}

	return found;
}

INT Plugin_MessageBox(LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
	HWND hHost, hLibrary = Plugin_GetLibrary();
	if (NULL != hLibrary && FALSE != IsWindowVisible(hLibrary))
	{
		hHost = (HWND)SENDMLIPC(hLibrary, ML_IPC_GETCURRENTVIEW, 0);
		if (NULL == hHost || FALSE == IsWindowVisible(hHost)) 
			hHost = hLibrary;
	}
	else
		hHost = Plugin_GetDialogOwner();

	if(IS_INTRESOURCE(lpText) && NULL != lpText)
	{
		WCHAR szText[2048] = {0};
		lpText = WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)lpText, szText, ARRAYSIZE(szText));
	}

	if(IS_INTRESOURCE(lpCaption) && NULL != lpCaption)
	{
		WCHAR szCaption[128] = {0};
		lpCaption = WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)lpCaption, szCaption, ARRAYSIZE(szCaption));
	}

	return MessageBox(hHost, lpText, lpCaption, uType);
}