// =============================================================================
// LIBRARY:  AwCcConfig
//
// FUNCTION: Simplify read and write access to a DEV_xxx.INI file in any BNCS
//           V2, V3, or V4.5 system.
//
// COMMENTS: The standard mechanism to call this library is by use of the full
// qualified function name. For example AwCcConfig::getDevPath(). Based on
// ccConfig from ATOS.
//

#pragma warning (disable :4786)
#include "stdafx.h"
#include "awCcConfig.h"
#include "include\cc.h"
#include "include\bncs.h"

// Instance the static data needed for operations
bool AwCcConfig::pathsSet = false;			// Need to set this value before make use of the functions 
bool AwCcConfig::sysTypeKnown = false;		// Set true when we have worked out the type of BNCS host
bool AwCcConfig::sysTypeV3 = false;			// Set true for a Version 3 BNCS host
bool AwCcConfig::sysTypeV4 = false;			// Set true for a Version 4 V4.5) host
char AwCcConfig::myCCRoot[MAX_PATH] = "";	// Holds the value of the BNCS V4.5 Root path
char AwCcConfig::myCCSystem[MAX_PATH] = "";	// Holds the value of the BNCS V4.5 System Environment Variable	
char AwCcConfig::myV3Path[MAX_PATH] = "";	// Used in testing for a V3 environment
char AwCcConfig::myWinDir[MAX_PATH] = "";

// Do not really need constructor or destructor as the class iis not instanced.
AwCcConfig::AwCcConfig() {}

AwCcConfig::~AwCcConfig() {}

