#include "CtrlCore.h"
#include <plugin/bmp/bmp.h>

NAMESPACE_UPP

#ifdef PLATFORM_WIN32

#define LLOG(x)  // LOG(x)

VectorMap<int, ClipData>& sClipMap()
{
	static VectorMap<int, ClipData> x;
	return x;
}

extern HWND utilityHWND;


int  GetClipboardFormatCode(const char *format_id)
{
	int x = (int)(intptr_t)format_id;
	if(x >= 0 && x < 65535)
		return x;
	String fmt = format_id;
	if(fmt == "text")
		return CF_TEXT;
	if(fmt == "wtext")
		return CF_UNICODETEXT;
	if(fmt == "dib")
		return CF_DIB;
	if(fmt == "files")
		return CF_HDROP;
	static VectorMap<String, int> format_map;
	int f = format_map.Find(format_id);
	if(f < 0) {
		f = format_map.GetCount();
		format_map.Add(format_id,
#ifdef PLATFORM_WINCE
			::RegisterClipboardFormat(ToSystemCharset(format_id))
#else
			::RegisterClipboardFormat(format_id)
#endif
		);
	}
	return format_map[f];
}

void ClearClipboard()
{
	sClipMap().Clear();
	if(OpenClipboard(utilityHWND)) {
		EmptyClipboard();
		CloseClipboard();
	}
}

void SetClipboardRaw(int format, const byte *data, int length)
{
	HANDLE handle = NULL;
	if(data) {
		handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, length + 2);
		byte *ptr;
		if(!handle)
			return;
		if(!(ptr = (byte *)GlobalLock(handle))) {
			GlobalFree(handle);
			return;
		}
		memcpy(ptr, data, length);
		ptr[length] = 0;
		ptr[length + 1] = 0;
		GlobalUnlock(handle);
	}
	if(!SetClipboardData(format, handle)) {
		LLOG("SetClipboardData error: " << GetLastErrorMessage());
		GlobalFree(handle);
	}
}

void AppendClipboard(int format, const byte *data, int length)
{
	if(OpenClipboard(utilityHWND)) {
		SetClipboardRaw(format, data, length);
		CloseClipboard();
	}
}

void AppendClipboard(const char *format, const byte *data, int length)
{
	Vector<String> f = Split(format, ';');
	for(int i = 0; i < f.GetCount(); i++)
		AppendClipboard(GetClipboardFormatCode(f[i]), data, length);
}

void AppendClipboard(const char *format, const String& data)
{
	AppendClipboard(format, data, data.GetLength());
}

void AppendClipboard(const char *format, const Value& data, String (*render)(const Value&))
{
	Vector<String> f = Split(format, ';');
	for(int i = 0; i < f.GetCount(); i++) {
		int c = GetClipboardFormatCode(f[i]);
		sClipMap().GetAdd(c) = ClipData(data, render);
		AppendClipboard(c, NULL, 0);
	}
}

void Ctrl::RenderFormat(int format)
{
	int q = sClipMap().Find(format);
	if(q >= 0) {
		String s = sClipMap()[q].Render();
		SetClipboardRaw(format, s, s.GetLength());
	}
}

void Ctrl::RenderAllFormats()
{
	if(sClipMap().GetCount() && OpenClipboard(utilityHWND)) {
		for(int i = 0; i < sClipMap().GetCount(); i++)
			RenderFormat(sClipMap().GetKey(i));
		CloseClipboard();
	}
}

void Ctrl::DestroyClipboard()
{
	sClipMap().Clear();
}

String ReadClipboard(const char *format)
{
	if(!OpenClipboard(NULL))
		return Null;
	HGLOBAL hmem = GetClipboardData(GetClipboardFormatCode(format));
	if(hmem == 0) {
		CloseClipboard();
		return Null;
	}
	const byte *src = (const byte *)GlobalLock(hmem);
	ASSERT(src);
	int length = (int)GlobalSize(hmem);
	if(length < 0) {
		CloseClipboard();
		return Null;
	}
	String out(src, length);
	GlobalUnlock(hmem);
	CloseClipboard();
	return out;
}

void AppendClipboardText(const String& s)
{
#ifdef PLATFORM_WINCE
	AppendClipboardUnicodeText(s.ToWString());
#else
	AppendClipboard("text", ToSystemCharset(s));
#endif
}

void AppendClipboardUnicodeText(const WString& s)
{
#ifndef PLATFORM_WINCE
	AppendClipboardText(s.ToString());
#endif
	AppendClipboard("wtext", (byte *)~s, 2 * s.GetLength());
}

const char *ClipFmtsText()
{
	return "wtext;text";
}

String GetString(PasteClip& clip)
{
	if(clip.Accept("wtext")) {
		String s = ~clip;
		return WString((const wchar *)~s, wstrlen((const wchar *)~s)).ToString();
	}
	if(clip.IsAvailable("text"))
		return ~clip;
	return Null;
}

WString GetWString(PasteClip& clip)
{
	if(clip.Accept("wtext")) {
		String s = ~clip;
		return WString((const wchar *)~s, wstrlen((const wchar *)~s));
	}
	if(clip.IsAvailable("text"))
		return (~clip).ToWString();
	return Null;
}


bool AcceptText(PasteClip& clip)
{
	return clip.Accept(ClipFmtsText());
}

static String sText(const Value& data)
{
	return data;
}

static String sWText(const Value& data)
{
	return Unicode__(WString(data));
}

