/*********************************************************************************************
 * Copyright 2004 - Volian Enterprises, Inc. All rights reserved.
 * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
 * ------------------------------------------------------------------------------
 * $Workfile: UsrRunTime.cs $     $Revision: 9 $
 * $Author: Jsj $   $Date: 10/31/07 10:11a $
 *
 * $History: UsrRunTime.cs $
 * 
 * *****************  Version 9  *****************
 * User: Jsj          Date: 10/31/07   Time: 10:11a
 * Updated in $/LibSource/Utils
 * Bug fix to ExeAdjust()
 * 
 * *****************  Version 8  *****************
 * User: Jsj          Date: 7/21/06    Time: 2:57p
 * Updated in $/LibSource/Utils
 * added comment of logic that might be needed in future.
 * 
 * *****************  Version 7  *****************
 * User: Jsj          Date: 5/17/05    Time: 11:54a
 * Updated in $/LibSource/Utils
 * cleanup
 * 
 * *****************  Version 6  *****************
 * User: Kathy        Date: 4/21/05    Time: 10:17a
 * Updated in $/LibSource/Utils
 * issingleuser
 * 
 * *****************  Version 5  *****************
 * User: Kathy        Date: 4/12/05    Time: 1:00p
 * Updated in $/LibSource/Utils
 * B2004-050: single user needed network info
 * 
 * *****************  Version 4  *****************
 * User: Kathy        Date: 1/24/05    Time: 2:44p
 * Updated in $/LibSource/Utils
 * B2005-004 fixes
 * 
 * *****************  Version 3  *****************
 * User: Kathy        Date: 1/10/05    Time: 12:57p
 * Updated in $/LibSource/Utils
 * B2004-063 fix
 * 
 * *****************  Version 2  *****************
 * User: Jsj          Date: 11/12/04   Time: 10:32a
 * Updated in $/LibSource/Utils
 * Save the user's TEMP dir path ExeAjust() handles TEMP dir and Data Dir
 * 
 * *****************  Version 1  *****************
 * User: Kathy        Date: 7/27/04    Time: 8:34a
 * Created in $/LibSource/Utils
 *********************************************************************************************/

using System;
using System.IO;
using System.Windows.Forms;
using System.Text;
using Org.Mentalis.Files;

namespace Utils
{
	public enum UserCStatus
	{
		PRINACTIVE=0, PRACTIVE=1		// process record is NOT active, active respectively
	};

	public class UserData
	{
		public byte   UserStatus;
		public string UserNetworkID;
		public string UserName;
		public string UserPhone1;
		public string UserPhone2;
		public string UserLoc1;
		public string UserLoc2;
		public string UserShell;			// 1 - vfw; 2 - browser; 3 - 32-bit browser
		public string UserProcess;
		
		// the first constructor loads data from a user's cfg file
		public UserData(string ucfgpath)
		{
			if (File.Exists(ucfgpath)==false) return;
			// create an xml node from the [User Data] in the cfg file & transfer
			// to this.  Note that the UserNetworkID is not set until the serial
			// number is read to determine whether in multi-user-mode (B2004-050)
			PrivateProfile ppUserData = new PrivateProfile(ucfgpath);
			UserName=ppUserData.Attr("User Data","UserName");
			UserPhone1=ppUserData.Attr("User Data","UserPhone1");
			UserPhone2=ppUserData.Attr("User Data","UserPhone2");
			UserLoc1=ppUserData.Attr("User Data","UserLoc1");
			UserLoc2=ppUserData.Attr("User Data","UserLoc2");
			char tmp = '\x03';
			UserShell = tmp.ToString();		// 1 - vfw; 2 - browser; 3 - 32-bit browser
			UserProcess="";
			ppUserData=null;
		}
		
