//===========================================================================
// Custom Solution Wizard - Visual C++ Shell - Sample Functions
//
//---------------------------------------------------------------------------
//	These files were created with the MFC AppWizard to illustrate how the
//	sample NSLearningNetwork and NSRecallNetwork classes can be used to
//  interact with a Custom Solution Wizard generated DLL for training
//  or recall from a Visual C++ program.
//---------------------------------------------------------------------------
//	This program will compile and run without any further modification.
//	It uses the training data taken from your original breadboard. This data
//  was saved to two files: InputData.txt and DesiredData.txt during the DLL
//  generation.
//===========================================================================

//===========================================================================
// Includes
//===========================================================================
#include "stdafx.h"
#include <string.h>
#include "VCPPShell.h"
#include "NSNetwork.h"
#include "Globals.h"

//===========================================================================
// Debug handling generated by MFC AppWizard.
//===========================================================================
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//===========================================================================
//===========================================================================
// CLASS: CVCPPShellApp
//===========================================================================
//===========================================================================

//===========================================================================
// Global declaration of application generated by MFC AppWizard.
//===========================================================================
CVCPPShellApp theApp;

//===========================================================================
// CLASS: CVCPPShellApp			MESSAGE MAP
//---------------------------------------------------------------------------
// Note: This mapping was built by the MFC AppWizard.
//===========================================================================
BEGIN_MESSAGE_MAP(CVCPPShellApp, CWinApp)
	//{{AFX_MSG_MAP(CVCPPShellApp)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG
	ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

//===========================================================================
// CLASS: CVCPPShellApp			FUNCTION: <constructor>()
//---------------------------------------------------------------------------
// Note: This function was built by the MFC AppWizard.
//===========================================================================
CVCPPShellApp::CVCPPShellApp()
{
}

//===========================================================================
// CLASS: CVCPPShellApp			FUNCTION: InitInstance()
//---------------------------------------------------------------------------
// Note: This function was built by the MFC AppWizard.
//===========================================================================
BOOL CVCPPShellApp::InitInstance()
{
	// Standard initialization
	// If you are not using these features and wish to reduce the size
	//  of your final executable, you should remove from the following
	//  the specific initialization routines you do not need.

	CVCPPShellDlg dlg;
	m_pMainWnd = &dlg;
	int nResponse = dlg.DoModal();
	if (nResponse == IDOK)
	{
		//  Place code here to handle when the dialog is
		//  dismissed with OK
	}
	else if (nResponse == IDCANCEL)
	{
		//  Place code here to handle when the dialog is
		//  dismissed with Cancel
	}

	// Since the dialog has been closed, return FALSE so that we exit the
	//  application, rather than start the application's message pump.
	return FALSE;
}

//===========================================================================
//===========================================================================
// CLASS: CVCPPShellDlg
//===========================================================================
//===========================================================================

//===========================================================================
// CLASS: CVCPPShellDlg			FUNCTION: <constructor>(CWnd*)
//---------------------------------------------------------------------------
// Note: This function was built by the MFC AppWizard.
//===========================================================================
CVCPPShellDlg::CVCPPShellDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CVCPPShellDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CVCPPShellDlg)

	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

}

//===========================================================================
// CLASS: CVCPPShellDlg			FUNCTION: DoDataExchange(CDataExchange*)
//---------------------------------------------------------------------------
// Note: This function was built by the MFC AppWizard.
//===========================================================================
void CVCPPShellDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CVCPPShellDlg)
	DDX_Text(pDX, IDC_TRAIN_NETWORK_REPORT, m_TrainNetwork_Report);
	//}}AFX_DATA_MAP
}


//===========================================================================
// CLASS: CVCPPShellDlg			FUNCTION: FillArrayFromFile(LPCTSTR, int, int)
//===========================================================================
double *CVCPPShellDlg::FillArrayFromFile(LPCTSTR fName, int numRows, int numCols)
{
	CStdioFile fl;
	LPTSTR tmpBuffer = new char [250];

	char *token;				//pointer to string tokens to extract from file strings
	char *delim = " ,\t\n";		//delimiter to use for string tokenizer

	//create array to hold double values extracted from file
	long numCells = numRows * numCols;
	double *retArray = (double *)calloc (numCells, sizeof (double));
	int curIndx = 0;

	if (fl.Open(fName, CFile::modeRead))
	{

		//read file until end is reached
		while (((fl.ReadString(tmpBuffer, 250)) != NULL) && ((curIndx < numCells)))
		{
			//tokenize string to extract double values as strings
			token = strtok (tmpBuffer, delim);

			while (token != NULL)
			{
				//convert token to double and store in array
				retArray[curIndx] = (double) atof (token);
				curIndx++;

				//tokenize string to extract double values as strings
				token = strtok (NULL, delim);

			} //end token while


		} //end fl.Open while

		fl.Close();
	} //end if

	free (tmpBuffer);

	return retArray;
} //end FillArrayFromFile


