/*
 * synergy -- mouse and keyboard sharing utility
 * Copyright (C) 2014 Synergy Si Ltd.
 * 
 * This package is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * found in the file LICENSE that should have accompanied this file.
 * 
 * This package is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "platform/MSWindowsDropTarget.h"

#include "base/Log.h"
#include "common/common.h"

#include <stdio.h>
#include <Shlobj.h>

void getDropData(IDataObject *pDataObject);

MSWindowsDropTarget* MSWindowsDropTarget::s_instance = NULL;

MSWindowsDropTarget::MSWindowsDropTarget() :
	m_refCount(1),
	m_allowDrop(false)
{
	s_instance = this;
}

MSWindowsDropTarget::~MSWindowsDropTarget()
{
}

MSWindowsDropTarget&
MSWindowsDropTarget::instance()
{
	assert(s_instance != NULL);
	return *s_instance;
}

HRESULT
MSWindowsDropTarget::DragEnter(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect)
{
	// check if data object contain drop
	m_allowDrop = queryDataObject(dataObject);
	if (m_allowDrop) {
		getDropData(dataObject);
	}
	
	*effect = DROPEFFECT_NONE;

	return S_OK;
}

HRESULT
MSWindowsDropTarget::DragOver(DWORD keyState, POINTL point, DWORD* effect)
{
	*effect = DROPEFFECT_NONE;

	return S_OK;
}

HRESULT
MSWindowsDropTarget::DragLeave(void)
{
	return S_OK;
}

HRESULT
MSWindowsDropTarget::Drop(IDataObject* dataObject, DWORD keyState, POINTL point, DWORD* effect)
{
	*effect = DROPEFFECT_NONE;

	return S_OK;
}

bool
MSWindowsDropTarget::queryDataObject(IDataObject* dataObject)
{
	// check if it supports CF_HDROP using a HGLOBAL
	FORMATETC fmtetc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };

	return dataObject->QueryGetData(&fmtetc) == S_OK ? true : false;
}

void
MSWindowsDropTarget::setDraggingFilename(char* const filename)
{
	m_dragFilename = filename;
}

std::string
MSWindowsDropTarget::getDraggingFilename()
{
	return m_dragFilename;
}

void
MSWindowsDropTarget::clearDraggingFilename()
{
	m_dragFilename.clear();
}

void
getDropData(IDataObject* dataObject)
{
	// construct a FORMATETC object
	FORMATETC fmtEtc = { CF_HDROP, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
	STGMEDIUM stgMed;

	// See if the dataobject contains any DROP stored as a HGLOBAL
	if (dataObject->QueryGetData(&fmtEtc) == S_OK) {
		if (dataObject->GetData(&fmtEtc, &stgMed) == S_OK) {
			// get data here
			PVOID data = GlobalLock(stgMed.hGlobal);

			// data object global handler contains:
			// DROPFILESfilename1 filename2 two spaces as the end
			// TODO: get multiple filenames
			wchar_t* wcData = (wchar_t*)((LPBYTE)data + sizeof(DROPFILES));

			// convert wchar to char
			char* filename = new char[wcslen(wcData) + 1];
			filename[wcslen(wcData)] = '\0';
			wcstombs(filename, wcData, wcslen(wcData));

			MSWindowsDropTarget::instance().setDraggingFilename(filename);
			
			GlobalUnlock(stgMed.hGlobal);

			// release the data using the COM API
			ReleaseStgMedium(&stgMed);

			delete[] filename;
		}
	}
}

HRESULT __stdcall
MSWindowsDropTarget::QueryInterface (REFIID iid, void ** object)
{
	if (iid == IID_IDropTarget || iid == IID_IUnknown) {
		AddRef();
		*object = this;
		return S_OK;
	}
	else {
		*object = 0;
		return E_NOINTERFACE;
	}
}

ULONG __stdcall
MSWindowsDropTarget::AddRef(void)
{
	return InterlockedIncrement(&m_refCount);
}

ULONG __stdcall
MSWindowsDropTarget::Release(void)
{
	LONG count = InterlockedDecrement(&m_refCount);
		
	if (count == 0) {
		delete this;
		return 0;
	}
	else {
		return count;
	}
}
