// CopyThread.cpp : implementation file
//

#include "stdafx.h"
#include "cp_main.h"
#include "CopyThread.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CCopyThread

IMPLEMENT_DYNCREATE(CCopyThread, CWinThread)

CCopyThread::CCopyThread():
	m_bQuit(false),
	m_bConfigChanged(false),
	m_pClipboardViewer(NULL),
	m_connectOnStartup(true)
{
	m_bAutoDelete = false;
}

CCopyThread::~CCopyThread()
{
	m_LocalConfig.DeleteTypes();
	m_SharedConfig.DeleteTypes();
	delete m_pClipboardViewer;
}

BOOL CCopyThread::InitInstance()
{
	m_pClipboardViewer = new CClipboardViewer(this);
	m_pClipboardViewer->m_connectOnStartup = m_connectOnStartup;

	// the window is created within this thread and therefore uses its message queue
	m_pClipboardViewer->Create();

	return TRUE;
}

int CCopyThread::ExitInstance()
{
	m_pClipboardViewer->Disconnect(false);

	return CWinThread::ExitInstance();
}

// Called within Copy Thread:
void CCopyThread::OnClipboardChange(CString activeWindow, CString activeWindowTitle)
{
	Log(_T("OnClipboardChange - Start"));

	SyncConfig(); // synchronize with the main thread's copy configuration
	
	// if we are told not to copy on change, then we have nothing to do.
	if(!m_LocalConfig.m_bCopyOnChange)
		return;
	
	int groupId = theApp.GetActiveGroupId();
	if(groupId > -1)
	{
		Log(StrF(_T("LoadFromClipboard - loading clips into groupId: %d"), groupId));
	}
	
	CClip* pClip = new CClip;
	pClip->m_copyReason = theApp.GetCopyReason();

	CClipTypes* pSupportedTypes = m_LocalConfig.m_pSupportedTypes;
	bool bDeleteMemory = false;

	//If we are copying from a Ditto Buffer then save all to the database, so when we paste this it will paste 
	//just like you were using Ctrl-V
	if(theApp.m_CopyBuffer.Active())
	{
		Log(_T("LoadFromClipboard - Copy buffer Active Start"));

		pSupportedTypes = new CClipTypes;
		if(pSupportedTypes)
		{
			bDeleteMemory = true;
			COleDataObject oleData;

			if(oleData.AttachClipboard())
			{
				oleData.BeginEnumFormats();

				FORMATETC format;
				while(oleData.GetNextFormat(&format))
				{
					pSupportedTypes->Add(format.cfFormat);
				}

				oleData.Release();
			}
		}
		else
		{
			pSupportedTypes = m_LocalConfig.m_pSupportedTypes;
		}

		Log(_T("LoadFromClipboard - Copy buffer Active End"));
	}

	Log(_T("LoadFromClipboard - Before"));
	int bResult = pClip->LoadFromClipboard(pSupportedTypes, true, activeWindow, activeWindowTitle);
	Log(_T("LoadFromClipboard - After"));

	if(bResult == FALSE)
	{
		DWORD delay = CGetSetOptions::GetNoFormatsRetryDelay();
		if(delay > 0)
		{
			Log(StrF(_T("LoadFromClipboard didn't find any clips to save, sleeping %dms, then trying again"), delay));
			Sleep(delay);

			Log(_T("LoadFromClipboard #2 - Before"));
			bResult = pClip->LoadFromClipboard(pSupportedTypes, activeWindow);
			Log(_T("LoadFromClipboard #2 - After"));
		}
		else
		{
			Log(_T("LoadFromClipboard didn't find any clips to save, retry setting is not set, not retrying"));
		}
	}

	if(bDeleteMemory)
	{
		delete pSupportedTypes;
		pSupportedTypes = NULL;
	}
	
	if(bResult != TRUE)
	{
		delete pClip;
		return; // error
	}

	if(pClip != NULL &&
		groupId > -1)
	{
		pClip->m_parentId = groupId;
	}
	
	if(m_LocalConfig.m_bAsyncCopy)
		::PostMessage(m_LocalConfig.m_hClipHandler, WM_CLIPBOARD_COPIED, (WPARAM)pClip, 0);
	else
		::SendMessage(m_LocalConfig.m_hClipHandler, WM_CLIPBOARD_COPIED, (WPARAM)pClip, 0);

	Log(_T("OnClipboardChange - End"));
}

