/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 1, or (at your option)
** any later version.

** This program 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, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "stdafx.h"

#include "BrowseViewColumn.h"
#include "CvsEntries.h"

#if TIME_WITH_SYS_TIME
#	include <sys/time.h>
#	include <time.h>
#elif HAVE_SYS_TIME_H
#	include <sys/time.h>
#else
#	include <time.h>
#endif

#if qMacPP
#	include "MacMisc.h"
#endif

#ifndef _countof
#	define _countof(array) (sizeof(array)/sizeof(array[0]))
#endif

extern "C" time_t get_date(const char *date, struct timeb *now); 
	// Jonathan M. Gilligan <jonathan.gilligan@vanderbilt.edu> 

static void SafeCopy(char* buffer, const char *value, int bufferSize)
{
	if (value && *value)
	{
		strncpy(buffer, value, bufferSize);
	}
	else
	{
		*buffer = 0;
	}
}

#ifdef WIN32
static bool IsLargeFonts()
{
	//JK - large fonts support for Windows
	static bool bLargeFonts = false;
	static bool defined = false;
	if (!defined)
	{
		defined = true;
		HWND hDesktop = ::GetDesktopWindow();
		HDC hDesktopDC = ::GetDC(hDesktop);
		short nLogPixels = ::GetDeviceCaps(hDesktopDC, LOGPIXELSX);
		::ReleaseDC(hDesktop, hDesktopDC);
		bLargeFonts = (nLogPixels > 96);
	}
	return bLargeFonts;
}
#endif

const class KoNameColumn : public KiColumn 
{
// Interfaces
public:
#ifdef WIN32
	// retrieve column label 
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
		lvc->pszText = _T("Name");
		lvc->cx = 150;
		lvc->fmt = LVCFMT_LEFT;
	}
#else
	// retrieve column label 
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->name = "Name";
		lvc->width = 210;
	}
#endif

	// retrieve formatted text as pertained to a node
	virtual void GetText(KoColumnContext* /*context*/, EntnodeData* data, char* buffer, int bufferSize) const
	{
		SafeCopy(buffer, data->GetName(), bufferSize);
	}

	// compare two nodes (-1,0,+1)
	virtual int Compare(KoColumnContext* /*context*/, EntnodeData* d1, EntnodeData* d2) const
	{
		int res = stricmp(d1->GetName(), d2->GetName());
		return res;
	}

	// tells if the column shall be sorted ascending initially
	virtual bool IsDefaultAscending() const
	{
		return false;
	}

} nameColumn;

const class KoPathColumn : public KiColumn 
{
// Interfaces
public:
	// retrieve column label 
#ifdef WIN32
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
		lvc->pszText = _T("Path");
		lvc->cx = 150;
		lvc->fmt = LVCFMT_LEFT;
	}
#else
	// retrieve column label 
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->name = "Path";
		lvc->width = 300;
	}
#endif

	// retrieve formatted text as pertained to a node
	virtual void GetText(KoColumnContext* context, EntnodeData* data, char* buffer, int bufferSize) const
	{				
		UStr buf;
		const char *fullpath = data->GetFullPathName(buf);
		int len = context->m_path.length();
		fullpath += len;
		if (*fullpath == '\\')
		{
			fullpath++;
		}
		SafeCopy(buffer, fullpath, bufferSize);
		const char *name = data->GetName();
		if (name)
		{
			int lenName = strlen(name) + 1;
			int lenPath = strlen(buffer);
			if (lenPath <= lenName)
			{
				strncpy(buffer, ".", bufferSize);
			}
			else
			{
				buffer[lenPath - lenName] = 0;
			}
		}
	}

	// compare two nodes (-1,0,+1)
	virtual int Compare(KoColumnContext* context, EntnodeData* d1, EntnodeData* d2) const
	{
		char text1[256];
		GetText(context, d1, text1, 256);
		char text2[256];
		GetText(context, d2, text2, 256);
		int res = stricmp(text1, text2);
		return res;
	}

	// tells if the column shall be sorted ascending initially
	virtual bool IsDefaultAscending() const
	{
		return false;
	}

} pathColumn;

	
const class KoRevisionColumn : public KiColumn 
{
// Interfaces
public:
	// retrieve column label 
#ifdef WIN32
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
		lvc->pszText = _T("Rev.");
		lvc->cx = 50;
		lvc->fmt = LVCFMT_LEFT;
	}