////////////////////////////////////////////////////////////////////////////////
// FUNCTION: string AwCcConfig::getDevPath(void)
//
// PURPOSE:  Return path to the configuration directory of the current system
//           "Current System" is defined as V4 if the environment variables
//           CC_ROOT and CC_SYSTEM are defined, V3 if a ConfigPath is set in
//           "c:\bncs_config.ini", V2 otherwise (which is the Windows directory). 
//
string AwCcConfig::getDevPath(void)
{
	// First test for a V4 system
	if (isV4Based())		// This function will set up the paths data if not previously known.
	{
		string pth1 = myCCRoot;
		string pth2 = myCCSystem;
		return (pth1 + "\\" + pth2 + "\\config\\System");
	}

	// Not V4? - try V3
	if (isV3Based())
	{
		// The testing of the system type results in the setting of various paths. We can find the
		// config path by use of the GetPrivateProfileString API call
		char mpp[MAX_PATH];

		GetPrivateProfileString("Config", "ConfigPath", "", mpp, MAX_PATH, "C:\\bncs_config.ini");
		string mppcs = mpp;
		return mppcs;
	}

	// Default is version 2 system
	string p2 = myWinDir;
	return p2;	
}


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: string AwCcConfig::getDevPath(int device)
//
// PURPOSE:  Return filename formatted for the given device number in the
//			 current environment.
//
// EXAMPLE:  string fname = getDevPath(123);  // for V2 "fname" typically
//                                            // contains c:\windows\dev_123.ini
//
string AwCcConfig::getDevPath(int device)
{
	char filestem[12];
	int devId = device;

	string pathName = AwCcConfig::getDevPath();		// Get the path element of the filename

	if ((devId < 1) || (devId > 999)) devId = 0;	// Force a value that can fit 3 digits
	sprintf_s(filestem, 12, "dev_%03d.ini", devId);	// Make a 3 digit number

	string devFile = filestem;						// Convert into a c++ string

	return pathName + "\\" + devFile;						
}


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: string AwCcConfig::getDevPath(const string & filename)
//
// PURPOSE:  Return path + filename formatted for the provided filename in the 
//           current host environment.
//
// RETURNS:  For V2, input of "myfile.ini" returns "c:\windows\myFile.ini"
//
// COMMENTS: There is no need to check the provided filename as this library
// is to support access to config files, hence the path is defined by the
// host environment.
//
string AwCcConfig::getDevPath(const string & filename)
{
	string pathName = AwCcConfig::getDevPath();		// Get the path element of the filename

	return pathName + "\\" + filename;
}


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: int AwCcConfig::getInt(int device, 
//                                  const string & section,
//                                  const string & key,
//                                  int def,
//                                  bool createOnDefault)
//
// PURPOSE:  Return the integer value for the named 'key' that is stored in 
//           ini file dev_xxx.ini where xxx is passed in parameter device,
//           in section name 'section'. If the key is not present and 
//           createOnDefault is true a new key is created with the value
//           passed in 'def'. If the key does not exist, the value
//           def is always returned, even when createOnDefault is false.
//
int AwCcConfig::getInt(int device, const string & section, const string & key, int	def, bool createOnDefault)
{
	DWORD sizeTransferred;	// Holds the number of bytes read by the key read function.
	char myBuff[MAX_PATH];	// Buffer used for transfers
	string fullfilename = AwCcConfig::getDevPath(device);		// Convert the filename into path + file
	
	if ((device < 1) || (device > 999))
	{
		// Invalid device ID. Return 0 as a safe number
		return 0;
	}
	// Try to read the provided token from the file. If the token does not exist, the default string of ""
	// is returned, and the sizeTransferred is zero.
	sizeTransferred = GetPrivateProfileString(section.c_str(), key.c_str(), "", myBuff, MAX_PATH, fullfilename.c_str());

	if (sizeTransferred == 0)
	{
		// Key does not exist. Next actions depend on input parameter "createOnDefault". If this is true, we must
		// tell windows to create a key with the integer value provided, otherwise just return the default value.
		if (createOnDefault)
		{
			sprintf_s(myBuff, MAX_PATH, "%d", def);		// Convert integer to a string
			WritePrivateProfileString(section.c_str(), key.c_str(), myBuff, fullfilename.c_str());
		}

		return def;
	}
	else
	{
		// Key exists. So convert the returned key value to an integer.
		return atoi(myBuff);
	}
}	// End of int AwCcConfig::getInt(int device, ...


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: int AwCcConfig::getInt(const string & file, 
//                                     const string & section,
//                                     const string & key,
//                                     int def,
//                                     bool createOnDefault)
//
// PURPOSE:  Return the integer value for the named 'key' that is stored in 
//           ini file 'file', in section name 'section'. If the key is not
//           present and createOnDefault is true a new key is created with
//           the value passed in 'def'. If the key does not exist, the value
//           def is always returned, even when createOnDefault is false.
//
int AwCcConfig::getInt(const string & file, const string & section, const string & key, int	def, bool createOnDefault)
{
	// Note that the file name provided 'file' is just the ini file name, the file path 
	// must be prepended based on the host environment.
	
	DWORD sizeTransferred;	// Holds the number of bytes read by the key read function.
	char myBuff[MAX_PATH];	// Buffer used for transfers
	string fullfilename = AwCcConfig::getDevPath(file);		// Convert the filename into path + file
	
	// Try to read the provided token from the file. If the token does not exist, the default string of ""
	// is returned, and the sizeTransferred is zero.
	sizeTransferred = GetPrivateProfileString(section.c_str(), key.c_str(), "", myBuff, MAX_PATH, fullfilename.c_str());

	if (sizeTransferred == 0)
	{
		// Key does not exist. Next actions depend on input parameter "createOnDefault". If this is true, we must
		// tell windows to create a key with the integer value provided, otherwise just return the default value.
		if (createOnDefault)
		{
			sprintf_s(myBuff, MAX_PATH, "%d", def);		// Convert integer to a string
			WritePrivateProfileString(section.c_str(), key.c_str(), myBuff, fullfilename.c_str());
		}

		return def;
	}
	else
	{
		// Key exists. So convert the returned key value to an integer.
		return atoi(myBuff);
	}
}


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: int AwCcConfig::getString(const string & file, 
//                                     const string & section,
//                                     const string & key,
//                                     const string & def,
//                                     bool createOnDefault)
//
// PURPOSE:  Return the string value for the named 'key' that is stored in 
//           ini file 'file', in section name 'section'. If the key is not
//           present and createOnDefault is true a new key is created with
//           the value passed in 'def'.
//
string AwCcConfig::getString(const string & file, const string & section, const string & key, const string & def, bool createOnDefault)
{
	// Note that the file name provided 'file' is just the ini file name, the file path 
	// must be prepended based on the host environment.

	DWORD sizeTransferred;	// Holds the number of bytes read by the key read function.
	char myBuff[MAX_PATH];	// Buffer used for transfers
	string fullfilename = AwCcConfig::getDevPath(file);		// Convert the filename into path + file

	// Try to read the provided token from the file. If the token does not exist, the default string of ""
	// is returned, and the sizeTransferred is zero.
	sizeTransferred = GetPrivateProfileString(section.c_str(), key.c_str(), "", myBuff, MAX_PATH, fullfilename.c_str());

	if (sizeTransferred == 0)
	{
		// Key was not found, as a blank token would return '\0'
		if (createOnDefault)
		{
			WritePrivateProfileString(section.c_str(), key.c_str(), def.c_str(), fullfilename.c_str());
		}
		return def;
	}
	else
	{
		string answer = myBuff;		// Convert character array to c++ string
		return answer;
	}
}	// End of AwCcConfig::getString


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: int AwCcConfig::getString(int device, 
//                                     const string & section,
//                                     const string & key,
//                                     const string & def,
//                                     bool createOnDefault)
//
// PURPOSE:  Return the string value for the named 'key' that is stored in 
//           ini file of device 'device', in section name 'section'. If the
//           key is not present and createOnDefault is true a new key is created
//           with the value passed in 'def'.
//
string AwCcConfig::getString(int device, const string & section, const string & key, const string & def, bool createOnDefault)
{
	DWORD sizeTransferred;	// Holds the number of bytes read by the key read function.
	char myBuff[MAX_PATH];	// Buffer used for transfers
	string fullfilename = AwCcConfig::getDevPath(device);	// Find the path + dev_xxx.ini for this device

	// Try to read the provided token from the file. If the token does not exist, the default string of ""
	// is returned, and the sizeTransferred is zero.
	sizeTransferred = GetPrivateProfileString(section.c_str(), key.c_str(), "", myBuff, MAX_PATH, fullfilename.c_str());

	if (sizeTransferred == 0)
	{
		if (createOnDefault)
		{
			WritePrivateProfileString(section.c_str(), key.c_str(), def.c_str(), fullfilename.c_str());
		}
		return def;
	}
	else
	{
		// Valid string was found, so return that string
		string answer = myBuff;		// Convert character array to c++ string
		return answer;
	}
}	// End of AwCcConfig::getString(int device, ......


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: bool AwCcConfig::isV3Based(void)
//
// PURPOSE:  Return a flag indicating if the host system is BNCS Version 3
//
bool AwCcConfig::isV3Based(void)
{
	if (!(sysTypeKnown))
	{
		AwCcConfig::findBNCSproperties();
	}
	return AwCcConfig::sysTypeV3;
}


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: bool AwCcConfig::isV4Based(void)
//
// PURPOSE:  Return a flag indicating if the host system is BNCS Version 3 
//
bool AwCcConfig::isV4Based(void)
{
	if (!(sysTypeKnown))
	{
		AwCcConfig::findBNCSproperties();
	}
	return AwCcConfig::sysTypeV4;
}


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: void AwCcConfig::setString(const string & file, 
//                                     const string & section,
//                                     const string & key,
//                                     const string & value)
//
// PURPOSE:  Set the string for the named 'key' to the value 'def' in section
//           'section' of ini filename passed as paramter 'file'.
//
void AwCcConfig::setString(const string & file, const string & section, const string & key, const string & value)
{
	string fullfilename = AwCcConfig::getDevPath(file);		// Convert the filename into path + file

	WritePrivateProfileString(section.c_str(), key.c_str(), value.c_str(), fullfilename.c_str());
}


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: void AwCcConfig::setString(int device,
//                                     const string & section,
//                                     const string & key,
//                                     const string & value)
//
// PURPOSE:  Set the string value for 'key' to the pprovided 'value' in section
//           'section' of dev_xxx.ini where xxx is passed to this function as
//           parameter 'device'
//
void AwCcConfig::setString(int device, const string & section, const string & key, const string & value)
{
	string fullfilename = AwCcConfig::getDevPath(device);	// Convert to filename into path + file

	WritePrivateProfileString(section.c_str(), key.c_str(), value.c_str(), fullfilename.c_str());
}


///////////////////////////////////////////////////////////////////////////////
// FUNCTION: int AwCcConfig::workstation(void)
//
// PURPOSE:  Gets the workstation id for the host workstation
//
// RETURNS:  Number should be in range 1 to 999 if WS ID found, or -1 if the
//           workstation number is not found or is out of valid range.
//
// COMMENTS:
// Tries to read the V4.5 environment string. If this is not found, then tries
// to read number from CSI.INI. The function uses host_BNCS_system value to
// check if the BNCS paths are set. If not set, then this function calls
// find_BNCS_paths to set up the data.
//
int AwCcConfig::workstation(void)
{
	static int myWs = -1;
	static bool alreadyKnown = false;		// Flag is set true once workstation ID is known.
											// Subsequent calls will then use shortcut code

	if (alreadyKnown)
	{
		return myWs;
	}

	// Do we already know the various path elements? If not find them and the host system
	if (!(AwCcConfig::pathsSet))
	{
		AwCcConfig::findBNCSproperties();	// Read the various paths
		AwCcConfig::pathsSet = true;	// Set flag to show the paths are now set.
	}

	// Now extract the workstation id
	if (AwCcConfig::sysTypeV4)
	{
		char workstationID[MAX_PATH];

		// Read workstation ID from environment variable
		GetEnvironmentVariable("CC_WORKSTATION", workstationID, MAX_PATH);
		myWs = atoi(workstationID);
		if ((myWs < 1) || (myWs >999)) myWs = -1;
		alreadyKnown = true;
		return myWs;
	}

	// Is the system type V3 or Classic (V2)...
	if ((AwCcConfig::sysTypeV3) || (!(AwCcConfig::sysTypeV4) && !(AwCcConfig::sysTypeV3)))
	{
		char workstationID[MAX_PATH];
		char csi_ini[MAX_PATH];
		char myBNCS_File_Path[MAX_PATH];

		// Compute path and filename to access CSI.INI
		sprintf_s(csi_ini, sizeof(csi_ini), "%s\\%s", myBNCS_File_Path, "CSI.INI");

		// Read the workstation ID from CSI.INI
		GetPrivateProfileString("Network", "Workstation", "-1", workstationID, sizeof(workstationID), csi_ini);
		myWs = atoi(workstationID);
		if ((myWs < 1) || (myWs >999)) myWs = -1;
		alreadyKnown = true;
		return myWs;
	}

	// A catchall
	alreadyKnown = true;
	myWs = -1;
	return myWs;	
}	// End of int AwCcConfig::workstation(void)


////////////////////////////////////////////////////////////////////////////////
// FUNCTION: void AwCcConfig::findBNCSproperties(void)
//
// PURPOSE:  Discover the routes to the system files and the host system type
//
void AwCcConfig::findBNCSproperties(void)
{
	// Try to read the V4.5 environment variables CC_ROOT and CC_SYSTEM
	GetEnvironmentVariable("CC_ROOT", myCCRoot, MAX_PATH);
	GetEnvironmentVariable("CC_SYSTEM", myCCSystem, MAX_PATH);

	// Ask for the windows directory to be returned.
	GetWindowsDirectory(myWinDir, MAX_PATH);

	// See if the V3 config file exists by looking for the ConfigPath entry in c:\bncs_config.ini.
	// We set the default string to return to the null string, thus a non-null string indicates that the entry exists and we have a V3 system.
	GetPrivateProfileString("Config", "ConfigPath", "", AwCcConfig::myV3Path, sizeof(AwCcConfig::myV3Path), "C:\\bncs_config.ini");

	// Try to determine the BNCS host system
	if ((strlen(myCCRoot) > 0) && (strlen(myCCSystem) > 0))
	{
		// Version 4.5 system detected.
		AwCcConfig::sysTypeV4 = true;
		AwCcConfig::sysTypeV3 = false;
		AwCcConfig::sysTypeKnown = true;
	}
	else if (strlen(myV3Path) > 0)
	{
		// We assume that we have a version 3 system
		AwCcConfig::sysTypeV3 = true;
		AwCcConfig::sysTypeV4 = false;
		AwCcConfig::sysTypeKnown = true;
	}
	else
	{
		// Assume we have a classic system, using windows directory
		AwCcConfig::sysTypeV3 = false;
		AwCcConfig::sysTypeV4 = false;
		AwCcConfig::sysTypeKnown = true;
	}

}
