/*------------------------------------------------------------------------------*
 * File Name:Matlab Console	 													*
 * Creation:CPY 12/27/02														*
 * Purpose: OriginC Source C file for Dialog Buidler Matlab Console				*
 * Copyright (c) Originlab Corporation, 2002, 2003								*
 * All Rights Reserved															*
 * 																				*
 * Modification Log:															*
 *	DVT	1/2/03 QA70-3606 v7.0467 MATLAB_IMPORT_DIALOG_USE_MATLAB_CLASS			*
 *	DVT 7/1/03 QA70-4753 v7.0614 MATLAB_CONSOLE_PUT_COMPLEX						*
 *	DVT 8/14/03 MATLAB_SINGLE_INSTANCE_FOR_MENU_IMPORT							*
 *	AW 09/07/03 QA70-3859 FIX_SOME_CHARACTER_INPUT_PROBLEM						*
 *  Soapy 9/19/03 INCREASE_TEXT_LIMIT_IN_RICHEDIT								*
 *  Soapy 9/19/03 NO_DELETE_PROMPT												*
 *  Soapy 9/19/03 SUPPORT_MULTILINE_COMMAND										*
 *  Soapy 9/19/03 CHECK_MATRIX_SIZE_BEFORE_PUT_MATRIX							*
 *	Soapy 9/26/03 REMOVE_DUPLICATED_CODE										*
 *	Soapy 9/27/03 MORE_EFFECT_ON_COMMAND_WINDOW									*
 *	Soapy 10/22/03	DELETE_SPACE_BEFORE_COMMAND									*
 *  Soapy 11/4/03  	FIX_BUG_ON_BRACKET											*
 *  Soapy 11/12/03 GET_REAL_MATRIX_BY_VARIABLE_NAME								*
 *------------------------------------------------------------------------------*/
 
#include <Origin.h>
//#include <Project.h>
//#include <sys_utils.h> // basic routines implemeted through Origin C, see sys_utils.c
#include <Dialog.h>
//#include <utilities.h>
#include "ResizeDialog.h"

#include <externApps.h> // matlab class
#include "ODlg.h" // resource IDs, ODlg.dll also use this file for its res ids
#include "HelpID.h"			// dialog ID

////////////////////////////////////////////////////////////////////////////////////

#define STR_NO_MATLAB "Matlab not available"
#define STR_PROMPT	">>" //matlab prompt
#define STR_PROMPT_LEN 2
enum {CMDID_PUT_REAL, CMDID_GET_REAL, CMDID_PUT_COMPLEX, CMDID_GET_COMPLEX, CMDID_EXIT, CMDID_QUIT, CMDID_CLC, CMDID_COUNT};
// for simple list, we just keep the list consistent with CMDID_PUT etc
static StringArray s_asCmdList = {"pr", "gr", "pc", "gc",                   "exit",     "quit",     "clc"};
BOOL	MatlabImport(LPCSTR lpcszMatlabWkspaceName = NULL, HWND hParent = NULL, BOOL bSinle = FALSE);

///Soapy 9/19/03 INCREASE_TEXT_LIMIT_IN_RICHEDIT
#define TEXT_LIMITS 90000
///END INCREASE_TEXT_LIMIT_IN_RICHEDIT

enum {UNEDITABLE = 1, PARTEDITABLE, EDITABLE};  //Selection range status 

class MATLABConsoleDlg : public ResizeDialog
{
public:
	MATLABConsoleDlg() : ResizeDialog(IDD_MATLAB_CONSOLE, "ODlg")
	{
		m_pMATLABObj = NULL;
	}
	int Create(HWND hWndParent = NULL)
	{
		InitMsgMap();// will be called from internal later
		int nRet = ResizeDialog::Create(hWndParent);
		
		return nRet;
	}
	
///----------------------------------------------
///----------------- Message Map ----------------
///
protected:
	
EVENTS_BEGIN
	ON_INIT(OnInitDialog) 
	ON_SIZE(OnDlgResize)
	ON_KEY(IDC_MATLAB_RICHEDIT, OnKeyCmdWindow)
	ON_TIMER(OnTimer)
	ON_DESTROY(OnDestroy)

