/*----------------------------------------------------------------------------------*
 * File Name:	ProfilePlot.c			 											*
 * Creation:	Annia	9/24/03														*
 * Purpose: OriginC Source C file for ploting Z-level lines in Profile graph		*
 * Copyright (c) OriginLab Corp.	2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010	*
 * All Rights Reserved																*
 * 																					*
 * Modification Log:																*
 *----------------------------------------------------------------------------------*/
 
#include <Origin.h>
////////////////////////////////////////////////////////////////////////////////////
#define 	WKS_NAME			"ZLevels"
#define		PROFILEPLOT_STORAGE	"ProfilePlot"
#define		PROFILEPLOT_SECTION	"PageNames"

enum{	NO_ERR = 0,
		GRAPH_NOT_EXIST = 1,
		GRAPH_NOT_MATCH = 2,
		WKS_NAME_EMPTY = 3,
		WKS_NOT_EXIST = 4,
		WKS_COL_NUM_WRONG = 5
	};


// This class can plot Z-level lines when a profile plot is created, 
// and update the existing Z-level lines according to user's modification.
class ProfilePlot
{
public:
	ProfilePlot(string strGraphPageName, bool bMsgOnErr=false)
	{
		m_GraphErrType = checkGraphProfile(strGraphPageName);
		
		if(m_GraphErrType)
		{
			if (bMsgOnErr)
				outErr(m_GraphErrType);
		}

		init(strGraphPageName, bMsgOnErr);
	}
	
	// plot Z-level lines when there is no worksheet containing Z-level values.
	bool PlotZlevelLine()
	{
		if (m_GraphErrType)
			return show_err("PlotZlevelLine");
		
		if (!createZlevelWks())
			return false;
		
		if (!storeWksName2Graph())
			return false;
		
		if (!fillWksValue())
			return false;
		
		if (!plotLevelLine())
			return false;
		
		if (!updateScale())
			return false;
		
		return true;
	}
	
	// update Z-level lines when Z-level value changes.
	bool UpdateZScales()
	{
		if (m_GraphErrType)
			return false;

		if (!getZLevelWksName())
			return false;
			
		m_WksErrType = isZWksValid(m_strWksName);
			
		if (m_WksErrType)
		{
			switch (m_WksErrType)
			{
			case 4:					//the worksheet does not exist.
				break;
			case 5:					//the worksheet is not the one we need.
				delWksZlevel(m_strWksName);
				break;
			}
			PlotZlevelLine();
		}
		else
		{
			if (!fillWksValue())
				return false;
			
			if (!updateScale())
				return false;
		}
		
		return true;
	}
	
	// delete the worksheet when the profile graph is deleted or closed.
	bool DelZlevelWks()
	{
		if (m_GraphErrType)
			return show_err("DelZlevelWks");
		if (!getZLevelWksName())
			return error_report("DelZlevelWks failed getZLevelWksName");
		if (!delWksZlevel(m_strWksName))
			return error_report("DelZlevelWks failed delWksZlevel");
		
		return true;
	}
	
private:
	//---- CPY 10/27/2007
	bool show_err(LPCSTR lpcszHeader)
	{
		string strErr;
		strErr.Format("%s found m_GraphErrType = %d", m_GraphErrType);
		return error_report(strErr);
	}
	//----
	// Initialization to store graph name into m_strGraphName.
	void init(string strGraphPageName, bool bMsgOnErr)
	{
		m_strGraphName = strGraphPageName;
		m_bIsMsgOnErr = bMsgOnErr;
	}
	
	// Check that profile graph exists and has 3 layers
	int checkGraphProfile(string strGraphPageName)
	{
		GraphPage gpProfile(strGraphPageName);
		if (!gpProfile.IsValid())
			return GRAPH_NOT_EXIST;
		
		if (gpProfile.Layers.Count() != 3)
			return GRAPH_NOT_MATCH;
			
		return NO_ERR;
	}
	
	// Check that the worksheet exists and has 4 columns.
	int isZWksValid(string strZWksName)
	{
		Worksheet wksZlevel(strZWksName);
		if (!wksZlevel.IsValid())
			return WKS_NOT_EXIST;
		
		if (wksZlevel.Columns.Count() != 4)
			return WKS_COL_NUM_WRONG;
		
		return NO_ERR;
	}
	
