#include <origin.h>
#include <GetNBox.h>
#include <xfutils.h>
//put additional include files here
#include <okocUtils.h>

/*------------------------------------------------------------------------------*
 * Modification Log:															*
 * Kenny 04/23/2009 CHECK_ERROR_SHOULD_NOT_ALWAYS_RETURN_FALSE_WHEN_CHECKING_STYLE*
 *------------------------------------------------------------------------------*/

#define STR_WIKI_TABLE_FILE		 	"wikiTable.txt"
#define STR_LINE_END				"\r\n"
#define STR_BOLD_FORMAT				"'''"
#define CHAR_WKS_CELL_LINE_END		'\n'

//put your own support static functions here
#define SET_VAL_IF_OVER_BOUND(var, minVal, maxVal, setVal)	if(var < minVal || var > maxVal) var = setVal;
#define SET_MIN_MAX(minVar, maxVar, varType) \
	if(maxVar < minVar)\
	{\
		varType tmpMin = minVar;\
		minVar = maxVar;\
		maxVar = tmpMin;\
	}

static int _wks_convert_to_wiki_table(
	const Worksheet& wks,
	string& strTable,
	const vector<string>& vsHeaders, 
	uint nFromCol = 0, 
	uint nFromRow = 0,
	int nToCol = -1,
	int nToRow = -1,
	const bool bFirstColAsBold = false,
	LPCSTR lpcszStyleName = "simple"
	)
{
	const int nMaxColIndex = wks.GetNumCols()-1;
	const int nMaxRowIndex = wks.GetNumRows()-1;
	if (nMaxColIndex < 0 || nMaxRowIndex < 0 || nFromCol > nMaxColIndex || nFromRow > nMaxRowIndex)
		return -1;
	SET_VAL_IF_OVER_BOUND(nToCol, 0, nMaxColIndex, nMaxColIndex)
	SET_VAL_IF_OVER_BOUND(nToRow, 0, nMaxRowIndex, nMaxRowIndex)
	SET_MIN_MAX(nFromCol, nToCol, uint)
	SET_MIN_MAX(nFromRow, nToRow, uint)

	// 1. Adding style: class = "classname"
	strTable = "{| class=\"" + (string)lpcszStyleName + "\"" + STR_LINE_END;

	// 2. Adding headers
	for (int ii = 0; ii < vsHeaders.GetSize(); ++ii)
		strTable += "!" + vsHeaders[ii] + STR_LINE_END;

	// 3. Adding items
	for (int nRow = nFromRow; nRow <= nToRow; ++nRow)
	{
		strTable += "|-" + STR_LINE_END;
		for (int nCol = nFromCol; nCol <= nToCol; ++nCol)
		{
			string strVal;
			BOOL bRet = wks.GetCell(nRow, nCol, strVal);
			
			int nEnd = strVal.Find(CHAR_WKS_CELL_LINE_END);
			while(nEnd >= 0 )
			{
				strVal.Delete(nEnd);
				nEnd = strVal.Find(CHAR_WKS_CELL_LINE_END);
			}
			
			if (bFirstColAsBold && nCol == nFromCol)
				strTable += "|" + STR_BOLD_FORMAT + strVal + STR_BOLD_FORMAT + STR_LINE_END;
			else
				strTable += "|" + strVal + STR_LINE_END;
		}
	}

	// 4. End
	strTable += "|}";
	
	return 0;
}

static void _copy_str_to_clipboard(LPCSTR lpcszMem)
{	
	string str(lpcszMem);	
	int nMemSize = str.GetLength() + 1;	
    HANDLE hData = GlobalAlloc(GHND, nMemSize);
    LPSTR lpData = (LPSTR)GlobalLock(hData);
    lstrcpyn(lpData, str, nMemSize);
    GlobalUnlock(hData);
    if(OpenClipboard(NULL))
    {
        EmptyClipboard();
        SetClipboardData(CF_TEXT,hData);
        CloseClipboard();
    }
	
}

typedef string (*PFN_GET_BUILTIN_LABEL_NAME)(int nType);
static PFN_GET_BUILTIN_LABEL_NAME _get_pfn_get_builtin_label_name()
{
	return (PFN_GET_BUILTIN_LABEL_NAME)Project.FindFunction("get_builtin_label_name", "OriginLab\\WksColLabels");
}

