/*********************************************************************************************
 * Copyright 2004 - Volian Enterprises, Inc. All rights reserved.
 * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
 * ------------------------------------------------------------------------------
 * $Workfile: Plant.cs $     $Revision: 10 $
 * $Author: Jsj $   $Date: 10/20/06 9:34a $
 *
 * $History: Plant.cs $
 * 
 * *****************  Version 10  *****************
 * User: Jsj          Date: 10/20/06   Time: 9:34a
 * Updated in $/LibSource/VEObject
 * long name fix
 * 
 * *****************  Version 9  *****************
 * User: Jsj          Date: 9/26/06    Time: 9:38a
 * Updated in $/LibSource/VEObject
 * check for a siingle quote in directory name
 * 
 * *****************  Version 8  *****************
 * User: Kathy        Date: 6/06/05    Time: 12:35p
 * Updated in $/LibSource/VEObject
 * Allow lock set even if system lock set
 * 
 * *****************  Version 7  *****************
 * User: Kathy        Date: 4/12/05    Time: 1:01p
 * Updated in $/LibSource/VEObject
 * 
 * *****************  Version 6  *****************
 * User: Kathy        Date: 2/02/05    Time: 10:13a
 * Updated in $/LibSource/VEObject
 * B2005-006: fix missing proc sets under plant
 * 
 * *****************  Version 5  *****************
 * User: Kathy        Date: 1/31/05    Time: 11:06a
 * Updated in $/LibSource/VEObject
 * Fix B2005-005 (connection & delete directory errors). also, fix icon
 * usage & security for new
 * 
 * *****************  Version 4  *****************
 * User: Kathy        Date: 1/24/05    Time: 2:45p
 * Updated in $/LibSource/VEObject
 * B2005-004 fixes
 * 
 * *****************  Version 3  *****************
 * User: Kathy        Date: 1/14/05    Time: 10:38a
 * Updated in $/LibSource/VEObject
 * B2004-061: Fix security optoins
 * 
 * *****************  Version 2  *****************
 * User: Kathy        Date: 1/10/05    Time: 12:58p
 * Updated in $/LibSource/VEObject
 * B2004-063 fix
 * 
 * *****************  Version 1  *****************
 * User: Kathy        Date: 7/27/04    Time: 8:53a
 * Created in $/LibSource/VEObject
 *********************************************************************************************/
using System;
using System.Collections;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Xml;
using System.ComponentModel;
using Utils;
using VENetwork;
namespace VEObject
{
	/// 
	/// This defines the VEO_Plant class which handles plant data. This contains
	/// support for the vexxx layer.
	/// 
	public class VEO_Plant : VEO_Base
	{
		public long accessFlags;
		protected string tmpTitle;
		protected string tmpLocation;
		protected bool changeLocation;
		protected bool changeTitle;
		private string PrevDirectory;
		public VEO_Plant(UserRunTime iusrRunTime, string ititle, string ipath)
		{
			// iconStates are the imagelist indexes for the tree view (imglist1)
			// the states are Nolock, LockedByMe, LockedByOther, LockPending, Empty.
			iconStates = new int[5] {3,21,5,22,4};
			_Title = ititle;
			_Location = ipath;
			usrRunTime = iusrRunTime;
			changeLocation=false;
			changeTitle=false;
			IsEmpty=false;
			VEObjectType=(int)VEObjectTypesDefs.Plant;
			isNew=false;
			LoadLockInfo();
			icon = iconStates[(int)Lock.LockStatus];
			if (Lock.LockStatus != VENetwork.Status.LockedByOther)
			{
				if (ipath != null)
				{
					string[] dirs = new string[40];
					dirs = Directory.GetDirectories(ipath);
					if (dirs.Length == 0)
					{
						IsEmpty=true;
						//only reset the icon to empty if there is no lock icon.
						//if a lock, show the lock icon
						if (Lock.LockStatus == VENetwork.Status.NoLock) icon = iconStates[(int)VEO_IconStates.Empty];
					}
				}
			}
		}
		public override void LoadLockInfo()
		{
			Lock = new VELock(_Location, usrRunTime.myUserData, usrRunTime.InMultiUserMode?VENetwork.LockTypes.Plant:VENetwork.LockTypes.None);
		}
		