		public void SetNetworkID(string ucfgpath, bool inmultiuser)
		{
			// create an xml node from the [User Data] in the cfg file & transfer
			// to this.
			PrivateProfile ppUserData = new PrivateProfile(ucfgpath);
			UserNetworkID=ppUserData.Attr("User Data","UserNetworkID");
			if (inmultiuser && (UserNetworkID == "" || UserNetworkID==null))
			{
				MessageBox.Show("Invalid UserNetworkID from " + ucfgpath +", set before reexecuting VE-PROMS.","VE-PROMS Error");
				Environment.Exit(-1);
			}
			ppUserData=null;
		}
		// the next constructor gets the input 'nth' record from the dat file (dat file is
		// passed in via a FileStream
		public UserData(FileStream dfs, int n)
		{
			// seek to the position as defined by n.
			long skpos = (long)(n * 140);   // 140 is the size of the record from old proms.
			dfs.Seek(skpos,SeekOrigin.Begin);
			byte [] test = new byte[140];
			dfs.Read(test,0,140);
			UserStatus = test[0];
			string tmp = Encoding.ASCII.GetString(test,0,140);
			UserNetworkID = tmp.Substring(1,9).Replace('\0',' ').Trim();
			UserName = tmp.Substring(10,31).Replace('\0',' ').Trim();
			UserPhone1 = tmp.Substring(41,16).Replace('\0',' ').Trim();
			UserPhone2 = tmp.Substring(57,16).Replace('\0',' ').Trim();
			UserLoc1 = tmp.Substring(73,31).Replace('\0',' ').Trim();
			UserLoc2 = tmp.Substring(104,31).Replace('\0',' ').Trim();
			UserShell = tmp.Substring(135,2).Replace('\0',' ').Trim();
			UserProcess = tmp.Substring(137,3).Replace('\0',' ').Trim();
		}
		
		// write this user data out to the filestream at the current position in file
		// defined in dfs
		public void Write(FileStream dfs)
		{
			dfs.WriteByte(UserStatus);
			byte[] btmp = Encoding.ASCII.GetBytes(UserNetworkID.PadRight(9,'\0'));
			dfs.Write(btmp,0,9);			
			btmp = Encoding.ASCII.GetBytes(UserName.PadRight(31,'\0'));
			dfs.Write(btmp,0,31);
			btmp = Encoding.ASCII.GetBytes(UserPhone1.PadRight(16,'\0'));
			dfs.Write(btmp,0,16);
			btmp = Encoding.ASCII.GetBytes(UserPhone2.PadRight(16,'\0'));
			dfs.Write(btmp,0,16);
			btmp = Encoding.ASCII.GetBytes(UserLoc1.PadRight(31,'\0'));
			dfs.Write(btmp,0,31);
			btmp = Encoding.ASCII.GetBytes(UserLoc2.PadRight(31,'\0'));
			dfs.Write(btmp,0,31);
			btmp = Encoding.ASCII.GetBytes(UserShell.PadRight(2,'\0'));
			dfs.Write(btmp,0,2);
			btmp = Encoding.ASCII.GetBytes(UserProcess.PadRight(3,'\0'));
			dfs.Write(btmp,0,3);
		}
	};

	/// <summary>
	/// Summary description for UserRunTime. This class contains all pertinent information
	/// for the runtime for this instance of the program, including cfg, paths, menu file,
	/// security, serial number, options, etc.
	/// </summary>
	public class UserRunTime
	{
		public string exepath;
		public string syspath;
		public string netsyspath;
		public string TempDirPath;

		public string ucfgpath;			// user config file name
		public string appcfgpath;		// app config file name
		
		public Security sec;
		public SerialNo SerialNumber;
		public string OptionsFile;
		public string initials; // user login name
		public string UserID; // cfg name and/or login
		public MenuXML menuwinXML;
		public MenuXML menuwin2XML;
		public string myUserNetworkID;
		public UserData myUserData;
		public bool InMultiUserMode;

		// make some form properties  here, such as color. - make need it's own class,
		// 
		public UserRunTime()
		{
			menuwin2XML = null;
			SetExeName();
			// load in xml list of plants. Then do exist tests to see if they 
			// exist on the datapath.
			menuwinXML = new MenuXML(syspath + "\\menuwin.xml");
		}

		public void SetSecurity(Security isec)
		{
			// set the user's run time security settings. also, flag whether this user is
			// running in multi user mode (just so we don't have to keep making this check
			// later)
			sec = isec;
			// Do this separate (SetUserMode()) to remove interdependent logic path between
			// security(vesam), user CFG file, and the serial number
//			InMultiUserMode = (SerialNumber.GetSNOption((uint)Utils.SerialNo.SN_Flags.SN_NETWORK_ON)>0)?true:false;
		}