static bool _get_wks_label_name(const Worksheet& wks, vector<int>& vnLabelTypes, vector<string>& vsLabelNames)
{
	PFN_GET_BUILTIN_LABEL_NAME pfn_get_builtin_label_name = _get_pfn_get_builtin_label_name();
	Grid grid;   
	if( grid.Attach(wks) && pfn_get_builtin_label_name)
	{
		grid.GetShowLabels(vnLabelTypes);
		for (int ii = 0; ii < vnLabelTypes.GetSize(); ++ii)
		{
			const string strLabelName = pfn_get_builtin_label_name(vnLabelTypes[ii]);
			if (!strLabelName.IsEmpty())
				vsLabelNames.Add(strLabelName);
		}
		vector<string> vsUserDefLabels;
		grid.GetUserDefinedLabelNames(vsUserDefLabels);
		vsLabelNames.Append(vsUserDefLabels);
		return true;
	}
	return false;
}

enum { OUTPUT_TO_SCRIPT_WND, OUTPUT_TO_RESULT_LOG, OUTPUT_TO_FILE, OUTPUT_TO_CLIPBOARD };

enum { HEADER_FIRST_ROW, HEADER_COL_SHORT_NAME, HEADER_COL_LABEL };

#define STR_HEADER_FIRST_ROW		_L("First Row")
#define STR_HEADER_COL_SHORT_NAME	_L("Column Short Name")

#define STR_LABEL_TYPES_ATTRIB		"LabelTypes"

#define OBJECT_SEPARATOR			'|'

bool _get_wks_obj(TreeNode& trGetN, Worksheet* pwks = NULL)
{
	Worksheet wks;
	if( !okxf_resolve_string_get_origin_object(trGetN.wks.strVal, &wks) )
		return false;
	if(pwks)
		*pwks = wks;
	return true;
}

void _out_target_event(TreeNode& trGetN)
{
	const bool bOutputToFile = OUTPUT_TO_FILE == trGetN.output_target.nVal;
	trGetN.file_path.Show = bOutputToFile;
	trGetN.file_path.Use = bOutputToFile;
}

static bool _dlg_init(TreeNode& trGetN)
{
	Worksheet wks;
	if( !_get_wks_obj(trGetN, &wks) )
		return false;	
	
	const int nMaxCol = wks.GetNumCols();
	const int nMaxRow = wks.GetNumRows();
	if ( okutil_cvt_str_to_predefined_type(trGetN.col.strVal) == PDS_UNASSIGNED )
		trGetN.col.nVal = nMaxCol;
	if ( okutil_cvt_str_to_predefined_type(trGetN.row.strVal) == PDS_UNASSIGNED )
		trGetN.row.nVal = nMaxRow;

	// construct Header chioce according to active wks column labels
	//vector<string> vsHeaderCombo = {STR_HEADER_FIRST_ROW, STR_HEADER_COL_SHORT_NAME};
	vector<string> vsHeaderCombo;
	vsHeaderCombo.Add(STR_HEADER_FIRST_ROW);
	vsHeaderCombo.Add(STR_HEADER_COL_SHORT_NAME);

	vector<int> vnLabelTypes;
	vector<string> vsLabelNames;
	bool bRet = _get_wks_label_name(wks, vnLabelTypes, vsLabelNames);
	if(bRet)
	{
		vsHeaderCombo.Append(vsLabelNames);
	}
	
	string strHeadersCombo;
	strHeadersCombo.SetTokens(vsHeaderCombo, OBJECT_SEPARATOR);
	trGetN.headers.SetAttribute(STR_COMBO_ATTRIB, strHeadersCombo);
	
	string strLabelTypes;
	vector<string> vsLabelTypes;
	convert_int_vector_to_string_vector(vnLabelTypes, vsLabelTypes);
	strLabelTypes.SetTokens(vsLabelTypes, OBJECT_SEPARATOR);	
	trGetN.label_types.strVal = strLabelTypes;
	
	// init out file path
	if ( okutil_cvt_str_to_predefined_type(trGetN.file_path.strVal) == PDS_UNASSIGNED || trGetN.file_path.strVal.IsEmpty() )
		trGetN.file_path.strVal = GetAppPath(false) + STR_WIKI_TABLE_FILE;
	
	_out_target_event(trGetN);
	
	return true;
}

