/*------------------------------------------------------------------------------*
 * File Name: xfgraph_utils.h													*
 * Creation: CPY 11/13/05														*
 * Purpose: support utility functions for all graph based interactive XF		*
 * Copyright (c) OriginLab Corp.2005											*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	Leo 2005-11-18 ADD_ON_SNAP													*
 *	Cloud 04/21/2007 SEND_MSG_WHEN_FIT_ACCOMPLISHED								*
 *	Hong 11/22/07 v8.0753 CLEAN_CODE_ALLOW_LT_ACCESS_WITHOUT_DATAPLOT_DEPENDENCE*
 *	Folger 12/11/07 SHOULD_OUTPUT_ERROR_MESSAGE_IN_DEBUG_MODE					*
 *  Sandy 2008-10-13 QA80-12366 ADD_USER_INFO_TREE_ON_EDIT						*
 *	ML 01/19/09 QA80-12944 ANCHOR_GETTING_DISTANCE_CORRECTLY					*
 *	RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP					*
 *------------------------------------------------------------------------------*/

////////////////////////////////////////////////////////////////////////////////////
#include <origin.h>
#include <XFGraph_Utils.h>
//#include <..\Originlab\curve_utils.h>
#include <GetNbox.h> /// Cloud 04/21/2007 SEND_MSG_WHEN_FIT_ACCOMPLISHED


/// RVD 1/16/2009 qa70-12981 MARKER_INFO_SNAP_TO_DATA_CLEANUP
/*
bool get_distance(LineInfo& obj2, LineInfo& obj1, int nIndex, double* px, double* py, GraphLayer& glyForCvtTransient)// = NULL=NULL)
{
	TreeNode tr1 = obj1;
	TreeNode tr2 = obj2;
	
	vector vx1, vy1;
	vector vx2, vy2;
	
	vx1 = tr1.Data.X.dVals;
	vy1 = tr1.Data.Y.dVals;
	if(nIndex < 0 || nIndex >= vx1.GetSize())
		return error_report("get_distance found invalid index");
	
	if(glyForCvtTransient)
	{
		double x = tr2.Transient.Matrix.eDx.dVal;// logical coordinates
		double y = tr2.Transient.Matrix.eDy.dVal;
		double x0 = vx1[nIndex];
		double y0 = vy1[nIndex];
		/// ML 01/19/09 QA80-12944 ANCHOR_GETTING_DISTANCE_CORRECTLY
		//int nx, ny;
		//glyForCvtTransient.WorldToPage(nx, ny, x0, y0);
		//
		// The reason for this code change is that, as far as I understand it,
		// the transient values from the matrix in tree are relative
		// to the previous position of the obj2, not of obj1.
		vx2 = tr2.Data.X.dVals;
		vy2 = tr2.Data.Y.dVals;
		int nx, ny;
		glyForCvtTransient.WorldToPage(nx, ny, vx2[0], vy2[0]);
		/// end ANCHOR_GETTING_DISTANCE_CORRECTLY

		nx += x;
		ny += y;
		glyForCvtTransient.PageToWorld(x, y, nx, ny);
		if(px) *px = x - x0;
		if(py) *py = y - y0;
		return true;
	}
	
	
	vx2 = tr2.Data.X.dVals;
	vy2 = tr2.Data.Y.dVals;
	if(nIndex >= vx2.GetSize())
		return error_report("get_distance found nIndex nIndex >= vx2.GetSize()");
	
	if(px)
		*px = vx2[nIndex] - vx1[nIndex];
	if(py)
		*py = vy2[nIndex] - vy1[nIndex];
	return true;
}
*/
bool get_distance(LineInfo& obj2, LineInfo& obj1, int nIndex, double* px, double* py, GraphLayer& glyForCvtTransient)// = NULL=NULL)
{
	double dx1 = 0, dy1 = 0;
	int iRet = obj1.GetWorldXY(nIndex, &dx1, &dy1);
	
	if( iRet < 2 )
		return error_report("get_distance obj1 world coordinates invalid!");
		
	double dx2 = 0, dy2 = 0;

	iRet = obj2.GetWorldXY(nIndex, &dx2, &dy2);
	
	if( iRet < 2 )
		return error_report("get_distance obj2 world coordinates invalid!");

	if( glyForCvtTransient )
	{
		// this case is for transient state of obj2
		// its coordinates returned by GetWorldXY() are not up to date
		// during moving we should transform them with transient matrix in logical (page) space
		int nx, ny;
		glyForCvtTransient.WorldToPage(nx, ny, dx2, dy2);
		// logical coordinates
		TreeNode tr2 = obj2;
		nx += tr2.Transient.Matrix.eDx.dVal;
		ny += tr2.Transient.Matrix.eDy.dVal;
		glyForCvtTransient.PageToWorld(dx2, dy2, nx, ny);
	}

	if(px)
		*px = dx2 - dx1;
	if(py)
		*py = dy2 - dy1;
	
	return true;
}
/// end MARKER_INFO_SNAP_TO_DATA_CLEANUP