		public void SetUserMode()
		{
			if (SerialNumber.SerialNum != -1)
				InMultiUserMode = (SerialNumber.GetSNOption((uint)Utils.SerialNo.SN_Flags.SN_NETWORK_ON)>0)?true:false;
			if (InMultiUserMode) myUserData.SetNetworkID(ucfgpath, InMultiUserMode);
		}

		private bool IsSingleUserMode()
		{
			// Need to do:
			//     This needs to check if a lock is currently set as well
			// - jsj 7/20/06
			return !InMultiUserMode;
		}

		// Adjust the input file with the required pathname as follows:
		// '~': (system path) i.e. c:\VE-PROMS
		// '@': (executable path) i.e. c:\VE-PROMS\BIN
		// '\342': current data directory  i.e. c:\VEHPR1B\PROCS
		// '\347': resolve for single user mode, append tempdir onto data dir i.e. TEMPS\JOHN____.D00
		public string ExeAdjust(string str)
		{
			if (exepath==null)SetExeName();

			//	This logic will allow me to combine the use of our special
			//	tokens.  For example, I needed a user temp directory in the
			//	VE-PROMS root for the Search Across Procedure Sets option:
			//		"~\374results" --> C:\VE-PROMS\TEMPS\JOHN____.D00\RESUTLS
	 
			StringBuilder exebuff = new StringBuilder();
			StringBuilder exetkn = new StringBuilder(str);
			string exetkn_str = exetkn.ToString();
			int pos = 0;
			char[] tchar = "~@\xE7\xE2".ToCharArray();
			/** Bug Fix:  B2007-015
			 * If a short file/directory name contained a ~ character, it was
			 * being processed, when it should not  have.
			 * i.e.  we were getting the current directory, then looping back up
			 * and searching the directory string (finding a ~ char)
			 * 
			 * I added logic to use a starting position in the IndexOfAny() function
			 * so that we can skip past the portions already processes.
			 */
			while ((pos=exetkn_str.IndexOfAny(tchar,pos))>-1)
			{
				char[] chr;
				string curdir = Directory.GetCurrentDirectory(); // for use with '\xE2'
				chr = new char[exetkn_str.Length];
				chr = exetkn_str.ToCharArray();
				exetkn.Remove(pos,1);
				
				switch (chr[0]) 
				{
					case '~': exetkn.Insert(pos,syspath+"\\"); // i.e. c:\VE-PROMS
						pos += syspath.Length +1;
						break;
					case '@': exetkn.Insert(pos,exepath+"\\"); // i.e. c:\VE-PROMS\BIN
						pos += exepath.Length + 1;
						break;
					case '\xE2': // case '\342':
						exetkn.Insert(pos,curdir+"\\"); // i.e. c:\VEHPR1B\PROCS
						pos += curdir.Length + 1;
						break;
					case '\xE7': // case '\347':
						if (!IsSingleUserMode()&& TempDirPath!=null) 
						{
							exetkn.Insert(pos,TempDirPath+"\\"); // i.e. TEMPS\JOHN____.D00
							pos += TempDirPath.Length + 1;
						}
						break;
				}
				exetkn_str = exetkn.ToString();
			}
			return exetkn.ToString();
		}

		private void SetExeName()
		{
			ShortName sname = new ShortName(System.Environment.CommandLine);
			string cmdlinepath = sname.ShortFileName;
			string upbuff = cmdlinepath.ToUpper();
			string tmp = upbuff.Substring(1,upbuff.LastIndexOf("\\")-1);
			int idx = tmp.IndexOf("VE-PROMS.NET");
			if (idx > 0)
			{
				exepath = tmp.Substring(0,idx+8) + tmp.Substring(idx+12);
				syspath = exepath.Substring(0,exepath.IndexOf("\\BIN"));
				netsyspath = syspath + ".NET";
			}
			else
			{
				exepath = tmp;
				syspath = exepath.Substring(0,exepath.IndexOf("\\BIN"));
				netsyspath = syspath + ".NET";
			}
		}