static bool _check_error(TreeNode& trGetN, int nEventID, bool& bOKEnable, LPCSTR lpcszNodeName, string& strErrMsg)
{
	const bool bCheckAll = GETNE_ON_INIT == nEventID || GETNE_ON_THEME == nEventID;
	if ( bCheckAll || 0 == lstrcmpi(lpcszNodeName, "wks") )
	{
		bool bRet = _get_wks_obj(trGetN);
		return bOKEnable = bRet;
	}
	
	if( bCheckAll || 0 == lstrcmpi(lpcszNodeName, "style") )
	{
		///Kenny 04/23/2009 CHECK_ERROR_SHOULD_NOT_ALWAYS_RETURN_FALSE_WHEN_CHECKING_STYLE
		if( trGetN.style.strVal.IsEmpty() )
			//strErrMsg = XFERR_INVALID_STYLE_NAME;
		//return bOKEnable = false;
		{
			strErrMsg = XFERR_INVALID_STYLE_NAME;
			return bOKEnable = false;
		}
		///End CHECK_ERROR_SHOULD_NOT_ALWAYS_RETURN_FALSE_WHEN_CHECKING_STYLE
	}
	
	if( bCheckAll || 0 == lstrcmpi(lpcszNodeName, "file_path") || 0 == lstrcmpi(lpcszNodeName, "output_target") )
	{
		if( OUTPUT_TO_FILE == trGetN.output_target.nVal )		
		{
			if( !is_str_valid_file_path(trGetN.file_path.strVal) )
			{
				strErrMsg = XFERR_INVALID_FILE_PATH;
				return bOKEnable = false;
			}
			if( trGetN.file_path.strVal.IsFile() )
			{
				//warning 
				strErrMsg = EXPWARN_FILE_EXIST;
				strErrMsg += ":" + trGetN.file_path.strVal;				
			}
			
			//warning if file path is not existed
			if( !GetFilePath(trGetN.file_path.strVal).IsPath() )
				strErrMsg = XFWARN_WILL_CREATE_NONEXISTING_PATH;
				
		}		
	}
	
	return true;
}
			
			

void wks_to_wiki_table(const Worksheet& wks, const int& output_target, const string& file_path, const int& headers, const string& style, const int& col, const int& row, const int& first_col_bold, const string& label_types)
{
	
	vector<string> vsHeaders;
	if (HEADER_FIRST_ROW == headers)
	{
		for (int ii = 0; ii < col; ++ii)
		{
			string strVal;
			BOOL bRet = wks.GetCell(0, ii, strVal);
			vsHeaders.Add(strVal);
		}
	}
	else if (HEADER_COL_SHORT_NAME == headers)
	{
		for (int ii = 0; ii < col; ++ii)
			vsHeaders.Add( wks.Columns(ii).GetName() );
	}
	else if (headers >= HEADER_COL_LABEL)
	{
		int nLabelIndex = headers-HEADER_COL_LABEL;
		
		vector<string> vsLabelTypes;
		label_types.GetTokens(vsLabelTypes, OBJECT_SEPARATOR);
		vector<int> vnLabelTypes;
		convert_string_vector_to_int_vector(vsLabelTypes, vnLabelTypes);
		
		int nLabelType = vnLabelTypes[nLabelIndex];
		
		for (int ii = 0; ii < col; ++ii)
		{
			string strLabelValue;
			wks.Columns(ii).GetExtendedLabel(strLabelValue, nLabelType);
			vsHeaders.Add( strLabelValue );
		}
	}
	
	string strTable;
	int nRet = _wks_convert_to_wiki_table(wks, strTable, vsHeaders, 0, 0, col-1, row-1, first_col_bold, style);
	if(0 == nRet)
	{
		switch(output_target)
		{
		case OUTPUT_TO_SCRIPT_WND:
			okoc_out_msg(strTable, 'I');
			break;
			
		case OUTPUT_TO_RESULT_LOG:
			Project.OutStringToResultsLog(strTable);
			break;
			
		case OUTPUT_TO_CLIPBOARD:
			_copy_str_to_clipboard(strTable);
			break;
		case OUTPUT_TO_FILE:
			{
				if( !GetFilePath(file_path).IsPath() )
					CreateDirectory(GetFilePath(file_path), NULL);
				
				FILE *stream;
			    // Open file in text mode:
			    stream = fopen( file_path, "w+" );
			    if(stream != NULL )
			    {
			        size_t numwritten = fwrite( strTable.GetBuffer(strTable.GetLength()), strTable.GetLength(), 1, stream );
			        fclose( stream );
			    }
			}
			break;
		}
	}
}