bool get_plot_data_xy(DataPlot& dp, vector& vx, vector& vy, int i1, int i2,  bool bRemoveMissing)// = 0 = -1 = true 
{
	if(!dp.IsValid())
	{
		///Sandy 2007-5-15 need return false;
		//error_report("get_plot_data found invalid data plot");
		return false;
	}
		
	
	DataRange dr;
	if(dp.GetDataRange(dr, i1, i2))
	{
		DWORD dwPlotID;
		if(bRemoveMissing)
		{
			if(dr.GetData(DRR_GET_DEPENDENT | DRR_NO_FACTORS, 0, &dwPlotID, NULL, &vy, &vx) < 0)
				return error_report("get_plot_data failed GetData");
		}
		else
		{
			if(dr.GetData(DRR_GET_MISSING | DRR_GET_DEPENDENT | DRR_NO_FACTORS, 0, &dwPlotID, NULL, &vy, &vx) < 0)
				return error_report("get_plot_data failed GetData");
		}
		
		return true;
	}
	return error_report("get_plot_data failed GetDataRange");
}
		
bool load_GetN_from_page(Page& pg, TreeNode& trGetN, HWND& hWndDialog, LPCSTR lpcszStorageName)
{
	vector<byte> vb;
	if(pg.GetMemory(lpcszStorageName, vb))
	{
		string strGetN;
		if(strGetN.SetBytes(vb))
		{
			trGetN.XML = strGetN;
			int nhWnd;
			if(trGetN.GetAttribute(STR_HWND_ATTRIB, nhWnd))
			{
				hWndDialog = (HWND) nhWnd;
				return true;
			}
		}
	}
	return false;
}

bool save_GetN_to_page(Page& pg, TreeNode& trGetN, HWND hWndDialog, LPCSTR lpcszStorageName)
{
	vector<byte> vb;
	
	string strGetN;

	int nhWnd = (int) hWndDialog;
	
	///Kevin 11/16/05 CHECK_WHETHER_SET_GET_SUCCESS
	///trGetN.SetAttribute(STR_HWND_ATTRIB, nhWnd)
	if(! trGetN.SetAttribute(STR_HWND_ATTRIB, nhWnd))
		return false;
	///End CHECK_WHETHER_SET_GET_SUCCESS
	strGetN = trGetN.XML;

	if(strGetN.GetBytes(vb))
	{
		if (pg.SetMemory(lpcszStorageName, vb)) 
			return true;
	}
	
	return false;
}
enum{
	PARAMETER_TO_GETN,
	GETN_TO_PARAMETER
};

//Sandy 2006-10-17 RENAME_AGAIN
//Sandy 12/08/05 MOVE_FROM_XF
//Sandy 2006-1-3 MODIFY_FUNCTION_NAME
//void init_vxMarkers(DataPlot dp, int nPoint, vector& vxMI,vector& vyMI)
//bool average_dataplot_markers_by_point(DataPlot dp, vector& vxMI, vector& vyMI, int nPoint)
bool equably_divide_markers_in_dataplot(DataPlot dp, vector& vxMI, vector& vyMI, int nPoint)
{
	vxMI.SetSize(nPoint);
	vyMI.SetSize(nPoint);
	
	vector vx, vy;
	if(!get_plot_data_xy(dp, vx, vy) || vx.GetSize() < 2)
		return false;
	
	double x1 = vx[0];
	double x2 = vx[vx.GetSize()-1];
	
	for(int ii=0; ii<nPoint; ii++)
	{
		vxMI[ii] = x1 + ii * (x2-x1)/(nPoint-1.0);
		vyMI[ii] = 0;
	}
	
	return true;
	
}