//===========================================================================
// CLASS: CVCPPShellDlg			MESSAGE MAP
//---------------------------------------------------------------------------
// Note: This mapping was built by the MFC AppWizard.
//===========================================================================
BEGIN_MESSAGE_MAP(CVCPPShellDlg, CDialog)
	//{{AFX_MSG_MAP(CVCPPShellDlg)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_GET_NETWORK_OUTPUT, OnGetResponse)
	ON_BN_CLICKED(IDC_TRAIN_NETWORK, OnTrainNetwork)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


//===========================================================================
// CLASS: CVCPPShellDlg			FUNCTION: OnInitDialog()
//---------------------------------------------------------------------------
// Note: This function was built by the MFC AppWizard.
//===========================================================================
BOOL CVCPPShellDlg::OnInitDialog()
{

	CDialog::OnInitDialog();

	// Set the icon for this dialog.  The framework does this automatically
	// when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	//dispaly initial message in Get Network Output Report edit box
	CEdit *pGetNetworkOutputReportEditBox = (CEdit *) GetDlgItem (IDC_GET_NETWORK_OUTPUT_REPORT);
	pGetNetworkOutputReportEditBox->SetWindowText(_T("Not tested."));

	//disables training if the DLL only supports a recall network
	if (RECALL_ONLY_NETWORK)
	{
			//disable Train Network button
			CWnd *pTrainNetworkButton = (CWnd *) GetDlgItem (IDC_TRAIN_NETWORK);
			pTrainNetworkButton->EnableWindow(FALSE);

			//disable Reset Network Before Training check box
			CButton *pResetNetworkCheckBox = (CButton *) GetDlgItem (IDC_RESET_NETWORK);
			pResetNetworkCheckBox->EnableWindow(FALSE);

			//dispaly initial message in Train Network Report edit box
			CEdit *pTrainNetworkReportEditBox = (CEdit *) GetDlgItem (IDC_TRAIN_NETWORK_REPORT);
			pTrainNetworkReportEditBox->SetWindowText(_T("Only Recall DLL Supported."));

			m_TrainNetwork_Report = _T("Recall Network Only.");
	}
	else
	{
			//dispaly initial message in Train Network Report edit box
			CEdit *pTrainNetworkReportEditBox = (CEdit *) GetDlgItem (IDC_TRAIN_NETWORK_REPORT);
			pTrainNetworkReportEditBox->SetWindowText(_T("Not tested."));

			m_TrainNetwork_Report = _T("Not tested.");
	}

	return TRUE;  // return TRUE  unless you set the focus to a control
}

//===========================================================================
// CLASS: CVCPPShellDlg			FUNCTION: OnPaint()
//---------------------------------------------------------------------------
// Note: This function was built by the MFC AppWizard.
//---------------------------------------------------------------------------
// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.
//===========================================================================
void CVCPPShellDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

//===========================================================================
// CLASS: CVCPPShellDlg			FUNCTION: OnQueryDragIcon()
//---------------------------------------------------------------------------
// Note: This function was built by the MFC AppWizard.
//---------------------------------------------------------------------------
// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
//===========================================================================
HCURSOR CVCPPShellDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