//nEventID < 0 on event, like GETNE_ON_INIT, GETNE_ON_OK etc, >0 on change from control
//return 0 to do nothing, return PEVENT_GETN_RET_TO_CLOSE to close dialog
//dwEnables = bOKEnable, bBTN1Enable, bBTN2Enable, bBTN3Enable, , can set to false to disable OK/Apply buttons
//lpcszNodeName = the trGetN TreeNode tag name that generates the event, or is  if GETNE_ON_OK etc
//strAux = optional string for XF Wizard Bar, like DLG_NEED_CHANGE_GRID etc, it will be invalid if from internal node value change event
//strErrMsg = allowing messages to be shown
int wks_to_wiki_table_event1(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	if( xf_is_extended_event(nEventID) )
		return wks_to_wiki_table_event1_ex(trGetN, nRow, nEventID, dwEnables, lpcszNodeName, DynaCntrlContainer, strAux, strErrMsg);
	DECLARE_BUTTON_ENABLES
	if( GETNE_ON_INIT == nEventID || GETNE_ON_THEME == nEventID || 0 == strcmp(lpcszNodeName, "output_target") )
	{
		_out_target_event(trGetN);
	}
	
	if( GETNE_ON_INIT == nEventID || GETNE_ON_THEME == nEventID || lpcszNodeName && *lpcszNodeName)
		return _check_error(trGetN, nEventID, bOKEnable, lpcszNodeName, strErrMsg);
	return true;

}

int wks_to_wiki_table_make_tree(TreeNode& tr, LPCSTR lpcszVarName)
{
	return true;
}
//nGetNDialog = 0,1,2, corresponding to the XF GetN dropdown, 0 = dialog is not used
//you can change nRet to be one of XFEVT_PROCEED (defualt), XFEVT_ABORT, XFEVT_PROCEED_NO_DLG or XFEVT_PROCEED_WITH_DLG(when nGetNDialog = 0)
//nGetNDialog = -1 when Ok on GetN dialog to allow handler to preocess <auto> etc variables
//dwCntrl is combination of bits
//LTXF_FROM_GUI_MENU, LTXF_FROM_GUI_PROMPT, LTXF_FROM_AUTO_UPDATE, LTXF_FROM_FUNCTION, LTXF_THEME_USED, LTXF_CHANGE_PARAM
void wks_to_wiki_table_before_execute(TreeNode& trGetN, int nGetNDialog, int& nRet, int dwCntrl)
{
	if (-1 != nGetNDialog)
	{
		bool bRet = _dlg_init(trGetN);
	}
}
//nEventID < 0 on event, like GETNE_ON_INIT, GETNE_ON_OK etc, >0 on change from control
//return 0 to do nothing, return PEVENT_GETN_RET_TO_CLOSE to close dialog
//dwEnables = bOKEnable, bBTN1Enable, bBTN2Enable, bBTN3Enable, , can set to false to disable OK/Apply buttons
//lpcszNodeName = the trGetN TreeNode tag name that generates the event, or is  if GETNE_ON_OK etc
//strAux = optional string for XF Wizard Bar, like DLG_NEED_CHANGE_GRID etc, it will be invalid if from internal node value change event
//strErrMsg = allowing messages to be shown
int wks_to_wiki_table_event1_ex(TreeNode& trGetN, int nRow, int nEventID, DWORD& dwEnables, LPCSTR lpcszNodeName, WndContainer& DynaCntrlContainer, string& strAux, string& strErrMsg)
{
	return false;
}