		public void SetAppConfig(string pth)
		{
			appcfgpath = pth;
		}

		public bool SetUserConfig()
		{
			bool Rtnval = true;
			PrivateProfile Veproms_ini = new PrivateProfile(ExeAdjust("~veproms.ini"));
			// Find the UserId (not the number, he name/initials)
			if (UserID==null || UserID.Equals(""))
			{
				// see if it's defined via an environment variable
				UserID = System.Environment.GetEnvironmentVariable("userid");
				if (UserID == null)
				{
					// Check the VEPROMS.INI file
//					UserID = Veproms_ini.Attr("/ini/section[@name='veconfig']/param[@name='userid']/@value");
					UserID = Veproms_ini.Attr("veconfig","userid");
				}
				if (UserID == null || UserID.Equals(""))
				{
					if (sec.initials != null && !sec.initials.Equals(""))
					{
						// default to the user's login name
						UserID = sec.initials;
					}
					else
					{
						// default to VEPROMS.CFG
						UserID = "VEPROMS";
					}
				}
			}
			// Fing the VECONFIG path
			ucfgpath=System.Environment.GetEnvironmentVariable("veconfig");
			// If VECONFIG environment variable not set then check the
			// VEPROMS.INI file for VECONFIG settings.
			if (ucfgpath == null || ucfgpath.Equals(""))
			{
//				ucfgpath = Veproms_ini.Attr("/ini/section[@name='veconfig']/param[@name='file']/@value");
				ucfgpath = Veproms_ini.Attr("veconfig","file");
				if (ucfgpath==null || ucfgpath.Equals(""))
				{
					// still no path, use default cfg file path
					ucfgpath = ExeAdjust("~CONFIG");
				}
			}

			if (ucfgpath != null)
			{
				string tmp_UID="";
				// See it the VECONFIG path includes a CFG file name.
				// if no CFG file name, then build one based on userid
				bool atroot = ucfgpath.EndsWith(":\\");
				if (!atroot)
				{
					if (ucfgpath.EndsWith("\\"))
					{
						// trim the ending backslash
						ucfgpath = ucfgpath.Substring(0,ucfgpath.Length-1);
					}
				}
				//See if we need to build a CFG file name
				if (atroot || Directory.Exists(ucfgpath))
				{
					// use the no more than the first 8 chars of the userid
					tmp_UID = (UserID.Length > 8)?UserID.Substring(0,8) : UserID;

					// Build the CFG file name and append to the VECONFIG path
					ucfgpath = ucfgpath + "\\" + tmp_UID + ".CFG";
				}
			}
			if (File.Exists(ucfgpath))
				myUserData = new UserData(ucfgpath);
			else
			{
				// error - cfg file does not exist
				Rtnval = false;
				MessageBox.Show("Configuration file:\n\n" + ucfgpath + "\n\nDoes not exist","Missing CFG file");
			}
			return Rtnval;
		}

		public void FreeMenuWinXML()
		{
			menuwinXML=null;
		}
		public void LoadMenuWin2()
		{
			if (menuwin2XML == null)
				menuwin2XML = new MenuXML(syspath + "\\menuwin2.xml");
			//	menuwin2XML = new MenuXML("e:\\ve-proms\\menuwin2.xml");
		}
		public void FreeMenuWin2XML()
		{
			menuwin2XML=null;
		}

		// The following methods update the cfg file (delete item, add item & mod)
		//public bool DeletePathFromCfg(string delpth)
		//{
		//  IniReader in1 = new IniReader(ucfgpath);
		//  string curpath = in1.ReadString("Menu","DataPath","");
		//  string modpath = curpath.Replace(delpth,"");
		//  inifile.WriteINIKeyValueStr("Menu","DataPath",modpath,ucfgpath);
		//  return true;
		//}