void CCopyThread::SyncConfig()
{
	// atomic read
	if(m_bConfigChanged)
	{
		CClipTypes* pTypes = NULL;
		
		ATL::CCritSecLock csLock(m_cs.m_sect);
		
		pTypes = m_LocalConfig.m_pSupportedTypes;
		
		m_LocalConfig = m_SharedConfig;
		
		// NULL means that it shouldn't have been sync'ed
		if( m_SharedConfig.m_pSupportedTypes == NULL )
		{	// let m_LocalConfig keep its types
			m_LocalConfig.m_pSupportedTypes = pTypes; // undo sync
			pTypes = NULL; // nothing to delete
		}
		else
			m_SharedConfig.m_pSupportedTypes = NULL; // now owned by LocalConfig
		
		// delete old types
		if( pTypes )
		{
			delete pTypes;
		}
	}
}

bool CCopyThread::IsClipboardViewerConnected()
{
	return m_pClipboardViewer->m_bIsConnected;
}

bool CCopyThread::GetConnectCV()
{
	return m_pClipboardViewer->GetConnect();
}

void CCopyThread::SetConnectCV(bool bConnect)
{
	if(m_pClipboardViewer != NULL && m_pClipboardViewer->m_hWnd != NULL)
	{
		::SendMessage( m_pClipboardViewer->m_hWnd, WM_SETCONNECT, bConnect, 0 );
	}
}

void CCopyThread::SetSupportedTypes( CClipTypes* pTypes )
{
	ATL::CCritSecLock csLock(m_cs.m_sect);

	if(m_SharedConfig.m_pSupportedTypes)
	{
		delete m_SharedConfig.m_pSupportedTypes;
	}

	m_SharedConfig.m_pSupportedTypes = pTypes;
	m_bConfigChanged = true;
}

HWND CCopyThread::SetClipHandler(HWND hWnd)
{
	ATL::CCritSecLock csLock(m_cs.m_sect);

	HWND hRet = m_SharedConfig.m_hClipHandler;
	m_SharedConfig.m_hClipHandler = hWnd;
	m_bConfigChanged = (hRet != hWnd);

	return hRet;
}
HWND CCopyThread::GetClipHandler()
{
	ATL::CCritSecLock csLock(m_cs.m_sect);

	HWND hRet = m_SharedConfig.m_hClipHandler;

	return hRet;
}
bool CCopyThread::SetCopyOnChange(bool bVal)
{
	ATL::CCritSecLock csLock(m_cs.m_sect);

	bool bRet = m_SharedConfig.m_bCopyOnChange;
	m_SharedConfig.m_bCopyOnChange = bVal;
	m_bConfigChanged = (bRet != bVal);

	return bRet;
}
bool CCopyThread::GetCopyOnChange()
{
	ATL::CCritSecLock csLock(m_cs.m_sect);

	bool bRet = m_SharedConfig.m_bCopyOnChange;

	return bRet;
}
bool CCopyThread::SetAsyncCopy(bool bVal)
{
	ATL::CCritSecLock csLock(m_cs.m_sect);

	bool bRet = m_SharedConfig.m_bAsyncCopy;
	m_SharedConfig.m_bAsyncCopy = bVal;
	m_bConfigChanged = (bRet != bVal);

	return bRet;
}
bool CCopyThread::GetAsyncCopy()
{
	ATL::CCritSecLock csLock(m_cs.m_sect);

	bool bRet = m_SharedConfig.m_bAsyncCopy;

	return bRet;
}

void CCopyThread::Init(CCopyConfig cfg)
{
	ASSERT(m_LocalConfig.m_pSupportedTypes == NULL);
	m_LocalConfig = m_SharedConfig = cfg;
	// let m_LocalConfig own the m_pSupportedTypes
	m_SharedConfig.m_pSupportedTypes = NULL;
}

bool CCopyThread::Quit()
{
	m_bQuit = true;
	m_pClipboardViewer->PostMessage( WM_QUIT );
	return CWinThread::PostThreadMessage( WM_QUIT, NULL, NULL ) != FALSE;
}