#else
	// retrieve column label 
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->name = "Rev.";
		lvc->width = 70;
	}
#endif

	// retrieve formatted text as pertained to a node
	virtual void GetText(KoColumnContext* /*context*/, EntnodeData* data, char* buffer, int bufferSize) const
	{
		SafeCopy(buffer, (*data)[EntnodeFile::kVN], bufferSize);
	}

	// compare two nodes (-1,0,+1)
	virtual int Compare(KoColumnContext* /*context*/, EntnodeData* d1, EntnodeData* d2) const
	{
		const char *s1 = (*d1)[EntnodeFile::kVN];
		const char *s2 = (*d2)[EntnodeFile::kVN];
		int res;
		if(s1 != 0L && s2 != 0L)
			res = revcmp(s1, s2);
		else
			res = (long)s1 < (long)s2 ? -1 : ((long)s1 > (long)s2 ? 1 : 0);
		return res;
	}


	static int revcmp(const char *rev1, const char *rev2)
	{
		if(rev1 == 0L && rev2 == 0L)
			return 0;
		else if(rev1 == 0L || rev2 == 0L)
			return rev1 == 0L ? -1 : 1;

		CStr r1(rev1), r2(rev2);
		CStr q1, q2;
		char *tmp;
		int v1, v2;

		if((tmp = strchr(r1, '.')) != 0L)
		{
			tmp[0] = '\0';
			q1 = tmp + 1;
		}

		v1 = atoi(r1);

		if((tmp = strchr(r2, '.')) != 0L)
		{
			tmp[0] = '\0';
			q2 = tmp + 1;
		}
		v2 = atoi(r2);

		if(v1 == v2)
			return revcmp(q1.empty() ? (char *)0L : q1, q2.empty() ? (char *)0L : q2);

		return v1 < v2 ? -1 : 1;
	}

	// tells if the column shall be sorted ascending initially
	virtual bool IsDefaultAscending() const
	{
		return true;
	}

} revisionColumn;

const class KoOptionColumn : public KiColumn 
{
// Interfaces
public:
	// retrieve column label 
#ifdef WIN32
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
		lvc->pszText = _T("Option");
		lvc->cx = 50;
		lvc->fmt = LVCFMT_LEFT;
	}
#else
	// retrieve column label 
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->name = "Option";
		lvc->width = 55;
	}
#endif

	// retrieve formatted text as pertained to a node
	virtual void GetText(KoColumnContext* /*context*/, EntnodeData* data, char* buffer, int bufferSize) const
	{
		SafeCopy(buffer, (*data)[EntnodeFile::kOption], bufferSize);
	}

	// compare two nodes (-1,0,+1)
	virtual int Compare(KoColumnContext* /*context*/, EntnodeData* d1, EntnodeData* d2) const 
	{
		const char *s1 = (*d1)[EntnodeFile::kOption];
		const char *s2 = (*d2)[EntnodeFile::kOption];
		int res;
		if(s1 != 0L && s2 != 0L)
			res = strcmp(s1, s2);
		else
			res = (long)s1 < (long)s2 ? -1 : ((long)s1 > (long)s2 ? 1 : 0);
		return res;
	}

	// tells if the column shall be sorted ascending initially
	virtual bool IsDefaultAscending() const
	{
		return true;
	}

} optionColumn;

const class KoStatusColumn : public KiColumn 
{
// Interfaces
public:
	// retrieve column label 
#ifdef WIN32
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
		lvc->pszText = _T("Status");
		lvc->cx = 100;
		lvc->fmt = LVCFMT_LEFT;
	}
#else
	// retrieve column label 
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->name = "Status";
		lvc->width = 70;
	}