		//public void AddPathToCfg(string pth)
		//{	
		//  string newpath=null;		
		//  INIFile inifile =  new INIFile();
		//  string curpath = inifile.GetINIKeyValueStr("Menu","DataPath","",132,ucfgpath);
		//  string tmp = curpath.TrimEnd(" \t".ToCharArray());
		//  // If none exists, just add that entered, if it ends with ';', don't add another
		//  if (tmp==null||tmp=="")
		//    newpath = pth;
		//  else if (tmp.Substring(tmp.Length-1,1)==";")
		//    newpath = tmp + pth;
		//  else
		//    newpath = tmp+";"+pth;
		//  inifile.WriteINIKeyValueStr("Menu","DataPath",newpath,ucfgpath);
		//}

		//public void ModTitleToCfg(string oldtitle, string newtitle)
		//{		
		//  INIFile inifile =  new INIFile();
		//  string curpath = inifile.GetINIKeyValueStr("Menu","DataPath","",132,ucfgpath);
		//  string newpath=curpath.Replace(oldtitle,newtitle);
		//  inifile.WriteINIKeyValueStr("Menu","DataPath",newpath,ucfgpath);
		//}

		// Check if plant (defined by location) is in the menuwin file. Return 0 if not,
		// 1 if in file and -1 if error reading file.
		public int IsInMenuWin(string location)
		{
			// see if this plant is already in the file, check menuwin
			// and if there, assume in both.
			try 
			{
				// Create an instance of StreamReader to read from a file.
				// The using statement also closes the StreamReader.
				using (StreamReader sr = new StreamReader(syspath+"\\menuwin")) 
				{
					string line;
					while ((line = sr.ReadLine()) != null) 
					{
						int indx;
						indx = line.ToUpper().IndexOf(location.ToUpper());
						if (indx>-1)
						{
							sr.Close();
							return 1;
						}
					}
				}
			}
			catch (Exception e) 
			{
				// Let the user know what went wrong.
				Console.WriteLine("Could not test " + e.Message + " for plant existence in menuwin file");
				return -1;
			}
			return 0;
		}

		public bool AddToMenuWin(string title,string location)
		{
			int inmenufile = IsInMenuWin(location);
			if (inmenufile==-1)return false;		// error checking for it
			if (inmenufile==1)return true;			// already there.

			// make backup files in case of failure.
			FileInfo menuwinbak = new FileInfo(syspath+"\\menuwin");
			menuwinbak.CopyTo(syspath+"\\menuwin.BAK",true);
			menuwinbak=null;

			// add the title and location to the menuwin menuwin.xml files.
			// menuwin first.
			FileStream fs = new FileStream(syspath+"\\menuwin",
				FileMode.Open, FileAccess.ReadWrite, FileShare.None);

			// if the file couldn't open, give an error.
			if (fs == null)
			{
				MessageBox.Show("Could not add plant to menuwin file.","VE-PROMS");
				menuwinbak = new FileInfo(syspath+"\\menuwin.BAK");
				menuwinbak.Delete();
				return false;
			}
			// go to last character of file & see if we have a /r/n. There
			// needs to be a single newline between plants in the menuwin file
			// or an error will occur.
			fs.Seek(-2,SeekOrigin.End);
			StreamReader tmpReader = new StreamReader(fs);
			int tmpi1 = tmpReader.Peek();
			bool hasnewline=false;
			if (tmpi1==0xD)hasnewline=true;
			fs.Seek(0,SeekOrigin.End);
			
			StreamWriter swFromFileStream = new StreamWriter(fs);
			if (hasnewline)
				swFromFileStream.Write(title);
			else
				swFromFileStream.Write("\r\n"+title);
			string tmpl = "\r\n{t}" + location + " menu ~menu1a\r\n";
			swFromFileStream.Write(tmpl);
			swFromFileStream.Flush();
			swFromFileStream.Close();
			fs.Close();

			// now add menuwin.xml text, add this before the end tags for 
			// system attach & menuwin (i.e. that's the seek part of this
			// subtracting from end of file the number of characters for
			// these two tags.
			// make backup files in case of failure.
			FileInfo menuwinxbak = new FileInfo(syspath+"\\menuwin");
			menuwinxbak.CopyTo(syspath+"\\menuwinx.BAK",true);
			menuwinxbak=null;

			fs = new FileStream(syspath+"\\menuwin.xml",
				FileMode.Open, FileAccess.Write, FileShare.None);
			if (fs == null)
			{
				MessageBox.Show("Could not add plant to menuwin.xml file.","VE-PROMS");
				//delete menuwin & rename the bak file.
				menuwinbak = new FileInfo(syspath+"\\menuwin");
				menuwinbak.Delete();
				menuwinbak = null;
				menuwinbak = new FileInfo(syspath+"\\menuwin.BAK");
				menuwinbak.CopyTo(syspath+"\\menuwin");
				menuwinbak.Delete();
				menuwinxbak = new FileInfo(syspath+"\\menuwinx.BAK");
				menuwinxbak.Delete();
				return false;
			}
			long flen = fs.Length;
			fs.SetLength(flen-27);  //remove tags.
			fs.Seek(0,SeekOrigin.End);
			swFromFileStream = new StreamWriter(fs);
			swFromFileStream.Write("<Plant>\r\n<MenuName>");
			swFromFileStream.Write(title);
			swFromFileStream.Write("</MenuName>\r\n<TemplateName>{t}");
			swFromFileStream.Write(location);
			swFromFileStream.Write(" menu menu1a</TemplateName>\r\n</Plant>\r\n");
			swFromFileStream.Write("</SystemAttach>\r\n</MenuWin>");
			
			swFromFileStream.Flush();
			swFromFileStream.Close();
			fs.Close();
			menuwinbak = new FileInfo(syspath+"\\menuwin.BAK");
			menuwinbak.Delete();
			menuwinxbak = new FileInfo(syspath+"\\menuwinx.BAK");
			menuwinxbak.Delete();
			// reload so that the new plant node gets added to the xml tree.
			menuwinXML = null;
			menuwinXML = new MenuXML(syspath + "\\menuwin.xml");
			MessageBox.Show("Please send \\ve-proms\\menuwin & \\ve-proms\\menuwin.xml files to Volian.","VE-PROMS");
			return true;
		}