//===========================================================================
// CLASS: CVCPPShellDlg			FUNCTION: OnGetResponse()
//---------------------------------------------------------------------------
//  This function illustrates how the DLL generated by the Custom Solution
//  Wizard can be used for getting the network response (output).
//  Step by step instructions are given on how to use the generated DLL
//  for this purpose.  Read through comments carefully performing all of the
//  REQUIRED ACTIONs as you get to them.
//===========================================================================
void CVCPPShellDlg::OnGetResponse()
{
	//---------------------------------------------------------------------------
	// Step 1: Create an instance of NSRecallNetwork.
	//---------------------------------------------------------------------------
    // IMPLEMENTATION NOTES: The DLL_PATH_NAME is defined in Globals.h
	//---------------------------------------------------------------------------
	// Following this call, IsInitialized() should return true.
	// If IsInitialized() is false, check to make sure the path to the DLL is correct.
	//---------------------------------------------------------------------------
    // You could create an NSLearningNetwork object instead, if you have the
    // Developers level of the Custom Solution Wizard and used it along with a
	// NeuroSolutions breadboard capable of learning to generate the DLL.
	// Both NSRecallNetwork and NSLearningNetwork support recall operations.
	//---------------------------------------------------------------------------
	NSRecallNetwork nn(DLL_PATH_NAME);

	if (nn.IsInitialized())
	{
		//---------------------------------------------------------------------------
		// Step 2: Get the size of the DLL (number of inputs and outputs)
		//---------------------------------------------------------------------------
		int inputs;
		int outputs;
		nn.GetInputOutputInfo(inputs,outputs);

		//---------------------------------------------------------------------------
		// Step 3: Load the initial network weights.
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES: The BEST_WEIGHTS_PATH_NAME is defined in Globals.h.
		// The initial best weights file is an exact copy of the weights file that was
		// saved when the DLL was generated. However, the best weights file will change
		// with each run of the TrainNetwork function if the network is reset before
		// training.
		//---------------------------------------------------------------------------
		nn.LoadWeights(BEST_WEIGHTS_PATH_NAME);

		//---------------------------------------------------------------------------
		// Step 4: Define the input data.
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES: The following loads the training input data from your initial
		// network configuration into the array inputData.
		// Note that the number of inputs in the input data must match the number of inputs
		// expected by the generated DLL.  Also, there can be any number of exemplars in the
		// input data as long as the number of exemplars in the input data matches the number
		// of exemplars in the desired data. In this case, NUMBER_OF_EXEMPLARS is defined in
		// Globals.h.
		//---------------------------------------------------------------------------
		double *inputData = FillArrayFromFile(INPUT_FILE_PATH_NAME, inputs, NUMBER_OF_EXEMPLARS);

		//---------------------------------------------------------------------------
		// Step 5: Declare the storage for the response.
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES : The dimensions of the outputData array have been set
		// to match the expected size of the output data.
		//---------------------------------------------------------------------------
		// Note: The first dimension of the outputData array is sized according
		// to the number of outputs in the DLL. The second dimension of the outputData
		// array is sized according to the number of exemplars in your data set as
		// defined in Globals.h.
		//---------------------------------------------------------------------------
		double *outputData = (double *) calloc (outputs * NUMBER_OF_EXEMPLARS, sizeof (double));

		//---------------------------------------------------------------------------
		// Step 6: Get the network response to the input data.
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES: NUMBER_OF_EXEMPLARS is defined in Globals.h.
		//---------------------------------------------------------------------------
		nn.GetResponse(NUMBER_OF_EXEMPLARS, inputData, outputData);

		//---------------------------------------------------------------------------
		// Step 7: Verify the results. (Optional)
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES: Outputs the results to the Get Network Output Report edit box
		//---------------------------------------------------------------------------
		// Prepare edit box for output -> clear its contents.
		CEdit *pGetNetworkOutputReportEditBox = (CEdit *) GetDlgItem (IDC_GET_NETWORK_OUTPUT_REPORT);
		pGetNetworkOutputReportEditBox->SetWindowText(_T(""));

		CString outStr = "Exemplar #\t";

		// Add labels to top of edit box
		for (int i = 1; i <= outputs; i++)
		{
			CString iAsStr;
			iAsStr.Format ("%d    \t", i);
			outStr += "Output" + iAsStr;
		}
		outStr += "\r\n";

		// Show outputs for all up to 1000 exemplars
		int maxExemplars = (NUMBER_OF_EXEMPLARS > 1000)?1000:NUMBER_OF_EXEMPLARS;
		for (int j = 0; j < maxExemplars; j++)
		{
			CString jAsStr;
			jAsStr.Format("%d\t\t", j + 1);
			outStr += jAsStr;

			for (int k = 0; k < outputs; k++)
			{
				CString resultAsString;
				resultAsString.Format("%9f\t", outputData[(j * outputs) + k]);
				outStr += resultAsString;
			} //end for j

			outStr += "\r\n";
		} //end for i


		pGetNetworkOutputReportEditBox->SetWindowText(_T(outStr));

		//---------------------------------------------------------------------------
		// Perform some memory clean-up
		//---------------------------------------------------------------------------
		free (inputData);
		free (outputData);

	}
	//---------------------------------------------------------------------------
	// If initialization failed, report any errors here.
	//---------------------------------------------------------------------------
	else
	{
		//prepare edit box for output -> reset its contents
		CEdit *pGetNetworkOutputReportEditBox = (CEdit *) GetDlgItem (IDC_GET_NETWORK_OUTPUT_REPORT);
		pGetNetworkOutputReportEditBox->SetWindowText(_T("DLL Load Failed."));
	}

	//---------------------------------------------------------------------------
	// Update the dialog.
	//---------------------------------------------------------------------------
	UpdateData(FALSE);
}