	// Print out the error message to user.
	void outErr(int iErrType)
	{
		string strErrMsg;
		switch (iErrType)
		{
		case 1:
			strErrMsg = "The graph does not exist!";
			break;
		case 2:
			strErrMsg = "The graph should have 3 layers!";
			break;
		case 3:
			strErrMsg = "There is no worksheet associated with the specific graph!";
			break;
		}
		
		MessageBox( GetWindow(), strErrMsg, "Error");
	}
	
	// Create worksheet to contain Z-level values.
	// Store worksheet name into m_strWksName.
	bool createZlevelWks()
	{
		Worksheet wksLevels;
		if (!wksLevels.Create(NULL, CREATE_HIDDEN))
			return false;

		if (wksLevels.GetPage().Rename(WKS_NAME, false)==1)
			m_strWksName = WKS_NAME;
		else
			m_strWksName = wksLevels.GetPage().GetName();

		vector<string> vecColName = {"HX", "HY", "VX", "VY"};		
		
		while (wksLevels.DeleteCol(0));			// delete all columns that a default wks has.
		
		for (int ii=0; ii<vecColName.GetSize(); ii++)
		{
			wksLevels.AddCol(vecColName[ii]);
		}
		
		if (!wksLevels.SetColDesignations("XYXY"))
			return false;
		
		return true;
	}
	
	// Store the name of newly created worksheet into corresponding graph as infomation.
	bool storeWksName2Graph()
	{
		GraphPage gpGraph = Project.GraphPages(m_strGraphName);
		if (!gpGraph.IsValid())
			return false;		
		
		Tree trPageName;
		trPageName.Wks.strVal = m_strWksName;
		
		string strStorage = PROFILEPLOT_STORAGE;	//Info name is defined by PROFILEPLOT_STORAGE.
		string strSection = PROFILEPLOT_SECTION;	//Section name is defined by PROFILEPLOT_SECTION
		
		gpGraph.Info.Add(strStorage);
		storage stNames;
		stNames = gpGraph.GetStorage(strStorage);
		if(stNames)
		{
			if(!stNames.SetSection(strSection, trPageName))
				return false;
		}
		else
			return false;
		
		return true;
	}
	
	// Fill in the worksheet with Z-level values from the Colormap info of the corresponding graph.
	// Also used to update the worksheet when Z-level value changes.
	bool fillWksValue()
	{
		Worksheet wksLevels(m_strWksName);
		if (!wksLevels.IsValid())
			return false;
		
		wksLevels.Reset();
		
		GraphLayer glSource(m_strGraphName, 0);
		if (!glSource.IsValid())
			return false;
		DataPlot dpSource = glSource.DataPlots(0);
		
		vector vecLevel;		
		bool bRet = dpSource.GetColormap(vecLevel,m_bLogScale);
		if( !(bRet && vecLevel.GetSize()) )
		{
			return false;
		}		
		
		double xFrom, xTo, yFrom, yTo;			// the begin and end point of X and Y axis
		
		Scale sX(glSource.X);
		xFrom = sX.From;
		xTo = sX.To;
		Scale sY(glSource.Y);
		yFrom = sY.From;
		yTo = sY.To;
		
		int iCount = vecLevel.GetSize();		// number of level lines
		int iCount2 = iCount*2;					// number of wks rows containing Z-level values
		Dataset dsHX(wksLevels, 0);
		Dataset dsHY(wksLevels, 1);
		Dataset dsVX(wksLevels, 2);
		Dataset dsVY(wksLevels, 3);
		dsHX.SetSize(iCount2);
		dsHY.SetSize(iCount2);
		dsVX.SetSize(iCount2);
		dsVY.SetSize(iCount2);
		
		for (int ii=0; ii<iCount; ii++)
		{
			dsHX[ii*2] = xFrom;
			dsHX[ii*2+1] = xTo;
			dsHY[ii*2] = vecLevel[ii];
			dsHY[ii*2+1] = vecLevel[ii];
			
			dsVX[ii*2] = yFrom;
			dsVX[ii*2+1] = yTo;
			dsVY[ii*2] = vecLevel[ii];
			dsVY[ii*2+1] = vecLevel[ii];
		}

		return true;
	}	
	