bool set_data_plot(const DataPlot& dp, TreeNode& trGetN)
{
	DataRange dr;
	dp.GetDataRange(dr);

	trGetN.strVal = dr.GetDescription();
	trGetN.SetAttribute(STR_PLOTOBJ_UID_ATTRIB, (int)dp.GetUID());

	return true;
}

bool get_data_plot(DataPlot& dp, const TreeNode& trGetN)
{
	int  DataPlotUID;
	trGetN.GetAttribute(STR_PLOTOBJ_UID_ATTRIB, DataPlotUID);

	dp = (DataPlot) Project.GetObject(DataPlotUID);
	
	if(dp)
		return true;

	return false;
}




//////////////////////////////////////////////
bool PlotMarkers::InitXY(MarkerInfo& mi, const vector& vxMI, const vector& vyMI)
{
	mi.SetWidth(0);
	//mi.SetFollowsParent();
	
	m_trMI.Data.X.dVals = vxMI;
	m_trMI.Data.Y.dVals = vyMI;
	mi.SnapToParentXY();
	return true;
}


//////////////////////////////////////////////
int CurveMarkers::ProcessEvents(const EventInfo& ei, MarkerInfo& mi)
{
	int iRet = 0;

	int nEventID = ei.GetEvent();
	switch( nEventID )
	{
	case OE_INIT_ANCHORS:

		bool bRet = GetSettings();
		
		if (!bRet)
		{
			if (OnInit(mi))
			{
				SaveSettings();
				iRet = OCD_MODIFY|OCD_REPAINT;
			}

			///Sandy 2007-5-8 MOVE_TO_XF_WHICH_NEED
			//post_getn_dialog_message(WM_USER_ON_GRAPH_OBJECT_CHANGE); /// Cloud 04/21/2007 SEND_MSG_WHEN_FIT_ACCOMPLISHED
		}
		else
		{
			if(OnUpdate(ei.GetSenderParam(), mi))
			{ 
				SaveSettings();
				OnSnap(mi);
				iRet = OCD_MODIFY|OCD_REPAINT;
			}
		}

		break;
	case OE_GRAPHOBJ_EDIT:
		if( ei.GetSenderId() == GetID() )
		{

		    bool bRet = GetSettings();
		    
		    
			///Sandy 2008-10-13 QA80-12366 ADD_USER_INFO_TREE_ON_EDIT
			//if(OnEdit(ei.GetSenderParam(), mi))
			if(OnEdit(ei.GetSenderParam(), ei.GetUserData(), mi))
			{
				SaveSettings();
				OnSnap(mi);
				iRet = OCD_MODIFY|OCD_REPAINT;
			}

			///Sandy 2007-5-8 MOVE_TO_XF_WHICH_NEED
			//post_getn_dialog_message(WM_USER_ON_GRAPH_OBJECT_CHANGE);  /// Cloud 04/21/2007 SEND_MSG_WHEN_FIT_ACCOMPLISHED
		}
		break;
		
	
		
	case OE_XF_MSG:
		//printf("wParam = %d, lParam = %d\n", ei.GetSenderId(), ei.GetSenderParam());

		GetSettings();
		
		if(OnUpdate(ei.GetSenderParam(), mi))
		{
			SaveSettings();
			OnSnap(mi);

			iRet = OCD_MODIFY|OCD_REPAINT;
		}
		break;
		
	case OE_SELECT:
		if( ei.GetSenderId() == GetID() )
		{
			GetSettings();
			if(OnSelect(ei.GetSenderParam(), mi))
			{
				SaveSettings();
				iRet = OCD_MODIFY|OCD_REPAINT;
			}			
		}

		break;
		
	///Sandy 2008-4-3 ADD_AFTER_ROMAN_IMPLEMENT_11338
	case OE_DELETE_PART:
		if( ei.GetSenderId() == GetID() )
		{
		    bool bRet = GetSettings();
			if(OnDelPart(ei.GetSenderParam(), ei.GetUserData(), mi))
			{
				SaveSettings();
				OnSnap(mi);
				iRet = OCD_MODIFY|OCD_REPAINT;
			}
		}
		break;
	//end of ADD_AFTER_ROMAN_IMPLEMENT_11338
		
	}
	

	return iRet;	
}

