/*------------------------------------------------------------------------------*
 * File Name: ColumnFormulaEx.h													*
 * Creation: ML 5/9/2003														*
 * Purpose: Baseclass for new column formula operations							*
 * Copyright (c) OriginLab Corp.2003											*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	CPY 8/2/04 REDO_COL_FORMULAR_EX_BASED_ON_COMMON_OPERATION_WKS_BASE			*
 *------------------------------------------------------------------------------*/

//#include <sys_utils.h>
#include <analysis_utils.h>


#define CFE_PLOT_OPTIONS		"No plot|Same layer|New graph"
enum {CFE_PLOT_OPTION_NO_PLOT, CFE_PLOT_OPTION_SAME_LAYER, CFE_PLOT_OPTION_NEW_GRAPH};


enum {
	CFEDATAOUTPUT_INSERT_NEW_Y = 0,
	CFEDATAOUTPUT_APPEND_NEW_XY,
	CFEDATAOUTPUT_CREATE_NEW_WKS,
};

#define		DEFAULT_RESULT_NAME				"Result"
#define		DEFAULT_RESULT_COLUMN_NAME		"Result"
#define		DEFAULT_RESULT_COLUMN_X_NAME	"ResultX"
#define		RESULT_COLUMN_WIDTH				14
 
//////////////////////////////////////////////////////////////////////////////////
// Class ColumnFormulaEx
//						- base class for all analyses which use one column as
//							output
//						- the input is one formula in the form
//							col(out) = func(col(in)<, col(in2)>, param1, param2...),
//							where in (in2, etc.) are the optional input columns,
//							whereas out is the (only) output column. The rest of
//							the arguments are parameters.
//						- the output column is created by this operation and
//							inserted to the right of the rightmost input column.
//////////////////////////////////////////////////////////////////////////////////
class	ColumnFormulaEx : public	WksReportOperation
{
protected:
	virtual uint ConstructGUIOptions(TreeNode& trGUI, int nOption) { return 0;}
	//virtual
	BOOL Construct(TreeNode& trOperation, int nOption = 0)
	{
		if( WksReportOperation::Construct(trOperation, nOption) )
		{
			///Iris 01/20/05 CONSTRUCT_GUI_CLEANUP
			//TreeNode trGUI = ConstructGUI();
			TreeNode	trGUI = ConstructAddGUI(trOperation);
			///end CONSTRUCT_GUI_CLEANUP
			
			tree_construct_add_input_branch(trGUI);

			uint nReportOptions = ConstructGUIOptions(trGUI, nOption);
			
			///Iris 4/01/05 FIT_REGRESSION_PLOTS
			/////Iris 01/19/05 ADD_GRAPHNUM_OPTION_TO_CONSTRUCT_REPORT
			////ConstructAddReportCommon(trGUI, REPORT_GRAPHS, nReportOptions);
			//ConstructAddReportCommon(trOperation, 0, nReportOptions, true, 1);
			ConstructAddReportCommon(trOperation, 0, nReportOptions, true);
			///---
			///end FIT_REGRESSION_PLOTS

			ConstructAddCommonBottom(trGUI);
			
			trOperation.AddNode(trGUI.Clone());

			trGUI.Output.InsertColsDataSheet.nVal = 1;
			trGUI.Output.CreateReportSheet.nVal = 0;				

			// prepare the basic tree nodes here
			TreeNode trOut = trOperation.Calculation; // Calculation node should have already been prepared in base class
			
						
			ConstructAddGraph(trOperation);//CPY 5/8/04
			
			//trOperation.AddNode(trGUI.Clone());	///Iris 01/20/05 CONSTRUCT_GUI_CLEANUP 		
			return TRUE;
		}

		return FALSE;
	}
	
	///Iris 4/06/05 INIT_GRAPH_NUMBER
	//virtual
	int ConstructGraphNumber()
	{
		return 1;
	}
	/////////////////////////////////////////////////////////////////////
	//virtual
	DWORD	GetDataRules(const TreeNode& trOp)
	{
		return DRR_GET_MISSING | DRR_GET_DEPENDENT | DRR_NO_FACTORS;
	}
	virtual bool DoFormula(Column& colOutput, TreeNode& trOp)
	{
		return false;
	}
	/////////////////////////////////////////////////////////////////////
	//virtual
	BOOL	Execute(int nExeMode = OEXEM_ON_CREATE, DWORD dwExecCntrl)	//CPY 2/24/08 SPFW_NEED_BETTER_OP_EXEC_CNTRL, added dwExecCntrl
	{
		//_DBMGS("Inside ColumnFormulaEx::Execute()")
		
		_BK
		
		Tree		tr;
		GetTree(tr);		
				
		Column		colOutput;
		if(!PrepareOutput(colOutput, tr))
			return false;

				
		BOOL		bOK = DoFormula(colOutput, tr);
		if (bOK)
		{
			//DoGraphs(tr);
			
			SetTree(tr);	// must remember the changes
		}
		//else
		//	DestroyOutput();
		
		return bOK;
	}
	