	ON_BN_CLICKED(IDC_MATLAB_OPEN_IMPORT, OnClickImport)
EVENTS_END
///----------------------------------------------
///	DVT	1/2/03 QA70-3606 v7.0467 MATLAB_IMPORT_DIALOG_USE_MATLAB_CLASS
// pending on Matlab::Attach(mlObj) or two BEGIN_EVENT_MAP in the same file (pending OriginC ## merge)
//	ON_BN_CLICKED(IDC_MATLAB_BTN_IMPORT, OnClickImport)

	BOOL OnInitDialog()
	{
		waitCursor junk;
		ResizeDialog::OnInitDialog();
		GetWindow().Text = "Connecting to Matlab...";
			
		m_reCntrl = GetItem(IDC_MATLAB_RICHEDIT);
		m_reCntrl.Enable = FALSE;// until ready by timer
				
		///Soapy 9/19/03 INCREASE_TEXT_LIMIT_IN_RICHEDIT
		m_reCntrl.LimitText(TEXT_LIMITS);
		///END INCREASE_TEXT_LIMIT_IN_RICHEDIT
		
		m_pMATLABObj = new Matlab(FALSE);			///DVT 
		
		m_nTimerID = SetTimer(5432,300);
		return TRUE;
	}
	
	BOOL OnTimer(UINT nTimerID)
	{
		ASSERT(nTimerID == m_nTimerID);
		
		KillTimer(m_nTimerID);
		m_nTimerID = 0;
		
		if(m_pMATLABObj && m_pMATLABObj->Attach(TRUE, FALSE, TRUE))		///	DVT MATLAB_SINGLE_INSTANCE_FOR_MENU_IMPORT using: Attach( bUseRunningInstance, bSingle, bKeepMatlabRunning )
		{
			add_str( STR_PROMPT, FALSE);
			GetWindow().Text = "Matlab Console";
			
			m_PromptPosition = 2;  		///Soapy 9/19/03 NO_DELETE_PROMPT
			
		}
		else
			GetWindow().Text = STR_NO_MATLAB;
			
		
		m_reCntrl.Enable = TRUE; // allow this no matter what
		///	DVT	1/2/03 QA70-3606 v7.0467 MATLAB_IMPORT_DIALOG_USE_MATLAB_CLASS
		//Import button added, have to set focus to the re control, Tarak will add SetFocus to Window class
		//reCntrl.SetFocus(TRUE);
		///	end MATLAB_IMPORT_DIALOG_USE_MATLAB_CLASS
	
		return TRUE;
	}
	
	BOOL OnDestroy(void)
	{
		// we keep this running so next time dialog open the workspace is still there
		//m_mlObj.Detach();
		///	DVT in case m_pMATLABObj->Attach(TRUE, FALSE, TRUE) overwritten externally:
		m_pMATLABObj->KeepMatlabRunning = TRUE;
		delete m_pMATLABObj;
		m_pMATLABObj = NULL;
		return TRUE;
	}
	
	BOOL OnClickImport(Control ctrl)
	{
		MatlabImport(NULL, GetSafeHwnd() );
		return TRUE;
	}
	
	BOOL OnDlgResize(int nType, int cx, int cy)
	{
		RECT r1;
		Control cntrlRichEdit;
		GetControlClientRect(IDC_MATLAB_RICHEDIT, r1, &cntrlRichEdit);
		r1.bottom = cy;
		r1.right = cx;		
		cntrlRichEdit.MoveWindow(&r1);
		return TRUE;
	}
	