///Leo 2005-11-29 MOVE_GETNTREE_FUNCTIONS_TO_PLOTMARKER
///Kevin 11/19/05 GETN_SAVE_TO_PAGE_SUPPORT
// bool CurveMarkers::GetGetNTree(TreeNode& trGetN, LPCSTR lpcszStorageName)
bool PlotMarkers::GetGetNTree(TreeNode& trGetN, LPCSTR lpcszStorageName)
{
	if(!m_gp)
	{
		///Sandy 2007-4-16 get layer from dataplot
		//GraphLayer gl = Project.ActiveLayer();
		GraphLayer gl;
		m_dp.GetParent(gl);
		m_gp = gl.GetPage();
	}

	if( lpcszStorageName != NULL)
		m_strStorageName = lpcszStorageName;

	if( !load_GetN_from_page(m_gp, trGetN, m_hwnd, m_strStorageName) )
		return false;

	return true;
}

///Leo 2005-11-29 MOVE_GETNTREE_FUNCTIONS_TO_PLOTMARKER
// bool CurveMarkers::SetGetNTree(TreeNode& trGetN, LPCSTR lpcszStorageName)
bool PlotMarkers::SetGetNTree(TreeNode& trGetN, LPCSTR lpcszStorageName)
{
	///Leo 2005-11-25 SHALL_WORK_WITHOUT_HWND
	// if (! m_gp || ! m_hwnd) 
	//	return false;
	if (!m_gp)
	{
		return false;
	}

	if( lpcszStorageName != NULL)
		m_strStorageName = lpcszStorageName;
	
	///Sandy 2007-4-23 REMOVE_NO_DLG_ANY_MORE
	///Leo 2005-11-25 SHALL_WORK_WITHOUT_HWND
	//if (save_GetN_to_page(m_gp, trGetN, m_hwnd, m_strStorageName)) 
	//{
		//if (m_hwnd)
		//{
			//Window wnd(m_hwnd);
			//if(wnd)
			//{
				//wnd.PostMessage(WM_USER_NOTIFY_STORED_GETN_CHANGED);
				////wnd.SendMessage(WM_USER_NOTIFY_STORED_GETN_CHANGED);
				//// return true;
			//}
		//}
		//return true;
	//}
	//return false;
	if (!save_GetN_to_page(m_gp, trGetN, m_hwnd, m_strStorageName))
		return false;
	///end

	return true;
	
}
///End GETN_SAVE_TO_PAGE_SUPPORT

///Sandy 2008-4-3 seem to no use
//static bool is_dataplot_in_graph(const DataPlot& dp, const GraphLayer& gl)
//{
	//return true;
//}

// save dp UID by set_plot_uid_to_tree,
// and also save Datasetname .strVal
bool dataplot_save_to_tree(TreeNode& trN, DataPlot& dp)
{
	if(!trN)
		return false;
	
	if(!dp.IsValid())
		return false;
	
	//string strName = dp.GetName();
	DataRange dr;
	dp.GetDataRange(dr);
	string strName = dr.GetDescription(GETLC_NO_DESIGNATIONS | GETLC_NO_ROWS);
	
	set_plot_uid_to_tree(trN, dp.GetUID());
	trN.strVal = strName;
	
	return true;
}

// return false if not found, or not valid (it may exist but not plotted in given layer)
// clear tree node if info not valid data plot
//  
bool dataplot_read_from_tree(GraphLayer& gl, TreeNode& trN, DataPlot& dp)
{
	if(!trN)
		return error_report("dataplot_read_from_tree found trN invalid");
	
	if(!gl.IsValid())
		return error_report("dataplot_read_from_tree found gl invalid");
	
	string dpName;
	dpName = trN.strVal;
	//---- CPY 12/10/2007 PFW_FAILED_FINDING_BASELINE
	if(dpName.IsEmpty())
	//	return error_report("dataplot_read_from_tree found trN empty.");
		//------ Folger 12/11/07 SHOULD_OUTPUT_ERROR_MESSAGE_IN_DEBUG_MODE
		//out_str("dataplot_read_from_tree found trN empty.");
		 ERR_MSG("dataplot_read_from_tree found trN empty.");
		//------
	//----
	foreach (DataPlot dpTemp in gl.DataPlots)
	{

		DataRange dr;
		dpTemp.GetDataRange(dr);
		
		string strName = dr.GetDescription(GETLC_NO_DESIGNATIONS | GETLC_NO_ROWS);
        if(dpName == strName)
        {
        	dp = dpTemp;
        	break;
        }
	}
	if(!dp)
	{
		return error_report("dataplot_read_from_tree cannot find matching plot");
	}	
	//if(!get_data_plot_from_range_tree(trN,  dp) || !dp.IsValid())
		//return false;
	
	
	return true;
}


