/*------------------------------------------------------------------------------*
 * File Name:ColDesignations.h													*
 * Creation: CPY 6/28/2003														*
 * Purpose: OriginC Class for managing column plot designations for the 		*
 * the Plot Setup dialog														*
 * Copyright (c) Originlab Corp. 2003, 2004, 2005, 2006, 						*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	CPY 8/17/03 9/9/03 ROW_NUMBER_ROW_IMPROVEMENT								*
 *	CPY 9/15/03 ROW_NUMBER_ROW_AUTO_SHOW_IF_NO_X_COL_IN_WKS						*
 *	CPY 9/15/03, SET_ALL_TO_TOP_NO_PLOT											*
 *	RVD 9/3/2003 QA70-5078 v7.0682 PLOT_RANGE_APPLY								*
 *------------------------------------------------------------------------------*/

#ifndef _COL_DESIGNATIONS_H
#define _COL_DESIGNATIONS_H

#include <GetNBox.h> // We need GetNBox support for Row# X start/increment values

enum {INFO_COL_NAME = 1, INFO_COL_LABEL, INFO_COL_1ST_DATA, INFO_COL_INDEX};

#define STR_HEADING_COL			_LC("Column", _CAT)
#define STR_HEADING_DATASET		_LC("Dataset", _CAT)
#define STR_ROW_NUMBER			"Row#"
#define STR_ROWNUM_ROW_LABEL	_LC("From/Step=",_CAT)

class ColListControl : public GridListControl
{
public:
	ColListControl()
	{
		m_dwAuxPlotInfo = 0;
		m_dwAuxTypeInfo = 0;
		m_nNameCol = -1;
		m_nLabelCol = -1;
		m_n1stDataValCol = -1;
		m_nRadioColBegin = 0;
		m_nRadioColEnd = -1;
		m_RowNumberXFrom = 1;
		m_RowNumberXStep = 1;
		m_pOwner = NULL;
		m_bIsWksPages = false;
		m_bIsMatrixPage = false;
		m_bIsFunctionPlots = false;
		m_bEditMode = false;
	}
	void Init(int nID, Dialog& dlg, uint nPlotID, const string& strRegForDlgInfo, int nMakePlotExVal, int nXErrID, int nPosNegErrID, int nAddRowNumForX, int nPageNames, int nShowAllCols)
	{
		m_pOwner = &dlg; // save this so we can cast to correct class later
		
		GridListControl::Init(nID, dlg);
		SetAlternateRowColors();
		m_flx.Cols = 1;// just to show at least something
		m_flx.ExtendLastCol =true;// false;
		m_flx.Editable = flexEDKbdMouse;
		m_flx.FocusRect = flexFocusLight;//flexFocusInset
		if(0) // multiple selection, this is powerful, but could be confusing in terms of GUI
		{
			m_flx.SelectionMode = flexSelectionByRow;//flexSelectionFree;flexSelectionListBox;//
			m_flx.AllowSelection = true;
		}
		else
		{
			m_flx.SelectionMode = flexSelectionFree;
			m_flx.AllowSelection = false;
		}
		
		///////////////
		m_nPlotType	= Project.GetPlotTypeInfo(nPlotID, m_dwAuxTypeInfo, m_dwAuxPlotInfo, m_strColPattern);
		countYs();
		m_nMakePlotExVal = nMakePlotExVal;
		///////////////
		
		m_chkXErrBars = dlg.GetItem(nXErrID);
		m_chkPosNegErrBars = dlg.GetItem(nPosNegErrID);
		m_chkAddRowNumX = dlg.GetItem(nAddRowNumForX);
		m_staticCntrlPageNames = dlg.GetItem(nPageNames);
		m_chkShowAllCols = dlg.GetItem(nShowAllCols);
	}
	bool OnReady()
	{
		notifyDlgOnDesignationChanges(); // init the Add button
		return true;
	}
	
	int GetPlotType(DWORD *pdwAuxTypeInfo = NULL)
	{
		if (pdwAuxTypeInfo)
			*pdwAuxTypeInfo = m_dwAuxTypeInfo;
		return m_nPlotType;
	}
	
	bool IsAllowErrbars()
	{
		return IS_ALLOW_ERR_BARS(m_dwAuxTypeInfo);
	}
	bool IsAllowX() { return (m_dwAuxTypeInfo & PCD_NO_X)? false:true;}
	bool IsWorksheetPages() { return m_bIsWksPages;}
	
	Control GetItem(int nID)
	{
		Control cntrl;
		if(m_pOwner)
			cntrl = m_pOwner->GetItem(nID);
		return cntrl;
	}
	//virtual
	void ClearAll()
	{
		GridListControl::ClearAll();
		m_vsPageNames.SetSize(0);
	}
	// return number of Col Designations that is greater then the given value
	int GetNumDesignationsAssigned(int nDesigBase = COLDESIG_NONE)
	{
		int nCount = 0;
		int nR1 = m_flx.FixedRows;
		int nR2 = m_flx.Rows;
		for(int nRow = nR1; nRow < nR2; nRow++)
		{
			if(getColDesignation(nRow) > nDesigBase)
				nCount++;
		}
		return nCount;
	}
	void ClearColDesignations(int nRow = -1, int nCol = -1, bool bAllDown = false, bool bKeepX = false, bool bRepaint = true);
	void ClearAllDesignations(bool bKeepX = true) {ClearColDesignations(-1, -1, bKeepX, true);}
	