		public bool ModMenuWin(string newtitle, string oldtitle)
		{
			// replace the oldtitle with the new in both the menuwin file
			// and the menuwin.xml file.
			FileInfo menuwinbak = new FileInfo(syspath+"\\menuwin");
			menuwinbak.CopyTo(syspath+"\\menuwin.BAK",true);
			menuwinbak=null;

			// read the whole file, replace the plant title string & save the file.
			StreamReader sr = File.OpenText(syspath+"\\menuwin");
			string input;
			if ((input=sr.ReadToEnd())!=null) 
			{
				sr.Close();
				string output = input.Replace(oldtitle,newtitle);
				StreamWriter sw = new StreamWriter(syspath+"\\menuwin",false);
				sw.Write(output);
				sw.Close();
			}
			else 
			{
				sr.Close();
				MessageBox.Show("Could not update menuwin file with new plant title","VE-PROMS");
				menuwinbak = new FileInfo(syspath+"\\menuwin.BAK");
				menuwinbak.CopyTo(syspath+"\\menuwin",true);
				menuwinbak.Delete();
				return false;
			}

			// now do the xml file.
			menuwinbak = new FileInfo(syspath+"\\menuwin.xml");
			menuwinbak.CopyTo(syspath+"\\menuwinx.BAK",true);
			menuwinbak=null;

			// read the whole file, replace the plant title string & save the file.
			sr = File.OpenText(syspath+"\\menuwin.xml");
			if ((input=sr.ReadToEnd())!=null) 
			{
				sr.Close();
				string output = input.Replace(oldtitle,newtitle);
				StreamWriter sw = new StreamWriter(syspath+"\\menuwin.xml",false);
				sw.Write(output);
				sw.Close();
			}
			else 
			{
				sr.Close();
				MessageBox.Show("Could not update menuwin.xml file with new plant title","VE-PROMS");
				menuwinbak = new FileInfo(syspath+"\\menuwin.BAK");
				menuwinbak.CopyTo(syspath+"\\menuwin",true);
				menuwinbak.Delete();
				menuwinbak = new FileInfo(syspath+"\\menuwinx.BAK");
				menuwinbak.CopyTo(syspath+"\\menuwin.xml",true);
				menuwinbak.Delete();
				return false;
			}
			
			return true;
		}
	}
}