		// Properties used for modify of 'Properties' from File menu.
		[Description("Location"),Category("Plant"),ReadOnly(true)]public string Location
		{
			get{return _Location;}
			set{_Location=value;}
		}
		[Description("Title"),Category("Plant")]public string Title
		{
			get
			{
				if (!changeTitle)
					return _Title;
				else
					return tmpTitle;
			}
			set
			{
				changeTitle=true;
				tmpTitle=value;
			}
		}
		// The open method checks for multi-user settings at the plant level. It
		// also positions into the plant directory.
		public override bool Open()
		{
			isOpen = true;
			PrevDirectory = Directory.GetCurrentDirectory();
			if(_Location!=null)Directory.SetCurrentDirectory(_Location);
			if (Connection!=null)Connection.Enter(false);
			return true;
		}
		// The close method resets, if necessary from any multi-user settings and
		// positions back to the datapath directory (which is a level above the 
		// current level)
		public override bool Close()
		{
			if (!isOpen)return false;
			isOpen=false;
			if (Connection!=null)Connection.Exit();
			Directory.SetCurrentDirectory("..");
			return true;
		}
		public override void Restore()
		{
			changeTitle=false;
		}
		public override bool Delete()
		{
			DialogResult result = MessageBox.Show("Are you sure you want to delete the entire plant directory " + this._Location,"Confirm Delete Again",MessageBoxButtons.YesNoCancel);
			if (result == DialogResult.Yes)
			{
				// delete the entire directory
				if (Directory.Exists(this._Location)) 
				{
					if(isOpen)Close();
					try
					{
						Directory.Delete(this._Location,true);
					}
					catch (Exception e)
					{
						MessageBox.Show("Could not delete directory - manually delete " + this._Location + ". Cause: " + e.Message);
					}
					if (Directory.Exists(this._Location))
					{
						MessageBox.Show("Unable to delete the plant directory.");
						return false;
					}
					return true;
				}
			}
			return false;
		}
		private void CreateProcSet(string dirpath, XmlElement nm, string dirname, long flags)
		{
			// see if there is a title file in the directory and use
			// it if there is, otherwise use menuname & if there's no menu
			// name, use the directory name.
			VEO_ProcSet procset = null;		
			string titlepath = dirpath + "\\" + "Title";
			FileInfo fi = new FileInfo(titlepath);
			if (File.Exists(titlepath))
			{
				StreamReader myReader = new StreamReader(titlepath);
				titlepath = myReader.ReadLine();
				myReader.Close();
				procset = new VEO_ProcSet(usrRunTime, titlepath.Trim(), dirpath, flags);
			}
			else
			{
				if (nm != null)
					procset = new VEO_ProcSet(usrRunTime, nm.InnerText, dirpath, flags);
				else
				{
					string tmpdirnm = dirname;
					int indx=0;
					// if this is temp change directory, tack on text to recognize it
					if ((indx=dirpath.ToUpper().IndexOf("TMPCHG"))>-1)
						tmpdirnm = dirname + " - Temporary Change Version";
					// if this is approved, tack on Current Approved Version to the title
					else if ((indx=dirpath.ToUpper().IndexOf("APPROVED"))>-1)
						tmpdirnm = dirname + " - Current Approved Version";					
					procset =  new VEO_ProcSet(usrRunTime, tmpdirnm, dirpath,flags);
				}
			}
			procset.parentObj = this;
			Children.Add(procset);
		}
		// private method, used below, to add a directory to the child list
		// (list contains proc sets)
		private bool ProcessDirName(XmlElement par, string dirname, int plntsecindx)
		{
			XmlNodeList nodeList = par.SelectNodes("ProcFile");
			if (nodeList == null) return false;
		
			foreach (XmlNode dirnd in nodeList)
			{
				XmlElement dele=(XmlElement)dirnd;
				string dirpath = _Location + "\\" + dirname;
				if (File.Exists(dirpath+"\\"+dirnd.InnerText))
				{
					// 
					// see if there's an exception or a requirement, i.e. use
					// the Working Draft if there's no proc2 directory (Except)
					// or use the Working Draft Unit 1 if there's a proc2
					// directory (Require)
					bool excpt = false;
					bool req = true;
					string path1=null;
					XmlElement except = (XmlElement) par.SelectSingleNode("Exception");
					if (except != null)
					{
						// if this exception directory exists, don't add this to
						// the set.
						
						path1 = dirpath + "\\" + except.InnerText;
						if (Directory.Exists(path1)) excpt = true;
					}
					XmlElement require = (XmlElement) par.SelectSingleNode("Require");
					if (require != null)
					{
						// if this require directory exists, add this to set only
						// if this exists.
						path1 = dirpath + "\\" + require.InnerText;
						if (Directory.Exists(path1) == false) req = false;
					}
					if (excpt == false && req == true)
					{
						XmlElement nm = (XmlElement) par.SelectSingleNode("MenuName");
						long flags=0L;
						// bug fix B2006-045
						// need to convert long folder/file names to short (8.3)
						ShortName sname = new ShortName(dirpath);
						bool hasSec = usrRunTime.sec.ProcSetHasSecurity(plntsecindx, Path.GetFileName(sname.ShortFileName));
//						bool hasSec = usrRunTime.sec.ProcSetHasSecurity(plntsecindx, dirname);
						if (this.usrRunTime.sec==null) flags=Security.SUPERACCESS;
						else if (hasSec) flags = usrRunTime.sec.GetProcSecurity(sname.ShortFileName);
						//						else if (hasSec) flags = usrRunTime.sec.GetProcSecurity(dirpath);
						
						if((hasSec && flags!=0L) || (!hasSec&&!usrRunTime.sec.BlockAccess()))
						{
							CreateProcSet(dirpath, nm, dirname, flags);
							return true;
						}
					}
				}
			}
			return false;
		}
		