	//return FALSE if not handled, and continue to be processed
	//return TRUE will stop default processing
	BOOL OnKeyCmdWindow(Control oCntrl, UINT msg, UINT wParam, UINT lParam)
	{	
		///Soapy 9/27/03 MORE_EFFECT_ON_COMMAND_WINDOW	
		int nStart, nEnd;
		int nSelectionStatus = checkAndReturnSelectionRange(nStart, nEnd);
		
		if( !is_sys_cmd_key(wParam) )
		{
			if( nSelectionStatus != EDITABLE)
					m_reCntrl.SetSel(-1,-1);
			
			return false;  // indicate default processing
		}
		///END MORE_EFFECT_ON_COMMAND_WINDOW	
		
		/// AW 09/07/03 QA70-3859 FIX_SOME_CHARACTER_INPUT_PROBLEM
		if(msg == WM_CHAR)
			return FALSE;
		/// END FIX_SOME_CHARACTER_INPUT_PROBLEM
		
		if(msg != WM_KEYDOWN)
			return TRUE;// we need to eat all other message related to Enter key
		
		switch(wParam)
		{
		case VK_RETURN:
			///Soapy 9/27/03 MORE_EFFECT_ON_COMMAND_WINDOW
			if(nSelectionStatus == UNEDITABLE) // Support selection execution
			{
				string strAddText = m_reCntrl.GetSelText();
				strAddText.Replace(">>","");
				m_reCntrl.SetSel(-1,-1);
				m_reCntrl.ReplaceSel(strAddText);
			}
			///END MORE_EFFECT_ON_COMMAND_WINDOW
			return OnEnterKey(nStart, nEnd);
			break;
			
		///Soapy 9/27/03 MORE_EFFECT_ON_COMMAND_WINDOW
		/*
		case VK_LEFT: case VK_BACK:
			// should not go left on prompt
			if(nStart - nLineStart <= strlen(STR_PROMPT))
				break;
			
			return FALSE;
		*/
		case VK_LEFT: case VK_RIGHT:
			return false;
			break;
			
		case VK_BACK:
			if(nSelectionStatus != EDITABLE)
			{
				m_reCntrl.SetSel(-1,-1);				
			}
			else
			{
				if((nStart == nEnd) &&(nStart>m_PromptPosition))
					m_reCntrl.SetSel(nStart-1,nEnd);
				m_reCntrl.ReplaceSel("");				
			}
			return true;
			break;
			
		case VK_DELETE:
			if(nSelectionStatus != EDITABLE)
			{
				m_reCntrl.SetSel(-1,-1);
			}
			return false;
			break;
		}
		///END MORE_EFFECT_ON_COMMAND_WINDOW
			
		return TRUE; // no more default processing	
	}
	
	BOOL OnEnterKey(int nStart, int nEnd)
	{
		///Soapy 9/19/03 SUPPORT_MULTILINE_COMMAND
		/*
		if(nEnd > nStart)		
		{
			// selected range, not supported for now,
			MessageBeep(0);
			return TRUE;// no more default processing
		}
		
		string strCmd = ed.GetLine();
		strCmd.TrimRight();
		string strPrompt = strCmd.Left(STR_PROMPT_LEN);
		
		if(strPrompt.Compare(STR_PROMPT) != 0)
			return TRUE;
				
		add_str("\r\n", FALSE);
		strCmd = strCmd.Mid(STR_PROMPT_LEN);
		*/
		m_reCntrl.SetSel(m_PromptPosition,-1);		
		string strCmd = m_reCntrl.GetSelText();
		m_reCntrl.SetSel(-1,-1);
		
	 	if(!isBracketMatch(strCmd,'[',']') ||!isBracketMatch(strCmd,'{','}' ))
			return false;
				
		add_str("\r\n", FALSE);		
		///END SUPPORT_MULTILINE_COMMAND
	
		string strOutput = STR_NO_MATLAB;
		if(m_pMATLABObj && !is_Origin_command(strCmd, strOutput) && *m_pMATLABObj)
		{
			strOutput = m_pMATLABObj->Execute(strCmd);
		}
		
		add_str(strOutput);	
		
		//we need put Enter in ourself as we were switching to anther control above
		add_str(STR_PROMPT, FALSE);
		
		///Soapy 9/19/03 NO_DELETE_PROMPT
		m_reCntrl.SetSel(-1,-1);
		m_reCntrl.GetSel(nStart,m_PromptPosition); //Record the position of latest prompt
		///END Soapy 9/19/03 NO_DELETE_PROMPT
		
		return TRUE;
	}
	
private:
	void add_str(LPCSTR lpcszText, BOOL bAddNewLine=TRUE)
	{
		m_reCntrl.SetSel(-1,-1);
		m_reCntrl.ReplaceSel(lpcszText);
		m_reCntrl.SetSel(-1,-1);
		if(bAddNewLine)
		{
			m_reCntrl.ReplaceSel("\r\n");
			m_reCntrl.SetSel(-1,-1);
		}
	}

	bool is_sys_cmd_key(UINT nKey)
	{
		if(nKey == VK_RETURN || nKey == VK_LEFT || nKey == VK_BACK || nKey == VK_UP || 
			nKey == VK_DOWN || nKey == VK_DELETE || nKey == VK_RIGHT)  ///Soapy 9/19/03 NO_DELETE_PROMPT
			return true;
		
		return false;
	}
	