	// Plot the Z-level lines according to the value of worksheet.
	bool plotLevelLine()
	{
		Worksheet wksLevels(m_strWksName);
		if (!wksLevels.IsValid())
			return false;
		
		GraphLayer glX(m_strGraphName, 1);
		GraphLayer glY(m_strGraphName, 2);
		if (!(glX.IsValid() && glY.IsValid()))
			return false;
		
		if (!( setPlotProperty(wksLevels, 0, 1, glX) && setPlotProperty(wksLevels, 2, 3, glY) ))
			return false;
		
		return true;
	}
	
	// Get worksheet name from profile graph
	bool getZLevelWksName()
	{
		GraphPage gpGraph = Project.GraphPages(m_strGraphName);
		if (gpGraph==NULL)
		{
			string strErr;strErr.Format("getZLevelWksName: graph(%s) not found",m_strGraphName); 
			return error_report(strErr);
		}
		
		string strStorage = PROFILEPLOT_STORAGE;
		string strSection = PROFILEPLOT_SECTION;
		
		Tree trName;
		storage stProfile;
		stProfile = gpGraph.GetStorage(strStorage);
		if (stProfile)
		{
			if (!stProfile.GetSection(strSection, trName))
				return false;
			m_strWksName = trName.Wks.strVal;
			if (m_strWksName.IsEmpty())
			{
				m_GraphErrType = WKS_NAME_EMPTY;
				return false;
			}
		}
		else
			return false;
		
		return true;
	}
	
	// Update the scale type by checking if Log Scale check box is selected.
	bool updateScale()
	{
		GraphLayer glXZ(m_strGraphName, 1);
		GraphLayer glYZ(m_strGraphName, 2);
		if (!( glXZ.IsValid() && glYZ.IsValid() ))
			return false;

		if (!(setPlotScale(glXZ) && setPlotScale(glYZ)))
			return false;
		
		return true;
	}
	
	// Set Z-level lines to a specific type.
	bool setPlotProperty(Worksheet wksZlevel, int iX, int iY, GraphLayer glProfilePlot)
	{
		Curve crvLevel(wksZlevel, iX, iY);
		
		int index = glProfilePlot.AddPlot(crvLevel, IDM_PLOT_LINE);
		DataPlot dpXLevel = glProfilePlot.DataPlots(index);
		if (!dpXLevel.IsValid())
			return false;
		dpXLevel.SetColor(18);					//set color as light grey
		dpXLevel.Curve.Line.Connect.nVal = 2;	//set connect type as 2 point segment
		
		return true;
	}
	
	// Reset Z axis type according to the Log Scale check box value.
	bool setPlotScale(GraphLayer glPlot)
	{
		Scale sZscale(glPlot.Y);
		if (m_bLogScale)
			sZscale.Type = LOG10_SPACE;
		else
			sZscale.Type = LINEAR_SPACE;
		
		return true;
	}
	
	// Delete the worksheet containing Z-level value.
	bool delWksZlevel(string strWksZName)
	{
		Worksheet wksZlevel(strWksZName);
		if (!wksZlevel.Destroy())
			return false;
		
		return true;
	}
	
private:
	string	m_strWksName;
	string	m_strGraphName;
	BOOL	m_bLogScale;
	bool	m_bIsMsgOnErr;
	int		m_WksErrType;
	int		m_GraphErrType;
};

// This function is called in profiler.ogs when a profile contour graph is created.
void ProfilePlotSetup(string strGraphPageName, bool bMsgOnErr=false)
{
	ProfilePlot myP(strGraphPageName, bMsgOnErr);
	myP.PlotZlevelLine();
}

// This function is called when user changes the Z-level value.
void ProfilePlotUpdate(string strGraphPageName, bool bMsgOnErr=false)
{
	ProfilePlot myP(strGraphPageName, bMsgOnErr);
	myP.UpdateZScales();
}

// This function is called when user deletes or closes the profile graph.
void ProfilePlotOnClose(string strGraphPageName)
{
	ProfilePlot myP(strGraphPageName);
	myP.DelZlevelWks();
}