#endif

	// retrieve formatted text as pertained to a node
	virtual void GetText(KoColumnContext* /*context*/, EntnodeData* data, char* buffer, int bufferSize) const
	{
		SafeCopy(buffer, (*data)[EntnodeData::kStatus], bufferSize);
	}

	// compare two nodes (-1,0,+1)
	virtual int Compare(KoColumnContext* /*context*/, EntnodeData* d1, EntnodeData* d2) const
	{
		int res = stricmp((*d1)[EntnodeData::kStatus], (*d2)[EntnodeData::kStatus]);
		return res;
	}

	// tells if the column shall be sorted ascending initially
	virtual bool IsDefaultAscending() const
	{
		return true;
	}

} statusColumn;

const class KoTagColumn : public KiColumn 
{
// Interfaces
public:
	// retrieve column label 
#ifdef WIN32
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
		lvc->pszText = _T("Tag");
		lvc->cx = 150;
		lvc->fmt = LVCFMT_LEFT;
	}
#else
	// retrieve column label 
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->name = "Tag";
		lvc->width = 90;
	}
#endif

	// retrieve formatted text as pertained to a node
	virtual void GetText(KoColumnContext* /*context*/, EntnodeData* data, char* buffer, int bufferSize) const
	{
		SafeCopy(buffer, (*data)[EntnodeFile::kTag], bufferSize);
	}

	// compare two nodes (-1,0,+1)
	virtual int Compare(KoColumnContext* /*context*/, EntnodeData* d1, EntnodeData* d2) const
	{
		const char *s1 = (*d1)[EntnodeFile::kTag];
		const char *s2 = (*d2)[EntnodeFile::kTag];
		int res;
		if(s1 != 0L && s2 != 0L)
			res = strcmp(s1, s2);
		else
			res = (long)s1 < (long)s2 ? -1 : ((long)s1 > (long)s2 ? 1 : 0);
		return res;

	}

	// tells if the column shall be sorted ascending initially
	virtual bool IsDefaultAscending() const
	{
		return true;
	}

} tagColumn;


const class KoDateColumn : public KiColumn 
{
// Interfaces
public:
	// retrieve column label 
#ifdef WIN32
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
		lvc->pszText = _T("Date");
		lvc->cx = (IsLargeFonts()) ? 170 : 150;
		lvc->fmt = LVCFMT_LEFT;
	}
#else
	// retrieve column label 
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->name = "Date";
		lvc->width = 135;
	}
#endif

	// retrieve formatted text as pertained to a node
	virtual void GetText(KoColumnContext* /*context*/, EntnodeData* data, char* buffer, int bufferSize) const
	{
		SafeCopy(buffer, (*data)[EntnodeFile::kTS], bufferSize);
	}

	// compare two nodes (-1,0,+1)
	virtual int Compare(KoColumnContext* /*context*/, EntnodeData* d1, EntnodeData* d2) const
	{
		const char *s1 = (*d1)[EntnodeFile::kTS];
		const char *s2 = (*d2)[EntnodeFile::kTS];
		int res;
 
		if(s1 != 0L && s2 != 0L) { 
			time_t t1 = get_date(s1,(struct timeb *)NULL); 
			time_t t2 = get_date(s2,(struct timeb *)NULL); 

			res = t1 < t2 ? -1 : (t1 > t2 ? 1 : 0);
		} 
		else
			res = (long)s1 < (long)s2 ? -1 : ((long)s1 > (long)s2 ? 1 : 0);
		return res;
	}

	// tells if the column shall be sorted ascending initially
	virtual bool IsDefaultAscending() const
	{
		return true;
	}


} dateColumn;

const class KoConflictColumn : public KiColumn 
{
// Interfaces
public:
	// retrieve column label 
#ifdef WIN32
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT;
		lvc->pszText = _T("Conflict");
		lvc->cx = 150;
		lvc->fmt = LVCFMT_LEFT;
	}
#else
	// retrieve column label 
	virtual void GetSetupData(LV_COLUMN* lvc) const
	{
		lvc->name = "Conflict";
		lvc->width = 100;
	}