	bool is_Origin_command(LPCSTR lpcszCmd, string& strResult)
	{
		string strCmdstr = lpcszCmd;
		
		strCmdstr.TrimLeft();	//Soapy 10/22/03 DELETE_SPACE_BEFORE_COMMAND
		
		//----- CPY 1/10/03
		// GetTokens has a bug, if str has ', will generate runtime error
		//int nArgs = strCmdstr.GetTokens(strCmdArgs);
		//if(nArgs < 1)
		//	return false;
		//string strCmd = strCmdArgs[0];
		int nSpace = strCmdstr.Find(' ');
		if(strCmdstr.IsEmpty() || nSpace == 0)
			return false;
	
		string strCmd = strCmdstr;
		if(nSpace > 0)
			strCmd = strCmdstr.Left(nSpace);
		//-----
	
		for(int nCmdID = 0; nCmdID < CMDID_COUNT; nCmdID++)
		{
			if(strCmd.CompareNoCase(s_asCmdList[nCmdID]) == 0)
			{
				//----- CPY 1/10/03
				StringArray	strCmdArgs;
				int nArgs = strCmdstr.GetTokens(strCmdArgs);
				if(nArgs < 1)
					return false;
				//-----
				do_Origin_commad(nCmdID, strCmdArgs, strResult);
				return true;
			}
		}
		return false;
	}
	
	// reutrn true if matrix does not exist and has to be created
	bool get_Origin_matrix(string strMatName, MatrixLayer& mLayer)
	{
		MatrixLayer mltemp(strMatName);
		if(!mltemp)
		{
			mltemp.Create();
			mltemp.GetPage().Rename(strMatName);
			mLayer = mltemp;
			return true;
		}
		mLayer = mltemp;
		return false;
	}
		
