#include "stdafx.h"
#include "emule.h"
#include "MetaDataDlg.h"
#include "OtherFunctions.h"
#include "Ini2.h"

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


//////////////////////////////////////////////////////////////////////////////
// LCX_COLUMN_INIT

typedef struct
{
	int				iColID;
	LPCTSTR			pszHeading;
	UINT			uFormat;
	int				iWidth;
	int				iOrder;
	LPCTSTR			pszSample;
} LCX_COLUMN_INIT;


//////////////////////////////////////////////////////////////////////////////
// COLUMN_INIT -- List View Columns

enum EMetaDataCols
{
	META_DATA_COL_NAME = 0,
	META_DATA_COL_TYPE,
	META_DATA_COL_VALUE
};

static LCX_COLUMN_INIT _aColumns[] =
{
	{ META_DATA_COL_NAME,		_T("Name"),			LVCFMT_LEFT,	-1, 0, _T("Temporary file MMMMM") },
	{ META_DATA_COL_TYPE,		_T("Type"),			LVCFMT_LEFT,	-1, 1, _T("Integer") },
	{ META_DATA_COL_VALUE,		_T("Value"),		LVCFMT_LEFT,	-1, 2, _T("long long long long long long long long file name.avi") }
};

#define	PREF_INI_SECTION	_T("MetaDataDlg")
#define	PREF_INI_COLWIDTH	_T("Col%uWidth")

// CMetaDataDlg dialog

IMPLEMENT_DYNAMIC(CMetaDataDlg, CResizablePage)

BEGIN_MESSAGE_MAP(CMetaDataDlg, CResizablePage)
	ON_NOTIFY(LVN_KEYDOWN, IDC_TAGS, OnLvnKeydownTags)
	ON_COMMAND(MP_COPYSELECTED, OnCopyTags)
	ON_COMMAND(MP_SELECTALL, OnSelectAllTags)
	ON_WM_CONTEXTMENU()
	ON_WM_DESTROY()
END_MESSAGE_MAP()

CMetaDataDlg::CMetaDataDlg()
	: CResizablePage(CMetaDataDlg::IDD, 0)
{
	m_file = NULL;
	m_strCaption = GetResString(IDS_META_DATA);
	m_psp.pszTitle = m_strCaption;
	m_psp.dwFlags |= PSP_USETITLE;
	m_pMenuTags = NULL;
}

CMetaDataDlg::~CMetaDataDlg()
{
	delete m_pMenuTags;
}

void CMetaDataDlg::DoDataExchange(CDataExchange* pDX)
{
	CResizablePage::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_TAGS, m_tags);
}

BOOL CMetaDataDlg::OnInitDialog()
{
	CResizablePage::OnInitDialog();
	InitWindowStyles(this);

	AddAnchor(IDC_TAGS, TOP_LEFT, BOTTOM_RIGHT);
	AddAnchor(IDC_TOTAL_TAGS, BOTTOM_LEFT, BOTTOM_RIGHT);

	m_tags.SetExtendedStyle(LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);

	CString strIniFile;
	strIniFile.Format(_T("%spreferences.ini"), theApp.glob_prefs->GetConfigDir());
	CIni ini(strIniFile, PREF_INI_SECTION);

	m_tags.InsertColumn(0, _T("Dummy"), LVCFMT_LEFT, 0);
	for (int iCol = 0; iCol < ARRSIZE(_aColumns); iCol++)
	{
		int iColWidth;
		if (_aColumns[iCol].iWidth >= 0) {
			iColWidth = _aColumns[iCol].iWidth;
		}
		else
		{
			CString strColKey;
			strColKey.Format(PREF_INI_COLWIDTH, iCol);
			iColWidth = ini.GetInt(strColKey, -1);
			if (iColWidth == -1)
			{
			    // Get the 'Optimal Column Width'
			    if (_aColumns[iCol].pszSample){
				    int iWidthSample = m_tags.GetStringWidth(_aColumns[iCol].pszSample);
				    int iWidthHeader = m_tags.GetStringWidth(_aColumns[iCol].pszHeading);
				    iWidthHeader += 30; // if using the COMCTL 6.0 header bitmaps (up/down arrows), we need more space
    
				    iColWidth = 6 + __max(iWidthSample, iWidthHeader) + 6;	// left+right margin
				    if (_aColumns[iCol].uFormat & LVCFMT_RIGHT) // right-justified text(!)
					    iColWidth += 4;
			    }
			    else
				    iColWidth = 0;
		    }
		}

		LVCOLUMN lvc;
		lvc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT | LVCF_SUBITEM;
		lvc.pszText = const_cast<LPTSTR>(_aColumns[iCol].pszHeading);
		lvc.cx = iColWidth;
		lvc.fmt = _aColumns[iCol].uFormat;
		lvc.iSubItem = _aColumns[iCol].iColID;
		m_tags.InsertColumn(_aColumns[iCol].iColID + 1/*skip dummy column*/, &lvc);
	}
	m_tags.DeleteColumn(0);

	InitTags();

	m_pMenuTags = new CMenu();
	if (m_pMenuTags->CreatePopupMenu()){
		m_pMenuTags->AppendMenu(MF_ENABLED | MF_STRING, MP_COPYSELECTED, GetResString(IDS_COPY));
		m_pMenuTags->AppendMenu(MF_SEPARATOR);
		m_pMenuTags->AppendMenu(MF_ENABLED | MF_STRING, MP_SELECTALL, GetResString(IDS_SELECTALL));
	}

	return TRUE;  // return TRUE unless you set the focus to a control
				  // EXCEPTION: OCX Property Pages should return FALSE
}