#endif

	// retrieve formatted text as pertained to a node
	virtual void GetText(KoColumnContext* /*context*/, EntnodeData* data, char* buffer, int bufferSize) const
	{
		SafeCopy(buffer, data->GetConflict(), bufferSize);
	}

	// compare two nodes (-1,0,+1)
	virtual int Compare(KoColumnContext* /*context*/, EntnodeData* d1, EntnodeData* d2) const
	{
		const char *s1 = d1->GetConflict();
		const char *s2 = d2->GetConflict();
		int res;
		if(s1 != 0L && s2 != 0L)
			res = strcmp(s1, s2);
		else
			res = (long)s1 < (long)s2 ? -1 : ((long)s1 > (long)s2 ? 1 : 0);
		return res;
	}

	// tells if the column shall be sorted ascending initially
	virtual bool IsDefaultAscending() const
	{
		return true;
	}


} conflictColumn;

// all known columns for view's perusal
static const KiColumn* columnsRegular[] = {
	&nameColumn, &revisionColumn, &optionColumn, &statusColumn, &tagColumn, &dateColumn, &conflictColumn
};

static const int typesRegular[] = {
	EntnodeData::kName, EntnodeFile::kVN, EntnodeFile::kOption, EntnodeData::kStatus,
	EntnodeFile::kTag, EntnodeFile::kTS, EntnodeFile::kConflict
};

static const KiColumn* columnsRecursive[] = {
	&nameColumn, &pathColumn, &revisionColumn, &optionColumn, &statusColumn, &tagColumn, &dateColumn, &conflictColumn
};

static const int typesRecursive[] = {
	EntnodeData::kName, EntnodeFile::kPath, EntnodeFile::kVN, EntnodeFile::kOption, EntnodeData::kStatus,
	EntnodeFile::kTag, EntnodeFile::kTS, EntnodeFile::kConflict
};

// regular columnar model
class KoRegularModel : public KiColumnModel
{
	

// Interfaces
public:
	virtual const KiColumn* GetAt(int pos) const
	{
		return columnsRegular[pos];
	}
	

	virtual int GetType(int pos) const
	{
		return typesRegular[pos];
	}
	

	virtual int GetCount() const
	{
		return _countof(columnsRegular);
	}
	
} modelRegular;

// recursive columnar model
class KoRecursiveModel : public KiColumnModel
{	
// Interfaces
public:
	virtual const KiColumn* GetAt(int pos) const
	{
		return columnsRecursive[pos];
	}
	

	virtual int GetType(int pos) const
	{
		return typesRecursive[pos];
	}
	

	virtual int GetCount() const
	{
		return _countof(columnsRecursive);
	}
	
} modelRecursive;

////////////////////////////////////////////////////////////////////////////////////////////////////////////
// instantiation
KiColumnModel* KiColumnModel::GetRecursiveModel()
{
	return &modelRecursive;
}
	
KiColumnModel* KiColumnModel::GetRegularModel()
{
	return &modelRegular;
}

int KiColumnModel::GetColumn(int type) const
{
	int numColumns = GetCount();
	for(int i = 0; i < numColumns; i++)
	{
		if(type == GetType(i))
			return i;
	}
	return 0;
}

#ifdef WIN32
int CALLBACK _Compare(LPARAM data1, LPARAM data2, LPARAM data)
#else
int _Compare(void *data1, void *data2, void *data)
#endif
{
	int res = 0;
	bool fOneDirOnly = false;

	EntnodeData *d1 = (EntnodeData *)data1;
	EntnodeData *d2 = (EntnodeData *)data2;
	
	const CSortParam *pSortParam = (CSortParam*)data;

	//	Group sub dirs ?
	if (pSortParam->m_fGroupSubDir)
	{
		int d1IsDir = d1->GetType()==ENT_SUBDIR;
		int d2IsDir = d2->GetType()==ENT_SUBDIR;

		if( (d1IsDir + d2IsDir) == 1)
		{
			//	One (and only one of them) is a directory
			fOneDirOnly = true;
			res = d1IsDir ? -1 : 1;
		}
	}
	
	if (!res)
	{
		res = pSortParam->m_column->Compare(pSortParam->m_context, d1, d2);
	}

	if (res==0 && pSortParam->m_column != pSortParam->m_columnAlt)
		res = pSortParam->m_columnAlt->Compare(pSortParam->m_context, d1, d2);
	else if (!fOneDirOnly)
		res = pSortParam->m_fAccendant ? -res : res;
	return res;
}