	void do_Origin_commad(int nCmdID, StringArray& asArgs, string& strResult)
	{
		uint nArgSize = asArgs.GetSize();
		if(nArgSize < 2 && nCmdID <= CMDID_GET_COMPLEX) // no argument
		{
			strResult = "Usage:" + asArgs[0];
			strResult += " matName1 matName2 ...";
			return;
		}
		///	DVT 7/1/03 QA70-4753 v7.0614 MATLAB_CONSOLE_PUT_COMPLEX
		if(nArgSize < 3 && nCmdID == CMDID_PUT_COMPLEX) // not enough arguments
		{
			strResult = "Usage:" + asArgs[0];
			strResult += " matNameReal matNameImag [matlNameResult (= matNameReal)]";
			return;
		}
		///	end MATLAB_CONSOLE_PUT_COMPLEX
	
		if(NULL == m_pMATLABObj)
		{
			strResult = STR_NO_MATLAB;
			return;
		}
			
		strResult.Empty();
		int ii;
		bool bSuccess;
		
		switch(nCmdID)
		{
		case CMDID_PUT_REAL:
			for(ii = 1; ii < nArgSize; ii++)
			{
				MatrixLayer mlay(asArgs[ii]);
				if(!mlay)
				{
					strResult = "???\r\n " + asArgs[ii];
					strResult += " is not an Origin matrix.\r\n";
					return;
				}
				Matrix<double> mm(mlay);
				m_pMATLABObj->PutMatrix(mm.GetName(), &mm);		
			}
			break;
		///	DVT 7/1/03 QA70-4753 v7.0614 MATLAB_CONSOLE_PUT_COMPLEX
		case CMDID_PUT_COMPLEX:
		{
			MatrixLayer mlayReal(asArgs[1]);
			if(!mlayReal)
			{
				strResult = "???\r\n " + asArgs[1];
				strResult += " is not an Origin matrix.\r\n";
				return;
			}
			Matrix<double> mmR(mlayReal);
			
			MatrixLayer mlayImag(asArgs[2]);
			if(!mlayImag)
			{
				strResult = "???\r\n " + asArgs[2];
				strResult += " is not an Origin matrix.\r\n";
				return;
			}
			Matrix<double> mmI(mlayImag);
			
			///Soapy 9/19/03 CHECK_MATRIX_SIZE_BEFORE_PUT_MATRIX
			if(mmI.GetNumRows()!=mmR.GetNumRows() || mmI.GetNumCols()!=mmR.GetNumCols())
			{
				strResult = "\r\nInconsistent Matrix Size.\r\n ";
				return;
			}
			///END CHECK_MATRIX_SIZE_BEFORE_PUT_MATRIX
			
			m_pMATLABObj->PutMatrix(  nArgSize >= 4 ? asArgs[3] : mmR.GetName(), &mmR, &mmI );
			break;
		}
		///	end MATLAB_CONSOLE_PUT_COMPLEX
		case CMDID_GET_REAL:
			for(ii = 1; ii < nArgSize; ii++)
			{
				///Soapy 9/26/03 REMOVE_DUPLICATED_CODE
				/*
				bool bNew=false;
				MatrixLayer mlay(asArgs[ii]);
				if(!mlay)
				{
					mlay.Create();
					mlay.GetPage().Rename(asArgs[ii]);
					bNew = true;
				}*/
				MatrixLayer mlay;
				bool bNew = get_Origin_matrix(asArgs[ii], mlay);
				///END REMOVE_DUPLICATED_CODE
				
				Matrix<double> mm(mlay);
				///Soapy 11/12/03 GET_REAL_MATRIX_BY_VARIABLE_NAME	
				//bSuccess = m_pMATLABObj->GetMatrix(mm.GetName(), &mm);
				bSuccess = m_pMATLABObj->GetMatrix(asArgs[ii], &mm);
				///END GET_REAL_MATRIX_BY_VARIABLE_NAME
				if(!bSuccess )
				{
					if(bNew)
						mlay.Destroy();
					strResult += asArgs[ii] + " ";
				}
			}
			if(!strResult.IsEmpty())
				strResult += ": matrices not found in Matlab!\r\n";
				
			break;
		case CMDID_GET_COMPLEX:
			// may need to see how to share code with CMDID_GET_REAL
			for(ii = 1; ii < nArgSize; ii++)
			{
				MatrixLayer mlay_r, mlay_i;
				bool bNewR= get_Origin_matrix(asArgs[ii]+"r", mlay_r);
				bool bNewI= get_Origin_matrix(asArgs[ii]+"i", mlay_i);
				
				Matrix<double> mr(mlay_r), mi(mlay_i);
				bSuccess = m_pMATLABObj->GetMatrix(asArgs[ii], &mr, &mi);
				if(!bSuccess)
				{
					if(bNewR)
						mlay_r.Destroy();
					if(bNewI)
						mlay_i.Destroy();
					
					strResult += asArgs[ii] + " ";
				}
			}
			if(!strResult.IsEmpty())
				strResult += ": matrices not found in Matlab!";
				
			break;
			
		case CMDID_EXIT:
		case CMDID_QUIT:
			m_pMATLABObj->Detach();
			GetWindow().SendMessage(WM_CLOSE);
			break;
		case CMDID_CLC: // clear screen display
			m_reCntrl.Text="";
			//add_str_to_richEdit(cCmdWindow, STR_PROMPT, FALSE);
			break;
		}
		return;
	}
	
	int checkAndReturnSelectionRange(int& nStart, int& nEnd)
	{
		m_reCntrl.GetSel(nStart, nEnd);
		
		if(nEnd<m_PromptPosition)
			return UNEDITABLE ;
		else if(nStart < m_PromptPosition)
			return PARTEDITABLE;
		else
			return EDITABLE;
	}
	
	// Check if '[',']' and '{','}' match
	bool isBracketMatch(string str, char char1, char char2)
	{
		int nLength = str.GetLength();
		int nL, nR;
		
		str.Remove(char1);
		nL = nLength - str.GetLength();
		str.Remove(char2);
		nR = nLength - nL - str.GetLength();
		
		return (nR>=nL);  ///Soapy 11/4/03  	FIX_BUG_ON_BRACKET
	}
		
			
private:
	int 		m_nTimerID;
	RichEdit	m_reCntrl;
	Matlab* 	m_pMATLABObj;
	LONG 		m_PromptPosition; ///Soapy 9/19/03 NO_DELETE_PROMPT
	
};

static MATLABConsoleDlg MyDlg;
////////////////////////////////////////////////////////////////////////////////////
// opens the MatlabConsole
BOOL MatlabConsole(BOOL bClose=FALSE)
{
	if(MyDlg.GetWindow())
	{
		if(!bClose)
		{
			out_str("Matlab Console already open");
			return FALSE; //already open
		}
		//we will need to close it
		return TRUE;
	}
	
	return MyDlg.Create();
}