void Append(VectorMap<String, ClipData>& data, const String& text)
{
	data.GetAdd("text", ClipData(text, sText));
	data.GetAdd("wtext", ClipData(text, sWText));
}

void Append(VectorMap<String, ClipData>& data, const WString& text)
{
	data.GetAdd("text", ClipData(text, sText));
	data.GetAdd("wtext", ClipData(text, sWText));
}

String GetTextClip(const WString& text, const String& fmt)
{
	if(fmt == "text")
		return text.ToString();
	if(fmt == "wtext")
		return Unicode__(text);
	return Null;
}

String GetTextClip(const String& text, const String& fmt)
{
	if(fmt == "text")
		return text;
	if(fmt == "wtext")
		return Unicode__(text.ToWString());
	return Null;
}

String ReadClipboardText()
{
#ifdef PLATFORM_WINCE
	return ReadClipboardUnicodeText().ToString();
#else
	String s = ReadClipboard((const char *)CF_TEXT);
	return String(s, (int)strlen(~s));
#endif
}

WString ReadClipboardUnicodeText()
{
	String s = ReadClipboard((const char *)CF_UNICODETEXT);
	return WString((const wchar *)~s, wstrlen((const wchar *)~s));
}

bool IsClipboardAvailable(const char *id)
{
	return ::IsClipboardFormatAvailable(GetClipboardFormatCode(id));
}

bool IsClipboardAvailableText()
{
	return IsClipboardAvailable((const char *)CF_TEXT);
}

const char *ClipFmtsImage()
{
	static const char *q;
	ONCELOCK {
		static String s = "dib;" + ClipFmt<Image>();
		q = s;
	}
	return q;
}

bool AcceptImage(PasteClip& clip)
{
	return clip.Accept(ClipFmtsImage());
}

Image GetImage(PasteClip& clip)
{
	Image m;
	if(Accept<Image>(clip)) {
		LoadFromString(m, ~clip);
		if(!m.IsEmpty())
			return m;
	}
	if(clip.Accept("dib")) {
		String data = ~clip;
		if(data.GetCount() < sizeof(BITMAPINFO)) return Null;
		BITMAPINFO *lpBI = 	(BITMAPINFO *)~data;
		BITMAPINFOHEADER& hdr = lpBI->bmiHeader;
		byte *bits = (byte *)lpBI + hdr.biSize;
		if(hdr.biBitCount <= 8)
			bits += (hdr.biClrUsed ? hdr.biClrUsed : 1 << hdr.biBitCount) * sizeof(RGBQUAD);
		if(hdr.biBitCount >= 16 || hdr.biBitCount == 32) {
			if(hdr.biCompression == 3)
				bits += 12;
			if(hdr.biClrUsed != 0)
				bits += hdr.biClrUsed * sizeof(RGBQUAD);
		}
		int h = abs((int)hdr.biHeight);
		ImageDraw   iw(hdr.biWidth, h);
		::StretchDIBits(iw.GetHandle(),
			0, 0, hdr.biWidth, h,
			0, 0, hdr.biWidth, h,
			bits, lpBI, DIB_RGB_COLORS, SRCCOPY);
		return iw;
	}
	return Null;
}

Image ReadClipboardImage()
{
	PasteClip d = Ctrl::Clipboard();
	return GetImage(d);
}

String sDib(const Value& image)
{
	Image img = image;
	BITMAPINFOHEADER header;
	Zero(header);
	header.biSize = sizeof(header);
	header.biWidth = img.GetWidth();
	header.biHeight = -img.GetHeight();
	header.biBitCount = 32;
	header.biPlanes = 1;
	header.biCompression = BI_RGB;
	StringBuffer b(sizeof(header) + 4 * img.GetLength());
	byte *p = (byte *)~b;
	memcpy(p, &header, sizeof(header));
	memcpy(p + sizeof(header), ~img, 4 * img.GetLength());
	return b;
}

String sImage(const Value& image)
{
	Image img = image;
	return StoreAsString(const_cast<Image&>(img));
}

String GetImageClip(const Image& img, const String& fmt)
{
	if(img.IsEmpty()) return Null;
	if(fmt == "dib")
		return sDib(img);
	if(fmt == ClipFmt<Image>())
		return sImage(img);
	return Null;
}

void AppendClipboardImage(const Image& img)
{
	if(img.IsEmpty()) return;
	AppendClipboard(ClipFmt<Image>(), img, sImage);
	AppendClipboard("dib", img, sDib);
}

bool AcceptFiles(PasteClip& clip)
{
	if(clip.Accept("files")) {
		clip.SetAction(DND_COPY);
		return true;
	}
	return false;
}


struct sDROPFILES {
    DWORD offset;
    POINT dummy;
    BOOL  dummy2;
    BOOL  unicode;
};

Vector<String> GetFiles(PasteClip& clip)
{
	Vector<String> f;
	String data = clip;
	if(data.GetCount() < sizeof(sDROPFILES) + 2)
		return f;
	const sDROPFILES *df = (const sDROPFILES *)~data;
	const char *s = ((const char *)df + df->offset);
	if(df->unicode) {
		const wchar *ws = (wchar *)s;
		while(*ws) {
			WString fn;
			while(*ws)
				fn.Cat(*ws++);
			f.Add(fn.ToString());
			ws++;
		}
	}
	else
		while(*s) {
			String fn;
			while(*s)
				fn.Cat(*s++);
			f.Add(fn.ToString());
			s++;
		}
	return f;
}

#endif

END_UPP_NAMESPACE