///Sandy 2007-3-12 move to tree_utils.h
//Sandy 2006-1-9 Move_From_PFM_UTILS_FOR_USE_IN_O8_And_Modify_the_Prototype
//Frank add to check node by dataid, will move to tree.h later or else

//bool tree_check_get_node_by_dataid(TreeNode& trParent, TreeNode& trNew, LPCSTR lpcszTag, int nDataID, int nID, LPCSTR lpcszLableAttrib) // = 0 = NULL = NULL
//{
//
	//string strLabel(lpcszLableAttrib);
	//
	//if(strLabel.IsEmpty())
		//strLabel = lpcszTag;
	//
	//trNew = tree_get_node_by_dataid(trParent, nDataID);
	//
//
	//if(!trNew)
	//{
		/////----- Leo 2006-3-8 SAME_TAG_NAME_BUG
		////trNew  = tree_check_get_node(trParent, lpcszTag, nID, STR_LABEL_ATTRIB, strLabel );
		//
		//trNew = trParent.AddNode(lpcszTag);
		//
		//trNew.SetAttribute(STR_LABEL_ATTRIB, strLabel);
		//
		////----- end
		//
		//trNew.DataID = nDataID;
		//
		//return false;
	//}
//
	//return true;
//
//}




///Sandy 2007-3-31 add
/// Hong 11/22/07 v8.0753 CLEAN_CODE_ALLOW_LT_ACCESS_WITHOUT_DATAPLOT_DEPENDENCE
//bool get_gl_from_xyrange_plot(GraphLayer& gl, XYRange& xyr)
bool get_gl_from_xyrange_plot(GraphLayer& gl, XYRange& xyr, DataPlot* pdp) // = NULL
/// end CLEAN_CODE_ALLOW_LT_ACCESS_WITHOUT_DATAPLOT_DEPENDENCE
{
	DataPlot dp;
	if(!xyr.GetPlot(dp))
		return false;
		
	//if(!get_data_plot_from_range_tree(trN,  dp) || !dp.IsValid())
		//return false;		
	*pdp = dp; /// Hong 11/22/07 v8.0753 CLEAN_CODE_ALLOW_LT_ACCESS_WITHOUT_DATAPLOT_DEPENDENCE
	dp.GetParent(gl);
	
	return gl.IsValid();
}

/// YuI 12/17/07 VISUAL_FEEDBACK_FOR_INTEGRATION
//	bool get_gl_from_xyr_treenode(GraphLayer& gl, TreeNode& tr)
bool get_gl_from_xyr_treenode(GraphLayer& gl, TreeNode& tr, DataPlot& dp)
/// end VISUAL_FEEDBACK_FOR_INTEGRATION
{
	//XYRange xyr;
	if(!tr.IsValid())
		return false;
	
	TreeNode trTemp;
	if(tr.Range1)
	    trTemp = tr.Range1;
	else
		trTemp = tr;
	
	DataPlot dpTemp;
	if(!get_data_plot_from_range_tree(trTemp,  dpTemp) || !dpTemp.IsValid())
		return false;	
	
	dpTemp.GetParent(gl);
	/// YuI 12/17/07 VISUAL_FEEDBACK_FOR_INTEGRATION
	if( NULL != dp )
		dp = dpTemp;
	/// end VISUAL_FEEDBACK_FOR_INTEGRATION
	//okxf_resolve_tree_construct_range(&tr, &xyr);
	//
	//if(!xyr.IsValid())
		//return false;
	
	//return get_gl_from_xyrange_plot(gl, xyr);
	return gl.IsValid();
}