//===========================================================================
// CLASS: CVCPPShellDlg			FUNCTION: OnTrainNetwork()
//---------------------------------------------------------------------------
//  The function trains a learning network DLL and saves the best weights
//	to a specified file for later use.
//  Step by step instructions are given on how to use the generated DLL
//  for this purpose.  Read through comments carefully. The IMPLEMENTATION NOTES
//  highlight parameters that you may wish to change in the future.
//---------------------------------------------------------------------------
//	NOTE: This subroutine only applys to users of the Developers level of the
//	Custom Solution Wizard.  The Developers level allows the user to generate
//	learning DLLs, whereas all other levels can only generate recall DLLs.
//===========================================================================
void CVCPPShellDlg::OnTrainNetwork()
{

	//---------------------------------------------------------------------------
	// Update Train Network Report edit box to indicate that the network is training.
	//---------------------------------------------------------------------------
	CEdit *pTrainNetworkReportEditBox = (CEdit *) GetDlgItem (IDC_TRAIN_NETWORK_REPORT);
	pTrainNetworkReportEditBox->SetSel(0, -1, FALSE);
	pTrainNetworkReportEditBox->ReplaceSel("Training...", FALSE);

	//---------------------------------------------------------------------------
	// Step 1: Create an instance of NSLearningNetwork.
	//---------------------------------------------------------------------------
    // IMPLEMENTATION NOTES: The path of the DLL is defined in Globals.h.
	//---------------------------------------------------------------------------
	// Following this call, IsInitialized() should return true.
	// If IsInitialized() is false, one of the following errors occured:
	//		* The path to the DLL is incorrect.  IsLoaded() will be false.
	//		* The path points a recall DLL.  IsLoaded() will be true.
	//---------------------------------------------------------------------------
	// Note: This step will fail if you are not using the Developers level of
	// the Custom Solution Wizard or if you generated the DLL using a recall
	// NeuroSolutions breadboard.
	//---------------------------------------------------------------------------
	NSLearningNetwork nn(DLL_PATH_NAME);

	if (nn.IsInitialized())
	{
		//---------------------------------------------------------------------------
		// Step 2: Get the size of the DLL (number of inputs and outputs)
		//---------------------------------------------------------------------------
		int inputs;
		int outputs;
		nn.GetInputOutputInfo(inputs,outputs);

		//---------------------------------------------------------------------------
		// Step 3: Load the initial network weights. (Optional)
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES: The WEIGHTS_PATH_NAME has been defined in Globals.h to
		// reflect that of the weights file that was saved when the DLL was generated.
		//---------------------------------------------------------------------------
		// Note: This step is not necessary. If this step is not performed, the
		// network will simply start with random initial weights.
		//---------------------------------------------------------------------------
		nn.LoadWeights(WEIGHTS_PATH_NAME);

		//---------------------------------------------------------------------------
		// Resets the network (randomizes the network weights, etc.) if the user has so
		// indicated by checking the "Reset Network Before Training" check box
		//---------------------------------------------------------------------------
		CButton *pResetNetworkCheckBox = (CButton *) GetDlgItem (IDC_RESET_NETWORK);
		if (pResetNetworkCheckBox->GetCheck() == 1)
			nn.ResetNetwork();

		//---------------------------------------------------------------------------
		// Step 4: Define the input data.
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES: The following loads the training input data from your initial
		// network configuration into the array inputData.
		// Note that the number of inputs in the input data must match the number of inputs
		// expected by the generated DLL.  Also, there can be any number of exemplars in the
		// input data as long as the number of exemplars in the input data matches the number
		// of exemplars in the desired data. In this case, NUMBER_OF_EXEMPLARS is defined in
		// Globals.h.
		//---------------------------------------------------------------------------
		double *inputData = FillArrayFromFile(INPUT_FILE_PATH_NAME, inputs, NUMBER_OF_EXEMPLARS);

		//---------------------------------------------------------------------------
		// Step 5: Define the desired data.
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES: The following loads the training desired data from your initial
		// network configuration into the array desiredData.
		// Note that the number of outputs in the desired data must match the number of outputs
		// expected by the generated DLL.  Also, there can be any number of exemplars in the
		// desired data as long as the number of exemplars in the desired data matches the number
		// of exemplars in the input data. In this case, NUMBER_OF_EXEMPLARS is defined in
		// Globals.h.
		//---------------------------------------------------------------------------
		double *desiredData = FillArrayFromFile(DESIRED_FILE_PATH_NAME, outputs, NUMBER_OF_EXEMPLARS);

		//---------------------------------------------------------------------------
		// Step 6: Enable the automatic saving of the best weights. (Optional)
		//---------------------------------------------------------------------------
		// Note: This step is not necessary.  The default value of this property is
		// always False.  Setting it to True will cause the best network weights to be
		// saved during training.  If this property is set to True, be sure to call
		// SetBestWeightsPathName() to set a valid path for the best weights (See Step 7).
		//---------------------------------------------------------------------------
		nn.SetSaveBestWeightsEnabled(true);

		//---------------------------------------------------------------------------
		// Step 7: Set the path for saving the best weights. (Optional)
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES : The BEST_WEIGHTS_PATH_NAME is defined in Globals.h.
		//---------------------------------------------------------------------------
		// Note: The specified path will be used for saving the best weights during
		// training.  This SetBestWeightsPathName() function must be set to a
		// valid path if SetSaveBestWeightsEnabled() has been set to true.  Otherwise,
		// setting this property is optional.
		//---------------------------------------------------------------------------
		nn.SetBestWeightsPathName(BEST_WEIGHTS_PATH_NAME);

		//---------------------------------------------------------------------------
		// Step 8: Train the network.
		//---------------------------------------------------------------------------
		// IMPLEMENTATION NOTES : The NUMBER_OF_EPOCHS and NUMBER_OF_EXEMPLARS are defined
		// in Globals.h and have been determined from the initial network configuration.
		//---------------------------------------------------------------------------
		// Note: Two versions of the Train() function are provided.  This version does
		// not include cross validation parameters.  If cross validation is desired,
		// use the other version of Train() and call SetCrossValidationEnabled(true).
		//---------------------------------------------------------------------------
		nn.Train(NUMBER_OF_EPOCHS, NUMBER_OF_EXEMPLARS, inputData, desiredData);

		//---------------------------------------------------------------------------
		// Step 9: Report best cost. (Optional)
		//---------------------------------------------------------------------------
		// This dialog-based application reports the best cost to the user.
		//---------------------------------------------------------------------------
		// Note: This step is not necessary, it is provided only to show how the best
		// cost can be obtained.
		//---------------------------------------------------------------------------
		double bestCost = nn.GetBestCost();
		m_TrainNetwork_Report.Format("Best Cost = %g",bestCost);

		//---------------------------------------------------------------------------
		// Perform some memory clean-up
		//---------------------------------------------------------------------------
		free (inputData);
		free (desiredData);
	}

	//---------------------------------------------------------------------------
	// If initialization failed, report any errors here.
	//---------------------------------------------------------------------------
	else
	{
		//---------------------------------------------------------------------------
		// If the DLL was loaded, this is probably a recall-only network.
		//---------------------------------------------------------------------------
		if (nn.IsLoaded())
		{
			m_TrainNetwork_Report = "DLL recall only.";
		}
		//---------------------------------------------------------------------------
		// Else, the DLL was not loaded, the path is probably incorrect.
		//---------------------------------------------------------------------------
		else
		{
			m_TrainNetwork_Report = "DLL load failed.";
		}
	}

	//---------------------------------------------------------------------------
	// Update the dialog.
	//---------------------------------------------------------------------------
	UpdateData(FALSE);
}