	void SetColDesginations(int nRow, int nCol, bool bAllDown, bool bRepaint = true);// otherwise all up
	void ApplyWksColDesignations(bool bRepaint = false)
	{
		HOUR_GLASS
		
		int nR1 = m_flx.FixedRows;
		int nR2 = m_flx.Rows;
		string strColName;
		string str1stYDataset;
		
		for(int nRow = nR1; nRow < nR2; nRow++)
		{
			//int nDesig = m_flx.RowData(nRow);
			strColName = m_flx.TextMatrix(nRow, m_nNameCol);
			if(m_bIsWksPages)
			{
				Worksheet wks(m_vsPageNames[0]);
				Column cc = wks.Columns(strColName);
				if(cc)
				{
					int nDesignation = colTypeToColDesignation(cc.GetType());
					if(COLDESIG_Y == nDesignation && str1stYDataset.IsEmpty())
						str1stYDataset = strColName;

					setDesignation(nRow, nDesignation);
				}
			}
			else if(m_bIsMatrixPage)
				setDesignation(nRow, COLDESIG_Z);
			else // loose datasets
			{
				DWORD dw = Project.GetDatasetInfo(strColName);
				if(PGDN_PLOTTABLE & dw)
				{
					if(str1stYDataset.IsEmpty())
						str1stYDataset = strColName;
					setDesignation(nRow, COLDESIG_Y);
				}
				else if(PGDN_IS_DATASET & dw)
					setDesignation(nRow, COLDESIG_X);
				else
					setDesignation(nRow, COLDESIG_NONE);
			}
		}
		//----- CPY 9/9/03 ROW_NUMBER_ROW_IMPROVEMENT
		m_RowNumberXFrom = 1;
		m_RowNumberXStep = 1;
		if(isRowNumberRowAllowed() && !str1stYDataset.IsEmpty())
		{
			double dbXFrom = 1, dbXStep = 1;
			string strDataset = m_bIsWksPages? m_vsPageNames[0] + "_" + str1stYDataset : str1stYDataset;
			if(GetDatasetXFromStep(strDataset, &dbXFrom, &dbXStep))
			{
				m_RowNumberXFrom = dbXFrom;
				m_RowNumberXStep = dbXStep;
				updateRowNumberXFromAndStep();
			}
		}
		//----- end ROW_NUMBER_ROW_IMPROVEMENT

		if(bRepaint)
			updateDesignations();

		notifyDlgOnDesignationChanges();
	}
	// called from PlotList when a data plot is edited
	bool OnStartEditPlot(int nPlotType, DWORD dwAuxTypeInfo, LPCSTR lpcszColPattern)
	{
		m_nPlotType = nPlotType;
		m_dwAuxTypeInfo = dwAuxTypeInfo;
		m_dwAuxPlotInfo = 0;
		m_strColPattern = lpcszColPattern;
		countYs();
		m_nMakePlotExVal = 0;
		m_bEditMode = true;
		return true;
	}
	void OnEndEditPlot()
	{
		m_bEditMode = false;
	}
	bool IsInEditMode() { return m_bEditMode;}
	bool IsReadyAddReplacePlot()
	{
		int nMainColDesignations = 0;
		//if(isRowNumberRowAllowed() && countDesignations(COLDESIG_X) != 1)
		if(isRowNumberRowAllowed() && !(PCD_X_OPTIONAL & m_dwAuxTypeInfo) && countDesignations(COLDESIG_X) != 1)
			return false;
		bool bCountY = false;
		if(PCD_ONLY_ONE_Z & m_dwAuxTypeInfo)
			nMainColDesignations = countDesignations(COLDESIG_Z);
		else
		{
			bCountY = true;
			nMainColDesignations = countDesignations(COLDESIG_Y);
		}
		if(!bCountY && nMainColDesignations != 1) 
			return false;
		
		if(bCountY && !hasEnoughYs(nMainColDesignations))
			return false;
	
		if(bCountY && IsInEditMode() && hasTooManyYs(nMainColDesignations))
			return false;
		
		if(m_nModifiers > 0 && countModifiers() != m_nModifiers)
			return false;
		
		return true;
	}
	// called mainly from PlotTypeListControl
	void OnChangePlotType(int nNewPlotID, int nPageType = 0, int nPlotExVal = 0, DWORD dwLayerBits = 0)
	{		
		RemoveSelection();
		if( 0 == nNewPlotID )
		{
			ClearAll();
			return;
		}
		updatePlotType(nNewPlotID, nPageType, dwLayerBits);
		m_nMakePlotExVal = nPlotExVal;			
		updateListAfterChange(false, false, true);
	}
	bool ApplyColDesignations(const vector<string>&	vsColNames, const vector<uint>&	vnColDesignations, bool bRepaint = true)
	{
		HOUR_GLASS
		
		int nRow;
		for(int ii = 0; ii < vsColNames.GetSize(); ii ++)
		{
			nRow = FindRow(vsColNames[ii], m_nNameCol);
			if(nRow >= 0 && ii < vnColDesignations.GetSize())
				setDesignation(nRow, vnColDesignations[ii]);
			else if(nRow < 0 && (nRow = getRowNumberRow()) > 0) // not found, check to see if Row# CPY 8/21/03 QA70-5034
			{
				if(COLDESIG_X == vnColDesignations[ii] && vsColNames[ii].Find('/') > 0) // From/Step
				{
					setDesignation(nRow, COLDESIG_X);
					setXFromStepStr(vsColNames[ii]);
				}
			}
		}
		if(bRepaint)
			updateDesignations();
			
		notifyDlgOnDesignationChanges();
		return true;
	}
	int	GetNumLayers()
	{
		int		nNum = m_dwAuxPlotInfo & LTPI_LAYERS_NUMBER_MASK;
		if (nNum < 1)
			nNum = 1;
		
		return nNum;
	}	
	void UpdateList()// from default if no wks name given
	{
		vector<string> vsWksNames;
		WorksheetPage wksPage = Project.Pages();
		if(wksPage)
		{
			vsWksNames.Add(wksPage.GetName());
			UpdateList(vsWksNames);
		}
		else
			ClearAll();
	}
	void UpdateList(vector<string>& vsWksPageNames, bool bApplyWks = false, int nPageType = 0)
	{
		ClearAll();
		//--- CPY 9/15/03 ROW_NUMBER_ROW_AUTO_SHOW_IF_NO_X_COL_IN_WKS
		if(EXIST_WKS == nPageType && !m_chkAddRowNumX.Check)
		{
			Worksheet wks(vsWksPageNames[0]);
			if(wks)
			{
				string strColDesigs = wks.GetColDesignations();
				if(strColDesigs.Find('X') < 0) // no X col in wks
				{
					//out_str("Force Row#");
					m_chkAddRowNumX.Check = true;
				}
			}
		}
		//--- end
		if(nPageType < EXIST_DATA)
			nPageType = guessPageType(vsWksPageNames[0]);
			
		updateWksPageNames(vsWksPageNames, nPageType);
		updateShowWksNames();
		setupGridCols();
		vector<string> vsColNames, vsColLabels;
		
		m_vsColNames.SetSize(0);
		m_vsColLabels.SetSize(0);
		if(vsWksPageNames.GetSize() && Project.GetCommonColumns(vsWksPageNames, vsColNames, vsColLabels, isKeepColOrder()))
		{
			if(isRowNumberRowAllowed())//getRowNumberRow(false) > 0)
			{
				m_vsColNames.Add(STR_ROW_NUMBER);
				m_vsColLabels.Add(getXFromStepStr());
			}
			m_vsColNames.Append(vsColNames);
			m_vsColLabels.Append(vsColLabels);
		}
		updateListAfterChange(bApplyWks, true);
	}
	void OnChangeAddRowNumbers()
	{
		RemoveSelection();
		updateRowNumbersRow();
		updateColWidths();
	}
	void updateColWidths()
	{
		ResizeCols(4);
	}
	void OnChangeErrBarOptions()
	{
		RemoveSelection();
		setupGridCols();
		updateColNameLabelsAndOtherInfo(false);
		//ResizeCols(4);
		updateDesignations(true);
	}
	/// RVD 9/3/2003 QA70-5078 v7.0682 PLOT_RANGE_APPLY
	//bool GetDataPlots(TreeNode& trPlotList, bool bTestValid = false)
	bool GetDataPlots(TreeNode& trPlotList, bool bTestValid = false, DWORD dwPlotView = DPEDTVIEW_HIDE_LIMITS)
	/// end PLOT_RANGE_APPLY
	{
		vector<uint> vnColDesignations;
		//----- CPY 8/20/03 ADD_PLOT_WRONG_X_AFTER_SORT
		vector<uint> vnPositions;
		getColDesignations(vnColDesignations, vnPositions);
		vector<uint> vnReorderIndexes;
		vnPositions.Sort(SORT_ASCENDING, TRUE, vnReorderIndexes);
		vnColDesignations.Reorder(vnReorderIndexes);
		//-----
		string strSaveRowNumberDisplay;
		int nRow = -1;
		if((nRow = getRowNumberRow()) > 0)
		{
			nRow = 0;//-= m_flx.FixedRows;
			strSaveRowNumberDisplay = m_vsColNames[nRow];// Row# is always at index 0
			m_vsColNames[nRow] = m_vsColLabels[nRow];
			m_vsColLabels[nRow] = "";
		}
		
		if (PCD_LTPLOTINFO_HAS_NEXVAL & m_dwAuxTypeInfo)
		{
			DWORD		dwExValue = m_dwAuxPlotInfo & LTPI_NEXVAL_MASK;
			m_nMakePlotExVal = dwExValue >> NEXVAL_MASK_BITSHIFT_IN_LTPI;
		}
		
		vector<string> vsPagesSave;
		if(bTestValid) // if testing, only need 1st page
		{
			vsPagesSave = m_vsPageNames;
			m_vsPageNames.SetSize(1);
		}
		
		/// RVD 9/3/2003 QA70-5078 v7.0682 PLOT_RANGE_APPLY
		//bool bRet = Project.MakeDataplotsTree(trPlotList, m_nPlotType, m_dwAuxTypeInfo, m_dwAuxPlotInfo, m_strColPattern, m_nMakePlotExVal,
		//	vnColDesignations, m_vsPageNames, m_vsLayerNames, m_vsColNames);
		BOOL bNoLimits = (dwPlotView & DPEDTVIEW_HIDE_LIMITS) ? TRUE : FALSE;
		bool bRet = Project.MakeDataplotsTree(trPlotList, m_nPlotType, m_dwAuxTypeInfo, m_dwAuxPlotInfo, m_strColPattern, m_nMakePlotExVal,
			vnColDesignations, m_vsPageNames, m_vsLayerNames, m_vsColNames, bNoLimits);
		/// end PLOT_RANGE_APPLY


		if(bTestValid) // if testing, only need 1st page, now restore
			m_vsPageNames = vsPagesSave;

		if(nRow >= 0)
		{
			m_vsColLabels[nRow] = m_vsColNames[nRow];
			m_vsColNames[nRow] = strSaveRowNumberDisplay;
		}
		return bRet;
	}

private:
	bool isValidColDesignations()
	{
		Tree trTemp = NULL;
		return GetDataPlots(trTemp, true);
	}
	int guessPageType(LPCSTR lpcszName)
	{
		Page  pg(lpcszName);
		if(pg)
			return pg.GetType();
		
		return EXIST_DATA;
	}
	bool updateRowNumberXFromAndStep(bool bCheckShowRow = false)
	{
		int nRow = getRowNumberRow();
		if(nRow > 0 && getFromStepColIndex() > 0)
		{
			int ii = getRowNumberRowArrayIndex(); //nRow - m_flx.FixedRows;
			m_vsColLabels[ii] = getXFromStepStr();
			//----- CPY 8/17/03 ROW_NUMBER_ROW_IMPROVEMENT
			//m_flx.TextMatrix(nRow, m_nLabelCol) = m_vsColLabels[ii];
			if(m_n1stDataValCol > 0)
				m_flx.TextMatrix(nRow, getFromStepColIndex()) = m_vsColLabels[ii];
			//-----
			
			if(bCheckShowRow && !m_chkAddRowNumX.Check)
			{
				m_chkAddRowNumX.Check = true;
				HideRow(nRow, false);
			}
				
			return true;
		}
		return false;
	}
	bool updateRowNumbersRow()
	{
		int nRow = findRowNumberRow();
		if(nRow < 0 && !isRowNumberRowAllowed())
			return false; // consistent, nothing to do
		
		if(nRow && !isRowNumberRowAllowed()) // was added before, we need to just hide it
			m_chkAddRowNumX.Check = false;
		else if(nRow < 0) // no such row, so does not matter
			return false;
		
		if(m_chkAddRowNumX.Check)
		{
			updateDesigationRadio(nRow);
			//m_flx.RowHidden(nRow) = false;
			HideRow(nRow, false);
		}
		else
		{
			setDesignationRadioChoice(nRow, COLDESIG_NONE);
			//m_flx.RowHidden(nRow) = true;
			HideRow(nRow, true);
		}
		return true;
	}
	void updateListAfterChange(bool bApplyWks, bool bResetDesigationRadio, bool bResetGrid = false)
	{
		HOUR_GLASS
		
		if(bResetGrid)
			setupGridCols();
		
		updateColNameLabelsAndOtherInfo(bResetDesigationRadio);
		updateShowColsRows();
		if(bApplyWks)
			ApplyWksColDesignations();
		//ResizeCols(4);
		updateDesignations(true);
		notifyDlgOnDesignationChanges();

		if(isAllowSortCols())
			m_flx.ExplorerBar = flexExSortShow;
		else
			m_flx.ExplorerBar = flexExNone;
	}	
	void updateColNameLabelsAndOtherInfo(bool bResetDesigationRadio = true)
	{
		int nFixedRows = m_flx.FixedRows;
		m_flx.Rows = nFixedRows + m_vsColNames.GetSize();
		for(int ii = 0, nRow = nFixedRows; ii < m_vsColNames.GetSize(); ii++, nRow++)
		{
			if(bResetDesigationRadio)
				m_flx.RowData(nRow) = COLDESIG_NONE;
			
			m_flx.TextMatrix(nRow, m_nNameCol) = m_vsColNames[ii];
			if(m_nLabelCol > 0)
			{
				if(getRowNumberRow() == nRow)			
					m_flx.TextMatrix(nRow, m_nLabelCol) = STR_ROWNUM_ROW_LABEL; //----- CPY 8/17/03 ROW_NUMBER_ROW_IMPROVEMENT
				else
					m_flx.TextMatrix(nRow, m_nLabelCol) = m_vsColLabels[ii];
			}
			if(m_nIndexCol > 0)
				m_flx.TextMatrix(nRow, m_nIndexCol) = getColPositionInWks(ii, m_vsColNames[ii]);	
			if(m_n1stDataValCol > 0)
				m_flx.TextMatrix(nRow, m_n1stDataValCol) = get1stDataVal(m_vsColNames[ii]);
		}
	}
	void updateShowColsRows()
	{
		bool	bIsShowAll = m_chkShowAllCols.Check;
		
		int nFixedRows = m_flx.FixedRows;
		for(int ii = 0, nRow = nFixedRows; ii < m_vsColNames.GetSize(); ii++, nRow++)
		{
			if(bIsShowAll || !isWksColHiddenOrDisregard(m_vsColNames[ii]))
				HideRow(nRow, false);
			else
			{
				HideRow(nRow, true);
				m_flx.RowData(nRow) = COLDESIG_NONE;
			}
		}
		updateRowNumbersRow();// must do this after updateShowAllCols which also HideShowRows
	}
	void countYs()
	{
		m_nYs = 0;
		m_nModifiers = 0;
		int nPos = m_strColPattern.Find('Y');
		while(nPos >= 0)
		{
			m_nYs++;
			nPos = m_strColPattern.Find('Y', nPos + 1);
		}
		// not all plot IDs has correct string, so some needed correction
		if(PCD_EXACT_ONE_GROUP & m_dwAuxTypeInfo) // the rest are modifiers
		{
			m_nYs = 1;
			m_nModifiers = 2;// XYXY, may need to be more general later
		}
	}
	bool hasEnoughYs(int nYs)
	{
		if(nYs < 1)
			return false;
		if(IS_PCD_EXACT_Y_COLS(m_dwAuxTypeInfo) && nYs != m_nYs)
			return false;
		if(IS_PCD_EXACT_Y_COLS_OR_ONE_LESS(m_dwAuxTypeInfo) && (nYs < m_nYs -1 || nYs > m_nYs))
			return false;
		
		return true;
	}
	bool hasTooManyYs(int nYs)
	{
		if(!IS_PCD_EXACT_Y_COLS_OR_MORE(m_dwAuxTypeInfo) && nYs > m_nYs)
			return true;
		return false;
	}
	void updatePlotType(int nNewPlotID, int nPageType, DWORD dwLayerBits = 0)
	{
		//---- CPY 9/9/03 v7.5689 TERNARY_PLOT_USE_2D_TYPES
		//printf("id %d, dw %d, nLayer = %d\n", nNewPlotID, dwLayerBits, HIWORD(dwLayerBits));
		/*
		if(nNewPlotID)
			m_nPlotType	= Project.GetPlotTypeInfo(nNewPlotID, m_dwAuxTypeInfo, m_dwAuxPlotInfo, m_strColPattern);
		if(nPageType)
		{
			if(EXIST_DATA == nPageType || EXIST_FUNC_PLOT == nPageType)
				m_dwAuxTypeInfo &= ~PCD_CAN_ADD_E_H_L;
			if(EXIST_FUNC_PLOT == nPageType)
				m_dwAuxTypeInfo |= PCD_NO_X;
			
		}
		
		if (PCD_LAYER_TRI == dwLayerBits)
			m_dwAuxTypeInfo = PCD_LAYER_TRI | PCD_EXACT_YCOLS | PCD_Z_PREFER_Y | PCD_GROUP_MULTI_YS | PCD_PREFER_X | PCD_CAN_ADD_E_H_L | PCD_HIDE_ERR_BARS;
		else if (PCD_LAYER_SMITH == dwLayerBits)
			m_dwAuxTypeInfo |= PCD_HIDE_ERR_BARS;
		*/
		//nNewPlotID can be 0, and get_plot_type_info will fix other bits, change m_nPlotType only if nNewPlotID is not zero  
		int nn = get_plot_type_info(nNewPlotID, nPageType, dwLayerBits, m_dwAuxTypeInfo, m_dwAuxPlotInfo, m_strColPattern);
		if(nn)
		{
			m_nPlotType = nn;
			countYs();
		}
		//----
	}
	void setupGridCols()
	{
		updateErrBarOptions();
//		out_int("Plot type = ", m_nPlotType);
		if(!Project.GetPlotDesignations(m_nPlotType, m_dwAuxTypeInfo, m_strColPattern, m_vnColDesignations))
			m_vnColDesignations.SetSize(0);	
		prepareGridColumns();		
	}
	void updateErrBarOptions()
	{
		m_dwAuxTypeInfo &= ~(PCD_SHOW_X_ERR | PCD_SHOW_POS_NEG_ERR);
		if(IsAllowErrbars())//(m_dwAuxTypeInfo & PCD_CAN_ADD_E_H_L)
		{
			if(m_chkXErrBars.Check)
				m_dwAuxTypeInfo |= PCD_SHOW_X_ERR;
			if(m_chkPosNegErrBars.Check)
				m_dwAuxTypeInfo |= PCD_SHOW_POS_NEG_ERR;
		}
	}
	void updateDesignations(bool bResizeCols = false)
	{
		int nR1 = m_flx.FixedRows;
		int nR2 = m_flx.Rows;
		for(int nRow = nR1; nRow < nR2; nRow++)
			updateDesigationRadio(nRow);
	
		if(bResizeCols)
			updateColWidths();
	}
	void updateDesigationRadio(int nRow) // update check box according to RowData
	{
		int nDesig = m_flx.RowData(nRow);
		int nRadioChoice = getDesignationRadioChoice(nDesig);
				
		for(int nCol = m_nRadioColBegin, nRadio = 0; nCol <= m_nRadioColEnd; nCol++, nRadio++)
		{
			if(nRadioChoice == nRadio)
				m_flx.TextMatrix(nRow, nCol) = 1;
			else
				m_flx.TextMatrix(nRow, nCol) = 0;
		}
		
		if(nDesig != COLDESIG_NONE && isSingleDesignation(nDesig))
			checkSingleDesignationForAllRows(nRow, nDesig);
	}
	//only 1 X is allowed, we may relax this later
	checkSingleDesignationForAllRows(int nRowNewChange, int nDesignationToCheck)
	{
		int nDesig = m_flx.RowData(nRowNewChange);
		if(nDesignationToCheck == nDesig)
		{
			int nR1 = m_flx.FixedRows;
			int nR2 = m_flx.Rows;
			int nD;
			for(int nRow = nR1; nRow < nR2; nRow++)
			{
				if(nRowNewChange == nRow)
					continue;
				nD = m_flx.RowData(nRow);
				if(nDesignationToCheck == nD)
				{
					m_flx.RowData(nRow) = COLDESIG_NONE;
					updateDesigationRadio(nRow);
				}
			}
		}
	}
	bool isAllowSortCols()
	{
		DWORD dwModifierBits = PCD_MODIFIER_SIZE|PCD_MODIFIER_COLOR|PCD_MODIFIER_VEC_AM|PCD_MODIFIER_VEC_XYEND;
		if(m_dwAuxTypeInfo & dwModifierBits)
			return false;

		//---- CPY 9/10/03 v7.5690 HI_LO_CLOSE_FLOAT_COLS
		if(m_nYs > 1 && (m_dwAuxTypeInfo & PCD_EXACT_YCOLS))
			return false;
		//----
		return true;
	}
	bool isSingleDesignation(int nDesig) // between rows in table
	{
		if(COLDESIG_X == nDesig)
			return true;
		if(nDesig >= COLDESIG_SIZE) // all modifier does not make sense to have more then one
			return true;
		if(m_dwAuxTypeInfo & PCD_EXACT_ONE_GROUP)
			return true;
		
		return false;
	}
	int getFromStepColIndex()
	{
		return m_n1stDataValCol;
	}
	string getXFromStepStr()
	{
		string str;
		cvt_x_from_step_to_str(m_RowNumberXFrom, m_RowNumberXStep, str.GetBuffer(MAXLINE), MAXLINE);
		str.ReleaseBuffer();
		return str;
	}
	bool setXFromStepStr(LPCSTR lpcsz, bool bCheckShowRow = true) // must be in the form of 123/456
	{
		string str = lpcsz;
		int nPos = str.Find('/');
		if(nPos > 0 && nPos < str.GetLength())
		{
			string strFrom = str.Left(nPos);
			string strStep = str.Mid(nPos+1);
			m_RowNumberXFrom = atof(strFrom);
			m_RowNumberXStep = atof(strStep);
			updateRowNumberXFromAndStep(bCheckShowRow);
			return true;
		}
		return false;
	}
	int getRowNumberRowArrayIndex()
	{
		return 0; // this should never changed
	}
	bool isRowNumberRowAllowed()
	{
		return (m_dwAuxTypeInfo & PCD_NO_X) || ( m_dwAuxTypeInfo & PCD_ONLY_ONE_Z)? false:true;
	}
	int findRowNumberRow()
	{
		return FindRow(STR_ROW_NUMBER, m_nNameCol);
	}
	int getRowNumberRow(bool bCheckSort = true)
	{
		// there is some inconsistency sometimes, so PCD_NO_X is not always present
		if(!isRowNumberRowAllowed())
			return -1;
		
		//----- CPY 8/20/03, Row# row can be sorted out
		if(bCheckSort && isAllowSortCols())
			return findRowNumberRow();	
		else
			return m_flx.FixedRows;
		//-----
	}
	bool isXCol(int nCol)
	{
		if(!isColDesignationInList(COLDESIG_X))
			return false;
	
		// if X, must be 1
		if(1==nCol)
			return true;

		return false;
	}
	int getDesignationRadioChoice(int nDesignation)
	{
		int nRadioChoice = 0;
		for(int ii = 0; ii < m_vnColDesignations.GetSize(); ii++)
		{
			if(nDesignation == m_vnColDesignations[ii])
			{
				nRadioChoice = ii + 1;// disregard not in designation list
				break;
			}
		}
		return nRadioChoice;
	}
	int getDesignationOfCol(int nCol) // return -1 if not one of Col Designations Cols
	{
		// disregard not in designation list
		if(nCol > m_vnColDesignations.GetSize()) 
			return -1;
		
		if(0 == nCol)
			return COLDESIG_NONE;
		
		return m_vnColDesignations[nCol-1];
	}
	bool setDesignation(int nRow, int nDesig, bool bCheckInList = true)
	{
		if(bCheckInList && !isColDesignationInList(nDesig))
			return false;
		m_flx.RowData(nRow) = nDesig;
		return true;
	}
	bool setDesignationRadioChoice(int nRow, int nChoice)
	{
		int nDesig = COLDESIG_NONE;
		if(nChoice > 0 && nChoice <= m_vnColDesignations.GetSize())
			nDesig = m_vnColDesignations[nChoice-1];
		//--- CPY 9/15/03, SET_ALL_TO_TOP_NO_PLOT
		if(getRowNumberRow() == nRow && nDesig > COLDESIG_X)
			return false;
		//---
		int nOldDesignation = getColDesignation(nRow);
		//m_flx.RowData(nRow) = nDesig;
		setDesignation(nRow, nDesig, false);
		if(nChoice)
		{
			if(!isValidColDesignations())
			{
				setDesignation(nRow, nOldDesignation, false);
				return false;
			}
		}
		return true;
	}
	int getColDesignation(int nRow)
	{
		if(nRow < m_flx.FixedRows)
			return -1;
		
		int nDesig = m_flx.RowData(nRow);
		return nDesig;
	}
	// assume vnColDesignations and vnPositions empty on entering function
	bool getColDesignations(vector<uint>& vnColDesignations, vector<uint>& vnPositions)
	{
		int nR1 = m_flx.FixedRows;
		int nR2 = m_flx.Rows;
		for(int nRow = nR1; nRow < nR2; nRow++)
		{
			int nDesig = getColDesignation(nRow);//m_flx.RowData(nRow);
			vnColDesignations.Add(nDesig);
			vnPositions.Add(atoi(GetCell(nRow, m_nIndexCol)));// grid might have been sorted
		}
		return true;
	}
	int countDesignations(int nDesig)
	{
		int nR1 = m_flx.FixedRows;
		int nR2 = m_flx.Rows;
		int nCount = 0;
		for(int nRow = nR1; nRow < nR2; nRow++)
		{
			if(getColDesignation(nRow) == nDesig)
				nCount++;
		}
		return nCount;
	}
	int countModifiers()
	{
		int nR1 = m_flx.FixedRows;
		int nR2 = m_flx.Rows;
		int nCount = 0;
		int nDesig;
		for(int nRow = nR1; nRow < nR2; nRow++)
		{
			nDesig = getColDesignation(nRow);
			if(nDesig >= COLDESIG_SIZE)
				nCount++;
		}
		return nCount;
	}	
	void prepareGridColumns()
	{
		string str = " ";// begin 1st Col with a blank for disregarded col designation
		for(int ii = 0; ii < m_vnColDesignations.GetSize(); ii++)
		{
			str += "|";
			str += getColDesignationName(m_vnColDesignations[ii]);
		}
		m_nRadioColBegin = 0;
		m_nRadioColEnd = ii;
		int nInfoCol = 1; // offset from current
		m_nNameCol = ii + addColHeading(str, nInfoCol++);// there is still that 1st col with disregard
		m_nLabelCol = ii + addColHeading(str, nInfoCol++);
		m_n1stDataValCol = ii + addColHeading(str, nInfoCol++);
		//--CPY 8/6/03 ADD_COL_POSITION_IN_WKS
		m_nIndexCol = ii + addColHeading(str, nInfoCol++);
		//--
		m_flx.FormatString = str;
		m_flx.Cols = ii + nInfoCol;
		int nCol;
		for(nCol = m_nRadioColBegin; nCol <= m_nRadioColEnd; nCol++)
		{
			m_flx.ColDataType(nCol) = flexDTBoolean;
			m_flx.ColAlignment(nCol) = flexAlignCenterCenter;
		}
		//for(nCol = m_nRadioColEnd+1; nCol <= m_nLabelCol; nCol++)
		for(nCol = m_nRadioColEnd+1; nCol <m_flx.Cols; nCol++)
		{
			m_flx.ColDataType(nCol) = flexDTString;
			m_flx.ColAlignment(nCol) = flexAlignLeftCenter;
		}
		m_flx.ColHidden(m_nRadioColBegin) = true; // Joe etc suggested that disregard radio col can be left out
		
		updateColNameColHeading();
	}
	//return nTypeEnum
	int addColHeading(string& strFormat, int nTypeEnum) // starting from 1
	{
		strFormat += "|";
		switch(nTypeEnum)
		{
		case INFO_COL_NAME:
			strFormat += STR_HEADING_COL;
			break;
		case INFO_COL_LABEL:
			strFormat += _LC("Label", _CAT);
			break;
		case INFO_COL_INDEX:
			strFormat += _LC("Position", _CAT);
			break;			
		case INFO_COL_1ST_DATA:
			strFormat += _LC("1st Data Value", _CAT);
			break;
		default:
			ASSERT(FALSE);
			break;
		}
		return nTypeEnum;
	}
	string getColHeadingTooltips(int nCol);
	