CString GetName(const CTag* pTag)
{
	CString strName;
	if (pTag->tag.specialtag)
	{
		static const struct {
			UINT uID;
			LPCTSTR pszName;
		} _aTagNames[] =
		{
			{ FT_FILENAME,			_T("Name") },
			{ FT_FILESIZE,			_T("Size") },
			{ FT_FILETYPE,			_T("Type") },
			{ FT_FILEFORMAT,		_T("Format") },
			{ 0x05,					_T("Collection") },
			{ 0x06,					_T("Part path") },
			{ 0x07,					_T("Part hash") },
			{ FT_TRANSFERED,		_T("Transfered") },
			{ FT_GAPSTART,			_T("Gap start") },
			{ FT_GAPEND,			_T("Gap end") },
			{ 0x0B,					_T("Description") },
			{ 0x0C,					_T("Ping") },
			{ 0x0D,					_T("Fail") },
			{ 0x0E,					_T("Preference") },
			{ 0x0F,					_T("Port") },
			{ 0x10,					_T("IP") },
			{ 0x11,					_T("Version") },
			{ FT_PARTFILENAME,		_T("Temporary file") },
			{ 0x13,					_T("Priority") },
			{ FT_STATUS,			_T("Status") },
			{ FT_SOURCES,			_T("Availability") },
			{ 0x16,					_T("QTime") },
			{ 0x17,					_T("Parts") }
		};
		for (int i = 0; i < ARRSIZE(_aTagNames); i++){
			if (pTag->tag.specialtag == _aTagNames[i].uID){
				strName = _aTagNames[i].pszName;
				break;
			}
		}
		if (strName.IsEmpty())
			strName.Format(_T("Tag 0x%02X"), pTag->tag.specialtag);
	}
	else
		strName = pTag->tag.tagname;
	return strName;
}

CString GetValue(const CTag* pTag)
{
	CString strValue;
	if (pTag->tag.type == 2)
		strValue = pTag->tag.stringvalue;
	else if (pTag->tag.type == 3)
		strValue = GetFormatedUInt(pTag->tag.intvalue);
	else if (pTag->tag.type == 4)
		strValue.Format(_T("%f"), pTag->tag.floatvalue);
	else
		strValue.Format(_T("<Unknown value of type 0x%02X>"), pTag->tag.type);
	return strValue;
}

CString GetType(const CTag* pTag)
{
	CString strValue;
	if (pTag->tag.type == 2)
		strValue = _T("String");
	else if (pTag->tag.type == 3)
		strValue = _T("Integer");
	else if (pTag->tag.type == 4)
		strValue = _T("Float");
	else
		strValue.Format(_T("<Unknown type 0x%02X>"), pTag->tag.type);
	return strValue;
}