	//virtual	
	void	InitFromSelection(TreeNode &trOperation)
	{
		InitFromSelectionXY(trOperation);
	}

	/*	
	BOOL	UpdateGraph(TreeNode &tr)
	{
		Column			colOut;
		if ( GetOutput(colOut) < 0 || !colOut )
			return FALSE;
		
		Worksheet		wks;
		colOut.GetParent(wks);
		if (!wks)
			return FALSE;
		
		Curve			cuvResult;
		int				nDataOutput = GetDataOutputFromTree(tr);
		
		if (CFEDATAOUTPUT_INSERT_NEW_Y == nDataOutput)
		{
			int				nXIndex = -1, nYIndex = colOut.GetIndex();
			
			Column			colXInput;
			if (0 <= GetInput(colXInput, 1) && colXInput)	// get the second column (X) of input
			{
				nXIndex = colXInput.GetIndex();
			}
			
			if (0 <= nXIndex)
			{
				if ( !cuvResult.Attach(wks, nXIndex, nYIndex) )
					return FALSE;
			}
			else
			{
				if ( !cuvResult.Attach(wks, nYIndex) )
					return FALSE;
			}
		}
		else
		{
			Worksheet	wksOut;
			if (!GetOutputWks(wksOut))
			{
				ASSERT(FALSE);
				return FALSE;
			}
			
			int				nXIndex = -1, nYIndex = -1;
			
			Column			col, colX;
			if (GetOutput(col, 0) && col)
				nYIndex = col.GetIndex();
			else
			{
				ASSERT(FALSE);
				return FALSE;
			}
			
			if (GetOutput(colX, 1) && colX)	// get the second column (X) of output
			{
				nXIndex = colX.GetIndex();
			}
			
			if (0 <= nXIndex)
			{
				if ( !cuvResult.Attach(wksOut, nXIndex, nYIndex) )
					return FALSE;
			}
			else
			{
				if ( !cuvResult.Attach(wksOut, nYIndex) )
					return FALSE;
			}
			
		}
		
		curvebase		&cuvInput = GetCurveInput();

		bool bRescale = false;
		if( tr.Temp.Plot.Rescale )
			bRescale = (bool) tr.Temp.Plot.Rescale.nVal;

		// graph
		GraphPage		pg;
		// Is it already remembered in the aux. array?
		if (GetAuxObject(OPAUXOBJ_GRAPHPAGE, 0, pg))
		{
			// Just set it inside the tree (considered as input)
			// since the user may have renamed it.
			tr.Input.Page.strVal = pg.GetName();
			
			// Just check and update the result curve inside the graph.
			return curve_update_in_page(cuvResult, tr.GUI.CurveColor.nVal, pg, bRescale);
		}
		else
		{
			// Apparently first time, so set it in aux. array:
			ResetAuxArray(OPAUXOBJ_GRAPHPAGE);	// in case there are others
			string			strGrNameInTree;
			if (tr.Input.Page)
				strGrNameInTree = tr.Input.Page.strVal;
			if (!strGrNameInTree.IsEmpty())
			{
				GraphPage		pgFromTree(strGrNameInTree);
				if (pgFromTree)
				{
					int		nAux = AddAuxObject(OPAUXOBJ_GRAPHPAGE, pgFromTree);
					ASSERT(0 == nAux);
				}
				else
				{
					; // it does not exist, we can't do anything then
				}
			}
		}
		
		
		curve_update(cuvResult, cuvInput, tr.Input, tr.GUI.CurveColor.nVal, bRescale);

		return TRUE;
	}

	string CreateGraph(TreeNode& tr)
	{
		// Empty the graph page aux. array:
		ResetAuxArray(OPAUXOBJ_GRAPHPAGE);
		
		Column colOut;
		if( !GetOutput(colOut) || !colOut )
			return "";

		Worksheet wks;
		colOut.GetParent(wks);
		if( !wks )
			return "";

		int nXIndex = -1, nYIndex = colOut.GetIndex();

		Curve crvResult;
		Column colXInput;
		Column	colXOutput;
		
		if( !GetOutput(colXOutput, 1) || !colXOutput )
		{
			if( 0 <= GetInput(colXInput, 1) && colXInput ) // Get the second column (X) of input
				nXIndex = colXInput.GetIndex();
		}
		else
		{
			nXIndex = colXOutput.GetIndex();
		}

		if( 0 <= nXIndex )
		{
			if( !crvResult.Attach(wks, nXIndex, nYIndex) )
				return "";
		}
		else
		{
			if( !crvResult.Attach(wks, nYIndex) )
				return "";
		}

		curvebase& crvInput = GetCurveInput();
		
		string strTemplate, strPageName, strPageLabel;
		int nPlotType;

		if( tr.Temp.Plot.Template )
			strTemplate = tr.Temp.Plot.Template.strVal;
		else
			strTemplate = "Origin.otp";

		if( tr.Temp.Plot.PageName )
			strPageName = tr.Temp.Plot.BasePageName.strVal;
		else
			strPageName = "Graph";

		if( tr.Temp.Plot.PageLabelPrefix )
			strPageLabel = tr.Temp.Plot.PageLabelPrefix.strVal + " " + crvInput.GetName();
		else
			strPageLabel = "";

		if( tr.Temp.Plot.Type )
			nPlotType = tr.Temp.Plot.Type.nVal;
		else
			nPlotType = IDM_PLOT_LINE;

		colOut.SetLabel(strPageLabel);

		GraphPage gp;

		gp.Create(strTemplate, CREATE_HIDDEN);
		if( gp )
		{
			gp.Rename(strPageName);
			gp.Label = strPageLabel;
			if( gp )
			{
				GraphLayer gl;
				gl = gp.Layers();
				if( gl )
				{
					int nPlot = gl.AddPlot(crvResult, nPlotType);
					DataPlot dp = gl.DataPlots(nPlot);
					if( dp.IsValid() )
						dp.SetColor(tr.GUI.CurveColor.nVal);
					gl.Rescale();
					gp.SetShow();
					LabTalk.Page.title = 3; // GJLFix #4469 Need ability to show page label in OC
					
					// Remember it in the aux. array
					int		nAux = AddAuxObject(OPAUXOBJ_GRAPHPAGE, gp);
					ASSERT(0 == nAux);
					
					return gp.GetName();
				}
			}
		}
		return "";
	}
	
	BOOL	DestroyOutput()
	{
		Column			colOut;
		if ( 0 <= GetOutput(colOut) && colOut )
		{
			Worksheet		wks;
			colOut.GetParent(wks);
			if (!wks)
				return FALSE;
			
			// First remove it from operation:
			RemoveOutput(colOut);
			
			int				nColIndex = colOut.GetIndex();
			if (0 <= nColIndex)
			{
				return wks.DeleteCol(nColIndex);
			}
		}
		
		return FALSE;
	}
	
	
	
	int		GetDataOutputFromTree(TreeNode& tr)
	{
		int			nDataOutput = CFEDATAOUTPUT_INSERT_NEW_Y;
		if (tr.GUI.DataOutput)
			nDataOutput = tr.GUI.DataOutput.nVal;
		
		return nDataOutput;
	}
	
	
	//bool	PrepareOutput(Column &colOut, TreeNode& tr, LPCSTR lpcszDefaultName = NULL)
	//bool	PrepareOutput(Column &colOut, Column &colOutX, TreeNode& tr, LPCSTR lpcszDefaultName = NULL)
	bool	PrepareOutput(Column &colOut, Column &colOutX, TreeNode& tr)
	{
		int			nDataOutput = GetDataOutputFromTree(tr);
		
		int			nDataOutputOld = GetCurrentDataOutput();
		// if the same, already ready:
		if (nDataOutput != nDataOutputOld)
		{
			Worksheet		wksOut;
			if (CFEDATAOUTPUT_CREATE_NEW_WKS == nDataOutputOld)
			{
				GetOutputWks(wksOut);	// so that we can destroy it since
										// we don't need the additional worksheet any more
			}
			
			DestroyAllOutputObjects();
			
			if (wksOut)
			{
				wksOut.Destroy();
			}
		}
		
		//string		strDefaultColName = lpcszDefaultName;
		//if(strDefaultColName.IsEmpty())
		//{
		//	if(tr.Output.Name.IsValid())
		//		strDefaultColName = tr.Output.Name.strVal;
		//	if(strDefaultColName.IsEmpty())
		//		strDefaultColName = DEFAULT_RESULT_COLUMN_NAME;
		//}
		string			strDefaultColName = GetResultColumnDefName();
		
		string		strName;
		if (CFEDATAOUTPUT_INSERT_NEW_Y == nDataOutput || CFEDATAOUTPUT_APPEND_NEW_XY == nDataOutput)
		{
			// Just get the first Y column of input for now:
			Column			ccY;
			if ( GetInput(ccY) < 0 || !ccY )	// assume the first column input to be Y
			{
				ASSERT(FALSE);
				return false;
			}
			
			int				nColIndex = ccY.GetIndex();
			if (nColIndex < 0)
			{
				ASSERT(FALSE);
				return FALSE;
			}
			
			
			Worksheet		wks;
			ccY.GetParent(wks);
			if (!wks)
				return FALSE;
			
			
			int				nLocationToInsert = -1;	// -1 to append
			if (CFEDATAOUTPUT_INSERT_NEW_Y == nDataOutput)
			{
				int			nOffsetToInsert = 1;	// needs to be changed after #4627 is done to this:
				// int			nOffsetToInsert = wks.GetLastDependentColumnOffset(COLDEPOFFSET_MODIFIER, TRUE) + 1; 
				
				nLocationToInsert = nColIndex + nOffsetToInsert;
			}
			
			if (!GetOutput(colOut) || !colOut)
			{
				if ( !wks.InsertCol(nLocationToInsert, strDefaultColName, strName) )
					return FALSE;
				
				if (nLocationToInsert < 0)
					nLocationToInsert = wks.Columns.Count() - 1;
				
				colOut = wks.Columns(nLocationToInsert);
				
				if (!colOut)
					return FALSE;
				
				colOut.SetWidth(RESULT_COLUMN_WIDTH);
			
				SetOutput(colOut);
			}

			
			if (CFEDATAOUTPUT_APPEND_NEW_XY == nDataOutput)
			{
				if (!GetOutput(colOutX, 1) || !colOutX)
				{
					string		strResultXColumnName = GetResultColumnXDefName();
					
					if ( !wks.InsertCol(nLocationToInsert, strResultXColumnName, strName) )
						return FALSE;
					
					colOutX = wks.Columns(nLocationToInsert);
					if (!colOutX)
						return FALSE;
					
					colOutX.SetType(OKDATAOBJ_DESIGNATION_X);
					colOutX.SetWidth(RESULT_COLUMN_WIDTH);
					SetOutput(colOutX, 1);
				}
			}
		}
		else
		{
			ASSERT(CFEDATAOUTPUT_CREATE_NEW_WKS == nDataOutput);
			
			if (!GetOutput(colOut) || !colOut || !GetOutput(colOutX, 1) || !colOutX)
			{
				Worksheet		wksOut;
				
				wksOut.Create(NULL, CREATE_HIDDEN);
				if (wksOut)
				{
					string		strNewWksName = GetNewWksDefName();
					wksOut.GetPage().Rename(strNewWksName);
					
					while(wksOut.DeleteCol(0))	 // Remove all columns in worksheet
						;
					
					int			nCol = wksOut.AddCol(strDefaultColName);
					ASSERT(0 == nCol);
					colOut = wksOut.Columns(nCol);
					if (!colOut)
						return FALSE;
					
					SetOutput(colOut);
					colOut.SetWidth(RESULT_COLUMN_WIDTH);
					
					string		strResultXColumnName = GetResultColumnXDefName();
					BOOL		bOK = wksOut.InsertCol(0, strResultXColumnName, strName);
					ASSERT(bOK);
					colOutX = wksOut.Columns(0);
					if (!colOutX)
						return FALSE;
					
					colOutX.SetType(OKDATAOBJ_DESIGNATION_X);
					colOutX.SetWidth(RESULT_COLUMN_WIDTH);
					SetOutput(colOutX, 1);
				}
			}

		}
			
		return true;
	}

	virtual	BOOL	GetGraphInfo(string &strTemplate, string &strBasePageName, string &strPageLabelPrefix, int &nPlotType)
	{
		ASSERT(FALSE);	// should be overridden
		return FALSE;
	}
	
	virtual	BOOL	DoGraphs(TreeNode &tr)
	{
		_BK
		
		if (!tr.GUI.PlotOption)
		{
			ResetAuxArray(OPAUXOBJ_GRAPHPAGE);	// no graphs
			return TRUE;	// no graphs
		}
		
		switch( tr.GUI.PlotOption.nVal )
		{
			case CFE_PLOT_OPTION_NO_PLOT:
				ResetAuxArray(OPAUXOBJ_GRAPHPAGE);	// no graphs
				break;
			case CFE_PLOT_OPTION_NEW_GRAPH:
			{
				GraphPage		pg;
				// Is it already remembered in the aux. array?
				if (!GetAuxObject(OPAUXOBJ_GRAPHPAGE, 0, pg))
				{
					string	strTemplate, strBasePageName, strPageLabelPrefix;
					int		nPlotType;
					if (GetGraphInfo(strTemplate, strBasePageName, strPageLabelPrefix, nPlotType))
					{
						tr.Temp.Plot.Template.strVal = strTemplate;
						tr.Temp.Plot.BasePageName.strVal = strBasePageName;					
						tr.Temp.Plot.PageLabelPrefix.strVal = strPageLabelPrefix;
						tr.Temp.Plot.Type.nVal = nPlotType;
					}
					
					tr.Output.Result.PlotName.strVal = CreateGraph(tr);
					break;
				}
				// Otherwise fall through (doing ChangeParams or AutoUpdate, so we don't want a new graph every time)
			}
			case CFE_PLOT_OPTION_SAME_LAYER:
				tr.Temp.Plot.Rescale.nVal = 1;
				UpdateGraph(tr);
				break;
		}
		
		return TRUE;
	}
	
	
	BOOL	SetResultsData(vectorbase& vResult, Column &colOutputX, Curve &crv, int nOffset)
	{
		return SetResultsData(vResult, colOutputX, crv, nOffset, crv);
	}
	
	
	BOOL	SetResultsData(vectorbase& vResult, Column &colOutputX, Curve &crv, int nOffset, vectorbase &vbY)
	{
		//ASSERT(vResult);
		if (colOutputX)
		{
			vResult = vbY;
		
			Dataset			dsX;
			if (crv.AttachX(dsX))
			{
				vectorbase		&vResultX = colOutputX.GetDataObject();
			
				vResultX = dsX;
			}
		}
		else
		{
			vResult.SetSubVector(vbY, nOffset);
		}
		
		return TRUE;
	}
	

	BOOL	Execute()
	{
		//_DBMGS("Inside ColumnFormulaEx::Execute()")
		
		_BK
		
		Tree		tr;
		GetTree(tr);		
		
		//Worksheet	wksOutput;
		//if(!PrepareOutput(wksOutput, tr, "NLSF"))
		//	return false;
		
		Column		colOutput, colOutputX;
		//if(!PrepareOutput(colOutput, tr))
		if(!PrepareOutput(colOutput, colOutputX, tr))
			return false;

		
		//curvebase	&cv = GetCurveInput();
		//
		//if( !cv )
		//	return FALSE;
		
		BOOL		bOK = DoFormula(colOutput, colOutputX, tr);
		if (bOK)
		{
			DoGraphs(tr);
			
			SetTree(tr);	// must remember the changes
		}
		else
			DestroyOutput();
		
		return bOK;
	}
	
	virtual int	GetResultIndicator()			{return ORI_COLUMN;}
	*/

};


//CPY 6/6/03
// I need a common base class for Smoothing and FFTFiltering, 
// use this class for all Column based operation that make use of trOperation.GUI.nMethodKind.nVal
class	ColumnOperationMultiOption : public	ColumnFormulaEx
{
protected:
	//virtual
	BOOL	GUItoOperationSpec(TreeNode& trOperation, TreeNode &trGUI, int nOption)
	{
		int			nKind = trGUI.nMethodKind.nVal;
		
		if(GUI_OP_NEW == nOption)
		{
			if(nKind != trOperation.Operation.Option.nVal)
			{
				out_str("invalid initialization during Construct or when reading default");
				trGUI.nMethodKind.nVal = trOperation.Operation.Option.nVal;
				nKind = trGUI.nMethodKind.nVal;
			}
		}		
		trOperation.Operation.Option.nVal = nKind;
		return TRUE;
	}
	
	//virtual	
	BOOL	OperationtoGUISpec(TreeNode& trOperation, TreeNode &trGUI, bool bInit)
	{
		int			nKind = trOperation.Operation.Option.nVal;
		trGUI.nMethodKind.nVal = nKind;
		
		return TRUE;
	}
};

	