	bool isRowNumber(LPCSTR lpcszColName)
	{
		return lstrcmpi(lpcszColName, STR_ROW_NUMBER) == 0? true:false;
	}
	string getColPositionInWks(int nIndex, LPCSTR lpcszColName)
	{
		string str = nIndex + 1;// 1 offset
		//printf("1st wks is %s\n", m_vsPageNames[0]);
		
		Worksheet wks(m_vsPageNames[0]);
		if(wks)
		{
			Column cc = wks.Columns(lpcszColName);
			if(cc)
				str = cc.GetIndex() + 1; // 1 offset
			else if(isRowNumber(lpcszColName))
				str = 0;
		}
		return str;
	}
	
	string get1stDataVal(LPCSTR lpcszColName)
	{
		string str;
		Worksheet wks(m_vsPageNames[0]);
		if(wks)
		{
			Column cc = wks.Columns(lpcszColName);
			if(cc)
			{
				int nRow = cc.GetLowerBound();
				int nCol = cc.GetIndex();
				wks.GetCell(nRow, nCol, str);
			}
			else if(isRowNumber(lpcszColName))
				str = getXFromStepStr();
		}
		return str;
	}
	bool isWksColHiddenOrDisregard(LPCSTR lpcszColName)
	{
		Worksheet wks(m_vsPageNames[0]);
		if(wks)
		{
			Column cc = wks.Columns(lpcszColName);
			if(cc)
			{
				if(cc.Show==0 || cc.GetType() == OKDATAOBJ_DESIGNATION_NONE)
					return true;
			}
		}
		return false;
	}
//protected:
public:
	virtual string GetRuntimeClass()
	{
		return "ColListControl";
	}
	virtual int GetDefaultSize(){return 178;}