void CMetaDataDlg::InitTags()
{
	if (m_file == NULL) {
		ASSERT(0);
		return;
	}

	CWaitCursor curWait;
	const CArray<CTag*,CTag*>& m_aTags = m_file->GetTags();
	m_tags.DeleteAllItems();
	m_tags.SetRedraw(FALSE);
	int iTags = m_aTags.GetCount();
	for (int i = 0; i < iTags; i++)
	{
		const CTag* pTag = m_aTags.GetAt(i);
		CString strBuff;
		LVITEM lvi;
		lvi.mask = LVIF_TEXT;
		lvi.iItem = INT_MAX;
		lvi.iSubItem = META_DATA_COL_NAME;
		strBuff = GetName(pTag);
		lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strBuff);
		int iItem = m_tags.InsertItem(&lvi);
		if (iItem >= 0)
		{
			lvi.mask = LVIF_TEXT;
			lvi.iItem = iItem;

			strBuff = GetType(pTag);
			lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strBuff);
			lvi.iSubItem = META_DATA_COL_TYPE;
			m_tags.SetItem(&lvi);

			strBuff = GetValue(pTag);
			lvi.pszText = const_cast<LPTSTR>((LPCTSTR)strBuff);
			lvi.iSubItem = META_DATA_COL_VALUE;
			m_tags.SetItem(&lvi);
		}
	}
	CString strTmp;
	strTmp.Format(_T("Total tags: %u"), m_tags.GetItemCount());
	SetDlgItemText(IDC_TOTAL_TAGS, strTmp);
	m_tags.SetRedraw();
}

void CMetaDataDlg::OnCopyTags()
{
	CWaitCursor curWait;
	int iSelected = 0;
	CString strData;
	POSITION pos = m_tags.GetFirstSelectedItemPosition();
	while (pos)
	{
		int iItem = m_tags.GetNextSelectedItem(pos);
		CString strValue = m_tags.GetItemText(iItem, META_DATA_COL_VALUE);

		if (!strValue.IsEmpty())
		{
			if (!strData.IsEmpty())
				strData += _T("\r\n");
			strData += strValue;
			iSelected++;
		}
	}

	if (!strData.IsEmpty()){
		if (iSelected > 1)
			strData += _T("\r\n");
		theApp.CopyTextToClipboard(strData);
	}
}

void CMetaDataDlg::OnSelectAllTags()
{
	m_tags.SetItemState(-1, LVIS_SELECTED, LVIS_SELECTED);
}

void CMetaDataDlg::OnLvnKeydownTags(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLVKEYDOWN pLVKeyDow = reinterpret_cast<LPNMLVKEYDOWN>(pNMHDR);

	if (pLVKeyDow->wVKey == 'C' && (GetKeyState(VK_CONTROL) & 0x8000))
		OnCopyTags();
	else if (pLVKeyDow->wVKey == 'A' && (GetKeyState(VK_CONTROL) & 0x8000))
		OnSelectAllTags();
	*pResult = 0;
}

void CMetaDataDlg::OnContextMenu(CWnd * /*pWnd*/, CPoint point)
{
	if (m_pMenuTags == NULL){
		Default();
		return;
	}

	if (m_pMenuTags != NULL){
		if (point.x == -1 && point.y == -1){
			int iIdxItem = m_tags.GetNextItem(-1, LVNI_SELECTED | LVNI_FOCUSED);
			if (iIdxItem != -1){
				RECT rc;
				if (m_tags.GetItemRect(iIdxItem, &rc, LVIR_BOUNDS)){
					point.x = rc.left + m_tags.GetColumnWidth(0) / 2;
					point.y = rc.top + (rc.bottom - rc.top) / 2;
					ClientToScreen(&point);
				}
			}
			else{
				point.x = 16;
				point.y = 32;
				ClientToScreen(&point);
			}
		}

		m_pMenuTags->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, point.x, point.y, this);
	}
}

void CMetaDataDlg::OnDestroy()
{
	CString strIniFile;
	strIniFile.Format(_T("%spreferences.ini"), theApp.glob_prefs->GetConfigDir());
	CIni ini(strIniFile, PREF_INI_SECTION);
	for (int iCol = 0; iCol < ARRSIZE(_aColumns); iCol++)
	{
		CString strColKey;
		strColKey.Format(PREF_INI_COLWIDTH, iCol);
		ini.WriteInt(strColKey, _aColumns[iCol].iWidth = m_tags.GetColumnWidth(iCol));
	}

	CResizablePage::OnDestroy();
}