		// This method processes through the SubDirSearch xmlelement tree in 
		// menuwin2.xml to see if the input procedure set directory has valid
		// procedure set subdirectories.  Arguments are:
		// subtop is xmlelement for the SubDirSearch in menuwin2.xml, dir is actual
		// procset directory name (for example, procs, proc000.dvt, etc) and
		// dname is the name to be used for the directory name in the menuwin2.xml
		// file under the SubDirSearch xmlelement tree (for example, procs, *.dvt, etc)
		private bool ProcessSubDir(XmlElement subtop, string dri, string dname, int plntsecindx)
		{
			XmlNodeList nodeList;
			string xp = "descendant::ProcSet[Dir = \'" + dname + "\']";
			try
			{
				nodeList = subtop.SelectNodes(xp);
				// for each possible subdirectory for this directory, see if it
				// exists.
				foreach (XmlNode subdirname in nodeList)
				{
					XmlElement subdele=(XmlElement)subdirname;
					XmlNodeList FnodeList = subdirname.SelectNodes("ProcFile");
					// see if the files listed exist, if so, add them to the
					// plant procset list.
					foreach (XmlNode dirnd in FnodeList)
					{
						XmlElement dele=(XmlElement)dirnd;
						string dirpath = _Location + "\\" + dri;
						if (File.Exists(dirpath+"\\"+dirnd.InnerText))
						{
							XmlElement nm = (XmlElement) subdirname.SelectSingleNode("MenuName");							
							string subdir = dirnd.InnerText.Substring(0,dirnd.InnerText.LastIndexOf("\\"));
							long flags=0L;
							bool hasSec = usrRunTime.sec.ProcSetHasSecurity(plntsecindx, dri+"\\"+subdir);
							if (this.usrRunTime.sec==null) flags=Security.SUPERACCESS;
							else if (hasSec) flags = usrRunTime.sec.GetProcSecurity(dirpath + "\\" + subdir);
						
							if((hasSec && flags!=0L) || (!hasSec&&!usrRunTime.sec.BlockAccess()))
								CreateProcSet(dirpath+"\\"+subdir, nm, dri, flags);
						}
						break;   // out of file check foreach
					}
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message);
				return false;
			}
			return true;
		}
		// Using the menuwin2.xml file, read in child list by seeing which
		// directories (proc sets) are contained in this vexxx directory.
		public override bool Read(bool ChkForChldOnly)
		{
			bool didone;
			Cursor.Current = Cursors.WaitCursor;
			// using the . Then do exist tests to see if they 
			// exist on this datapath.
			usrRunTime.LoadMenuWin2();
			XmlDocument xmldoc = usrRunTime.menuwin2XML.GetXmlDoc();
			XmlElement top = (XmlElement) xmldoc.FirstChild; 
			XmlElement sys = (XmlElement) top.SelectSingleNode("PlantAttach");
			XmlElement subtop = (XmlElement) top.SelectSingleNode("SubDirSearch");
			
			DirectoryInfo cur = new DirectoryInfo(_Location);
			DirectoryInfo[] diArr = cur.GetDirectories();
			int plntsecindx = usrRunTime.sec.GetPlantSecurityIndex(_Location);
			foreach (DirectoryInfo dri in diArr)
			{
				XmlNodeList nodeList;
				string dname = null;
				int indx;
				if ((indx=dri.Name.IndexOf("."))>-1)
				{
					//must be *.prc, *.bck, *.dvt, *.sl? 
					if (indx+4<=dri.Name.Length)
					{
						dname = "*." + dri.Name.Substring(indx+1,3).ToLower();
						if (dname.StartsWith("*.sl"))
							dname = dname.Substring(0,4) + "?";
					}
					else
						dname = null;
				}
				else if ((indx=dri.Name.IndexOf("'"))>-1)
				{
					// bug fix B2006-047
					// if not a .PRC folder, then we get an error looking up
					// the directory name in XML (if it has an ' in the name)
					dname = null;
				}
				else
				{
					dname = dri.Name.ToLower();
				}
				if (dname!=null)
				{
					string xp = "descendant::ProcSet[Dir = \'" + dname + "\']";
					try
					{
						nodeList = sys.SelectNodes(xp);
						foreach (XmlNode dirname in nodeList)
						{
							XmlElement dele=(XmlElement)dirname;
							didone = ProcessDirName(dele, dri.Name, plntsecindx);
							if (Children.Count > 0 && ChkForChldOnly) 
							{
								Cursor.Current = Cursors.Default;
								return true;
							}
							// if a directory was found, see if any 
							// subdirectories exist that need added too.
							bool didsub = false;
							if (didone) didsub = ProcessSubDir(subtop, dri.Name, dname, plntsecindx);
						}
					}
					catch (Exception e)
					{
						MessageBox.Show(e.Message.ToString());
						Cursor.Current = Cursors.Default;
						return false;
					}
				}
			}
			// if no children, change icon to empty (unless it has a lock icon)
			if (Children.Count <=0 && Lock.LockStatus == VENetwork.Status.NoLock) icon = iconStates[(int)VEO_IconStates.Empty];
			Cursor.Current = Cursors.Default;
			return true;
		}
		// write out change (modification of title for plant) for plant to
		// the menuwin.xml file
		public override bool Write()
		{
			string newtitle=null;
			if (changeTitle)
			{
				newtitle = tmpTitle;
				bool success = usrRunTime.ModMenuWin(newtitle, this._Title);
				if (success==false)
				{
					MessageBox.Show("Could not modify title for the plant.");
					return false;
				}
				MessageBox.Show("Please send \\ve-proms\\menuwin & \\ve-proms\\menuwin.xml files to Volian.");
				_Title=newtitle;
				changeTitle=false;
			}
			return true;
		}
		// create a new proc set object (child for this plant)
		public override Object MakeNewChild()
		{
			if (!isOpen)Open();
			VEO_ProcSet ps = new VEO_ProcSetN(usrRunTime,null,_Location);
			return ps;	
		}
		// save a new proc set (child for this plant)
		public override bool SaveChild(Object obj)
		{
			VEO_ProcSetN ps = (VEO_ProcSetN) obj;
			
			// check for valid child
			bool valid = ps.IsValid();
			if (valid == false) return false;
			valid = ps.SaveNew("dummy");
			if (valid == true)
			{
				// add this item to this plants list of children.
				Children.Add((VEO_ProcSet)obj);
				ps.parentObj=this;
				ps._Location = this._Location + "\\" + ps._Location;
				ps.icon=iconStates[0];
				// if this plant's icon is 'empty', change it to either lock (if locked),
				// or to not empty.
				icon = iconStates[(int)Lock.LockStatus];
				IsEmpty=false;
			}
			return valid;
		}
		// save a new plant directory
		public override bool SaveNew(string pth)
		{
			// check for non-empty data.
			if (tmpLocation == null || tmpLocation == "" || tmpTitle == null || tmpTitle == "")
			{
				MessageBox.Show("Need to fill in all data.");
				return false;
			}
			// for this new plant, make the directory and write out
			// to the menuwin & menuwin.xml files.
			
			// if using the file browser dialog, the drive may be included, if so
			// check that the directory matches the datapath.
			int slash = tmpLocation.LastIndexOf("\\");
			if (slash>0)
			{
				string drvtmp = tmpLocation.Substring(0,slash+1);
				ShortName sname = new ShortName(drvtmp);
				string drv=sname.ShortFileName;
				sname = null;
				if (drv.ToUpper() != pth.ToUpper()) 
				{
					MessageBox.Show("Invalid path, directory must match datapath");
					return false;
				}
				// remove the drive part of the path, it's added back later.
				tmpLocation = tmpLocation.Substring(slash+1,tmpLocation.Length-(slash+1));
			}
			// check for ve*
			if (tmpLocation.Substring(0,2).ToUpper() != "VE")
			{
				MessageBox.Show("First two characters must be 'VE' for new plant directory name.");
				return false;
			}
			// also check that the name is not greater than 8 characters (an old 16-bit hangover)
			if (tmpLocation.Length>8)
			{
				MessageBox.Show("Directory/plant name must be 8 characters or less.");
				return false;
			}
			// make path to new directory & check for existence.
			string curpath=null;
			curpath = pth + "\\" + this.tmpLocation;
			DirectoryInfo cur = new DirectoryInfo(curpath);
			if (cur.Exists )
			{
				DirectoryInfo[] di = cur.GetDirectories("*");
				FileInfo[] fi = cur.GetFiles("*");
				if (di.Length>0 || fi.Length>0)
				{
					MessageBox.Show("The plant directory you specified already exists & has contents. Enter a different name.");
					return false;
				}
			}
			// if the directory exists & it is already in the menu file, it didn't succeed
			// in adding the new item.
			if (cur.Exists && (usrRunTime.IsInMenuWin(this.tmpLocation)!=0))
			{
				MessageBox.Show("The plant directory you specified already exists and the plant name exists in the menu files.");
				return false;
			}
			try
			{
				if (cur.Exists == false)cur.Create();
			}
			catch (Exception e)
			{
				MessageBox.Show("Error creating directory: " + e.Message);
				return false;
			}
			// now add to end of menuwin & menuwin.xml.
			bool success = usrRunTime.AddToMenuWin(this.tmpTitle,this.tmpLocation);
			if (success==false)
			{
				MessageBox.Show("Could not add directory to ve-proms menuing files. Could not create plant directory.");
				cur.Delete();
				return false;
			}
			MessageBox.Show("The VE-PROMS Security Access Module (VESAM) may need to be used to set permissions to access the new plant.");
			_Title=tmpTitle;
			_Location=tmpLocation;
			changeTitle=false;
			changeLocation=false;
			// set a security flag for this. Cases could be that there is no security in vesam,
			// that it has security in vesam, though it didn't exist for this user until now, 
			// or the user could be super user.
			accessFlags=0L;
			int idx = usrRunTime.sec.GetPlantSecurityIndex(curpath);
			accessFlags = usrRunTime.sec.GetPlantSecurity(idx);
			return true;
		}
		public override void DoListView(ListView veoListView)
		{
			ListViewItem item=null;
			veoListView.Columns.Add("Location", 120, HorizontalAlignment.Left);
			veoListView.Columns.Add("Title", 300, HorizontalAlignment.Left);
			
			for (int i=0; i