	////------------------ vsFlex events
	void OnAfterEdit(Control flxControl, int nRow, int nCol)
	{
		OnAfterEdit(nRow, nCol);
	}
	//virtual 
	void OnAfterEdit(int nRow, int nCol)
	{
		GridListControl::OnAfterEdit(nRow, nCol);
		bool bSetOK = true;
		int nVal = m_flx.Cell(flexcpText, nRow, nCol);
		if(nVal)
			bSetOK = setDesignationRadioChoice(nRow, nCol - m_nRadioColBegin);
		else
			setDesignationRadioChoice(nRow, 0);// always OK to clear
		
		updateDesigationRadio(nRow);
		if(bSetOK)
			notifyDlgOnDesignationChanges();
	}	
	void OnBeforeEdit(Control cntrl, long nRow, long nCol, BOOL* pCancel)
	{
		if(nRow > 0 && nRow == getRowNumberRow())
		{
			if(nCol == getFromStepColIndex())
			{
				m_flx.ComboList = "...";
				return;
			}
			//----- CPY 8/17/03 ROW_NUMBER_ROW_IMPROVEMENT CPY 8/17/03 ROW_NUMBER_ROW_ONLY_X_ALLOWED
			else if(!isXCol(nCol))// for Row number col, only X is allowed
			{
				*pCancel = true;
				return;
			}
			//----- end	
		}
		string str = m_flx.ComboList;
		if(!str.Empty())
		{
			str.Empty();
			m_flx.ComboList = str;
		}
		
		if(nCol < m_nRadioColBegin || nCol > m_nRadioColEnd)
			*pCancel = TRUE;
	}
	void OnButtonClick(Control flxControl, int nRow, int nCol)
	{
		if(nRow > 0 && nRow == getRowNumberRow() && nCol == getFromStepColIndex())
		{
			RECT r1;
			GetCellRect(nRow, nCol, r1, true, true);
			s_nLeft = r1.left + RECT_WIDTH(r1)/2;
			s_nTop = r1.top + RECT_HEIGHT(r1)/2;

			GETN_BOX(trTemp)
			GETN_NUM(xFrom, _LC("X From", _CAT), m_RowNumberXFrom)
			GETN_NUM(xStep, _LC("X Step", _CAT), m_RowNumberXStep)
			if(GetNBox(trTemp, SCP_DLG_X_ROW_NUM_TITLE, SCP_DLG_X_ROW_NUM_LABEL, NULL, get_n_box_event, GetDlgSafeHwnd()))
			{
				m_RowNumberXFrom = trTemp.xFrom.dVal;
				m_RowNumberXStep = trTemp.xStep.dVal;
				updateRowNumberXFromAndStep();
				updateColWidths();
			}
		}
	}

	// nButton = MK_LBUTTON	etc
	// nShift = 1 if shift, 0 if not
	void OnBeforeMouseDown(Control cntrl, short nButton, short nShift, float X, float Y, BOOL* pCancel);
	
	void OnMouseMove(Control cntrl, short nButton, short nShift, float X, float Y)
	{
		string strTooltips;
		int nRow, nCol;
		if(!GetMouseCell(nRow, nCol))
			return;
		
		if(!IsColHeadingRow(nRow))
			return;
		
		strTooltips = getColHeadingTooltips(nCol);
		SetToolTipsText(strTooltips);
	}
	////------------------	
private:
	//
	void notifyDlgOnDesignationChanges()
	{
		PostDlgMessage(WM_USER_ON_CONTROL_CHANGE, ON_CONTROL_CHANGE_COL_DESIGNATIONS, 0);			
	}
	void updateWksPageNames(const vector<string>& vsWksPageNames, int nPageType)
	{
		m_bIsWksPages = m_bIsMatrixPage = m_bIsFunctionPlots = false;
		if(EXIST_WKS == nPageType || EXIST_EXTERN_WKS == nPageType)
			m_bIsWksPages = true;
		else if(EXIST_MATRIX == nPageType)
			m_bIsMatrixPage = true;
		else if(EXIST_FUNC_PLOT == nPageType)
			m_bIsFunctionPlots = true;
		
		// ignore Excel for now, so only one pages
		m_vsPageNames = vsWksPageNames;
		m_vsLayerNames.SetSize(m_vsPageNames.GetSize());
		for(int ii = 0; ii < m_vsLayerNames.GetSize(); ii++)
			m_vsLayerNames[ii].Empty();
		// only wks page we will remember the page name, all others we will use full dataset names so keep page name empty to indicate such
		if(!m_bIsWksPages)
		{
			for(int ii = 0; ii < m_vsPageNames.GetSize(); ii++)
				m_vsPageNames[ii].Empty();
			
			//CPY 8/5/03, we also need to make sure only one, otherwise internal code will generate multiple plots
			m_vsPageNames.SetSize(1);
			m_vsLayerNames.SetSize(1);
		}
		updateColNameColHeading();
	}
	void updateColNameColHeading()
	{
		if(m_bIsWksPages)
			SetColHeading(m_nNameCol, STR_HEADING_COL);
		else
			SetColHeading(m_nNameCol, STR_HEADING_DATASET);
	}
	void updateShowWksNames()
	{
		string str;
		for(int ii = 0; ii < m_vsPageNames.GetSize(); ii++)
		{
			if(ii > 0)
				str += " ";
			str += m_vsPageNames[ii];
		}
		m_staticCntrlPageNames.Text = str;
	}
	bool	isKeepColOrder() { return false;} // should be reading from m_dwAuxTypeInfo
	string getColDesignationName(int nColDesignation)
	{
		string str;
		char szTemp[50];
		if(get_plot_designation_str(nColDesignation, szTemp, 50))
			str = szTemp;
		return str;
	}
	int colTypeToColDesignation(int nType) // OKDATAOBJ_DESIGNATION_Y --> COLDESIG_Y
	{
		int nColDesignation = cvt_col_type_to_designation(nType);
		if(isColDesignationInList(nColDesignation))
			return nColDesignation;
		
		return COLDESIG_NONE;
	}
	bool isColDesignationInList(int nColDesignation)
	{
		for(int ii = 0; ii < m_vnColDesignations.GetSize(); ii++)
		{
			if(m_vnColDesignations[ii] == nColDesignation)
				return true;
		}
		return false;
	}

private:
	int				m_nRadioColBegin;
	int				m_nRadioColEnd;
	int				m_nNameCol;
	int				m_nLabelCol;
	int				m_nIndexCol; //CPY 8/6/03 ADD_COL_POSITION_IN_WKS
	int				m_n1stDataValCol;
	DWORD 			m_dwAuxTypeInfo;
	DWORD			m_dwAuxPlotInfo;
	string			m_strColPattern;// "XYY" type of pattern
	int				m_nYs;// from m_strColPattern, as a cache value, must calll countYs everytime m_strColPattern is changed
	int				m_nModifiers; // number of needed modifiers
	int				m_nPlotType;
	int				m_nMakePlotExVal;
	vector<uint> 	m_vnColDesignations;
	vector<string>	m_vsLayerNames; // save a local copy
	vector<string>	m_vsPageNames; // wks pages, 
	vector<string>	m_vsColNames;
	vector<string>	m_vsColLabels;
	double			m_RowNumberXFrom;
	double			m_RowNumberXStep;
	bool			m_bIsWksPages;
	bool			m_bIsMatrixPage;
	bool			m_bIsFunctionPlots;
	bool			m_bEditMode;
public:
	/// main dialog controls that we need to make use for this control
	Button			m_chkXErrBars;
	Button			m_chkPosNegErrBars;
	Button			m_chkAddRowNumX;
	Button			m_chkShowAllCols;
private:
	Control			m_staticCntrlPageNames;
	//////////////////////////////////////
	Dialog*			m_pOwner;
};




class ColListControlMenu : public Menu
{
public:
	ColListControlMenu(ColListControl* pCLcntrl, int nRow, int nCol);

	void OnMenuItem(UINT nPos)
	{
		m_nChoice = nPos;
	}
	bool GetCommand(int& nChoice);
private:
	int				m_nChoice;
};

#endif //_COL_DESIGNATIONS_H

