/*********************************************************************************************
 * Copyright 2004 - Volian Enterprises, Inc. All rights reserved.
 * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE
 * ------------------------------------------------------------------------------
 * $Workfile: VEObject.cs $     $Revision: 9 $
 * $Author: Jsj $   $Date: 8/30/06 10:04a $
 *
 * $History: VEObject.cs $
 * 
 * *****************  Version 9  *****************
 * User: Jsj          Date: 8/30/06    Time: 10:04a
 * Updated in $/LibSource/VEObject
 * Added Multi Procedure Data Checker
 * 
 * *****************  Version 8  *****************
 * User: Kathy        Date: 6/21/05    Time: 7:27a
 * Updated in $/LibSource/VEObject
 * edit for approve if from procset/procedures node
 * 
 * *****************  Version 7  *****************
 * User: Kathy        Date: 5/11/05    Time: 9:29a
 * Updated in $/LibSource/VEObject
 * approve menu from procedures tree node
 * 
 * *****************  Version 6  *****************
 * User: Kathy        Date: 4/21/05    Time: 10:24a
 * Updated in $/LibSource/VEObject
 * remove upgrade2005 define
 * 
 * *****************  Version 5  *****************
 * User: Kathy        Date: 3/22/05    Time: 10:14a
 * Updated in $/LibSource/VEObject
 * approve: remove revise menu enable
 * 
 * *****************  Version 4  *****************
 * User: Kathy        Date: 2/03/05    Time: 11:20a
 * Updated in $/LibSource/VEObject
 * Change 'DUMMY' node to 'TEMP' node (just in case user sees it)
 * 
 * *****************  Version 3  *****************
 * User: Kathy        Date: 1/31/05    Time: 11:06a
 * Updated in $/LibSource/VEObject
 * Fix B2005-005 (connection & delete directory errors). also, fix icon
 * 
 * *****************  Version 2  *****************
 * User: Kathy        Date: 10/25/04   Time: 10:24a
 * Updated in $/LibSource/VEObject
 * Fix B2004-049
 * 
 * *****************  Version 1  *****************
 * User: Kathy        Date: 7/27/04    Time: 8:53a
 * Created in $/LibSource/VEObject
 *********************************************************************************************/
using System;
using System.Text;
using System.Collections;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Utils;
using VENetwork;
using VEMessageNS;
namespace VEObject
{
	/// 
	/// VEObject namespace provides classes for the Volian Enterprises Objects DataRoot, DataPath,
	/// Plant, Procedure Set, Procedures, Library Documents and Archives.
	/// 
	/// This file has the base class, VEO_Base, which all other objects inherits from.
	/// 
	public enum VEObjectTypesDefs
	{
		Generic = 0, ProcedureSet = 1, Procedure = 2, Archive = 3, LibraryDoc =4, System = 5, Plant = 6, DummySet = 7
	};
	public enum VEO_IconStates
	{
		Normal=0, LckByMe=1, LckByOther=2, LckPending=3, Empty=4
	};
	public enum ProcTypeOptions
	{
//		Standard=0, Background=1, Deviation=2, Slave=3
		Standard=0, Slave=1, Background=2, Deviation=3
	};
	public class VEO_Base
	{
		public int [] iconStates;
		public int icon;
		public string _Title;
		public string _Location;
		public ArrayList Children;
		public Object parentObj;
		public UserRunTime usrRunTime;
		public bool isOpen;
		private bool Expanded;
		public bool IsEmpty;
		public int VEObjectType;
		public bool isNew;
		public bool AllowListViewSort;
		public VELock Lock;
		public VEConnection Connection;
		public TreeNode treeNode;
		public enum ProcColumnOptions
		{
			OneColumn=0, TwoColumn=1, ThreeColumn=2
		};
		public enum ArchiveTypeOptions
		{
			Full=0, Partial=1
		};
		// constructor
		public VEO_Base()
		{
			Expanded = false;
			isOpen = false;
			AllowListViewSort = false;
			Children = new ArrayList();
			VEObjectType = (int)VEObjectTypesDefs.Generic;
		}
		~ VEO_Base()
		{
			if (Connection!=null) Connection.Exit();
		}
		public virtual void LoadLockInfo()
		{
			Lock = new VELock(_Location, usrRunTime==null?null:usrRunTime.myUserData, VENetwork.LockTypes.None);
		}
		public virtual bool mnuAllowDelete()
		{
			return true;
		}
		public virtual bool mnuAllowApprove()
		{
			return false;
		}
		public virtual bool mnuAllowApproveSel()
		{
			return false;
		}
		
		public virtual bool mnuAllowApproveAll()
		{
			return false;
		}
		public virtual bool mnuAllowProperties()
		{
			return true;
		}
		public virtual bool mnuAllowNew()
		{
			return true;
		}
		public virtual bool mnuAllowUpdRO()
		{
			return false;
		}
		
		public virtual bool mnuAllowClean()
		{
			return false;
		}
		public virtual bool mnuAllowDataCheck()
		{
			return false;
		}
		public virtual bool mnuAllowChgDef()
		{
			return false;
		}
		public virtual bool mnuAllowUpdateArch()
		{
			return false;
		}
		public virtual bool mnuAllowTestArchive()
		{
			return false;
		}
		public virtual bool mnuAllowExtractArch()
		{
			return false;
		}
		public virtual bool mnuAllowLckDB()
		{
			return false;
		}
		public virtual bool mnuAllowUnlckDB()
		{
			return false;
		}
		public virtual bool mnuAllowMonUsr()
		{
			if (usrRunTime.InMultiUserMode && !(Lock.LockType==VENetwork.LockTypes.None) && 
				!(Lock.LockStatus==VENetwork.Status.LockedByOther)) return true;
			return false;
		}
		public virtual bool canEdit()
		{
			return true;
		}
		// used to flag whether all siblings of a node should be collapsed before expanding the
		// node. This is needed for multi-user support for the plant & procedure set levels so
		// that a user does not have open connections all over the tree.
		public virtual bool CollapseSibling()
		{
			return false;
		}
		public virtual bool amILockedByMe()
		{
			// if not a newtork serial number, treat it as anything is locked.
			if (!usrRunTime.InMultiUserMode)return true;
			VEO_Base obj = this;
			while (obj!=null)
			{
				if (obj.Lock.LockStatus == VENetwork.Status.Locked) return true;
				obj = (VEO_Base) obj.parentObj;
			}
			return false;
		}
		public virtual bool isProcSet()
		{
			return false;
		}
		public virtual bool isArchiveSet()
		{
			return false;
		}
		public virtual bool updateROValues()
		{
			return false;
		}
		public virtual void CleanTransitionsAndROsUsages(int clntype,string ProcName) 
		{
			return;
		}
		public virtual void ApproveProcedures(int ApproveType, ArrayList prcs)
		{
			return;
		}
		public virtual void DataIntegrityCheck(string ProcName) 
		{
			return;
		}
		// Add TreeNodes to the input TreeNode parent. 
		public virtual void AddToTree(TreeNode parentnd)
		{
			for(int i=0; i0) veo.Children.Clear();
				if (nd.Nodes.Count>0) nd.Nodes.Clear();
				veo.Read(false);
				veo.AddToTree(nd);		
			}
			// if the object can have a lock at this level, but it currently doesnot
			// have a lock, if it has children add a temp node to the treeview. This
			// is done so that if this becomes expanded, the lock is checked again.
			else if (veo.Lock.LockStatus!=VENetwork.Status.LockedByOther)
			{
				veo.Read(true);
				if (veo.Children.Count>0)
				{
					veo.Children.Clear();
					TreeNode dmy = new TreeNode("TEMP");
					nd.Nodes.Add(dmy);
				}	
			}
			// if multi-user serial number, may have to reset lock status icon.
			if (usrRunTime.InMultiUserMode)
			{   
				// if no lock & emtpy, show empty. Otherwise, show lock icon
				if (IsEmpty && veo.Lock.LockStatus==VENetwork.Status.NoLock)
					veo.icon = veo.iconStates[(int)VEO_IconStates.Empty];
				else
					veo.icon = veo.iconStates[(int)veo.Lock.LockStatus];
				nd.ImageIndex = veo.icon;
				nd.SelectedImageIndex = veo.icon;
			}
		}
		
		// When the user collapses the treenode, also, do some object cleanup.
		// This removes children, exits connections  for all sub-tree children
		// and also refreshes locks, updates icons based on lock changes and
		// load in children at the current node level.
		public void Collapse(TreeNode nd, TreeViewAction act, bool dorefresh,bool closeconn)
		{
			Expanded=false;
			Children.Clear();			
			if (Connection != null && closeconn) Connection.Exit();
			// if this is the actual node that was collapsed, then refresh at this level, 
			// i.e. refresh the lock, update the icon (in case of lock status change)
			// and load children (may make into temp child if necessary)
			if (act == TreeViewAction.Collapse||dorefresh) {
				RefreshTreeNode(nd);
				// need to exit again because refresh tree node opens the connection
				// to determine locking status.
				if (Connection != null && closeconn) Connection.Exit();
			}
		}
		// Expand the TreeNode. This makes a new/reenters connection at the level, sets up
		// icons for the level, enters it by loading its children (if it successfully
		// entered it, i.e. it's not locked.
		public virtual bool Expand(TreeNode mytreend)
		{
			if (Connection==null)
				Connection = new VEConnection(Lock, usrRunTime);
			// Enter the connection, i.e. refresh the lock & then see if can get into the
			// data level.
			bool canenter = Connection.Enter(false);
			// update icons (in case of lock status change)
			if (usrRunTime.InMultiUserMode)
			{
				// if no lock & emtpy, show empty. Otherwise, show lock icon
				if (IsEmpty && Lock.LockStatus==VENetwork.Status.NoLock)
					icon = iconStates[(int)VEO_IconStates.Empty];
				else
					icon = iconStates[(int)Lock.LockStatus];
				mytreend.ImageIndex = icon;
				mytreend.SelectedImageIndex = icon;
			}
			// if user cannot enter, it must be locked. Return a false.
			if (canenter==false) 
			{
				Collapse(mytreend,TreeViewAction.Unknown,true,false);
				mytreend.Nodes.Clear();
				mytreend.TreeView.Refresh();
				return false;
			}
			if (Expanded) return true;
			Expanded = true;
			// load the children. First check for either no children (in case this was locked on
			// load, and then subsequently unlocked before the selection or check for a 'TEMP' 
			// node. If it exists, remove it. Then if this has a lock type of none, load it's 
			// children. Otherwise, load a temp child under each child. The temp child is
			// used because when the user actually expands this node, a recheck of the lock
			// status is needed and a change may have occurred (another user may have added, 
			// deleted or modified children by the time this is entered).
			TreeNode frstchld = mytreend.FirstNode;
			if (frstchld==null || frstchld.Text == "TEMP")
			{
				mytreend.Nodes.Clear();
				Read(false);
				AddToTree(mytreend);
			}
			VEO_Base veo;
			for (int i=0; i0)
					{
						veo.Children.Clear();
						TreeNode dmy = new TreeNode("TEMP");
						mytreend.Nodes[i].Nodes.Add(dmy);
					}	
				}
			}
			return true;
		}
		public virtual void LockedDlg()
		{
		}
		// virtual read
		public virtual bool Read(bool dummy)
		{
			return true;
		}
		
		// virtual write
		public virtual bool Write()
		{
			return false;
		}
		public virtual bool Open()
		{
			return false;
		}
		
		public virtual bool Close()
		{
			return false;
		}
		// virtual cancel write (save)
		public virtual void CancelWrite()
		{
		}
		public virtual bool Delete()
		{
			return true;
		}
		// make a new plain vanilla object (this is really a virtual.
		public virtual Object MakeNewChild()
		{
			return (new Object());
		}
		public virtual Object Copy()
		{
			return this;			//default to returning self
		}
		public virtual Object AddNewChild()
		{
			VEO_Base newobj =  (VEO_Base) this.MakeNewChild();
			if (newobj==null) return null;  // something happened, just return
			newobj.parentObj = (VEO_Base) this;
			newobj.isNew=true;
			bool prpchg = newobj.PropertiesDlg(this);
			if (prpchg == true)
			{
				// Copy is used so that a VEO-object is created that has editable
				// (versus new) properties. When the object is 'new' many fields
				// are editable that should not be editable when not new (this is
				// mainly an issue with the property grid, since the ReadOnly
				// setting can only be done at compile/build time - it CANNOT be
				// done at run-time. So when a new object is created, if it's 
				// successful, it's copied to an object which is edit (not new)
				// for the treeview & any further operations.
				VEO_Base cpyobj = (VEO_Base) newobj.Copy();
				Children.Add(cpyobj);    // add the new one to the children
				if(newobj!=cpyobj)Children.Remove(newobj);
				cpyobj.Read(false);			 // read any child data
				return cpyobj;
			}
			else
				return null;
		}
		public virtual string GetTreeNodeText()
		{
			return _Title;
		}
		public virtual bool SaveChild(Object obj)
		{
			return false;
		}
		// virtual SaveNew
		public virtual bool SaveNew(string pth)
		{
			return false;
		}
		
		public virtual void DoListView(ListView veoListView)
		{
			return;
		}
		public virtual void DoWizzard()
		{
			return;
		}
		// Comunication between this object & vfw
		struct COPYDATASTRUCT
		{
			public IntPtr dwData;
			public int cbData;
			public IntPtr lpData;
		}
		const int WM_COPYDATA = 0x004A;
		// sending side
		[DllImport("user32.dll")]
		static extern bool SendMessage(  
			IntPtr hWnd,
			UInt32 Msg,
			UInt32 wParam,
			ref COPYDATASTRUCT lParam);
		public struct RqstAttachMsg 
		{
			public Int16 level;
			public Int16 flag;
			public Int32 longval;
		}
		public struct AnswerMsg 
		{
			public Int16 done;
			public Int16 intAnswer;
			public Int32 longAnswer;
		}
		
		public struct PRecordMsg 
		{
			public Int16 done;
			public Int16 cnt;
			[MarshalAs(UnmanagedType.ByValTStr,SizeConst=140)] public String buff;
		}
		[StructLayout(LayoutKind.Sequential)]
			public struct PosUpdateStr
		{
			[MarshalAs(UnmanagedType.ByValTStr,SizeConst=16)] public String sender;
			[MarshalAs(UnmanagedType.ByValTStr,SizeConst=80)] public String directory;			
			public Int32 docnumindx;
			public Int32 recID;
		}
		private VEO_Base GetObjectAtLevel(int level)
		{
			try
			{
				VEO_Base veo = this;
				while (veo != null)
				{
					if (level == 2 && veo.VEObjectType == (int)VEObjectTypesDefs.ProcedureSet)
						return veo;
					else if (level == 1 && veo.VEObjectType == (int)VEObjectTypesDefs.Plant)
						return veo;
					else if (level == 0 && veo.VEObjectType == (int)VEObjectTypesDefs.System)
						return veo;
					veo = (VEO_Base)veo.parentObj;
				}
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message.ToString());
			}
			return null;
		}
		private bool ReturnProcRecord(VEO_Base veo,Int16 size, IntPtr vfwhndl)
		{
			bool success=true;
			COPYDATASTRUCT cds = new COPYDATASTRUCT();
			PRecordMsg prmsg;
			prmsg.done = 1;
			byte [] bt = new byte[size+4];
			prmsg.cnt = veo.Connection.GetProcRecBuff(size, ref bt);
			if (prmsg.cnt != size)
			{
				MessageBox.Show("Could not read multi-user connection information");
				return false;
			}
			prmsg.buff = Encoding.ASCII.GetString(bt,0,size);
			IntPtr p = Marshal.AllocHGlobal(size+4);
			Marshal.StructureToPtr(prmsg,p,true);
			cds.dwData = (IntPtr)VEMessageNS.MessageOps.GETPROCESSREC;
			// the following would be 'size+4', however marshalling requires space to be
			// a multiple of 8
			cds.cbData = 160;
			cds.lpData = p;
			try
			{
				SendMessage(vfwhndl,WM_COPYDATA,(UInt32)8, ref cds);
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message.ToString());
				success=false;
			}
			Marshal.FreeHGlobal(p);
			return success;
		}
		private bool Reply(Int16 done, Int16 intAnswer, Int32 longAnswer, IntPtr vfwhndl)
		{
			bool success=true;
			COPYDATASTRUCT cds = new COPYDATASTRUCT();
			AnswerMsg ans;
			ans.done = done;
			ans.intAnswer = intAnswer;
			ans.longAnswer = longAnswer;
			
			IntPtr p=Marshal.AllocHGlobal(8);
			Marshal.StructureToPtr(ans,p,true);
						
			cds.dwData = (IntPtr)VEMessageNS.MessageOps.GETANSWER;
			cds.cbData = 8;
			cds.lpData = p;
			try
			{
				SendMessage(vfwhndl,WM_COPYDATA,(UInt32)8, ref cds);
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message.ToString());
				success=false;
			}
			Marshal.FreeHGlobal(p);
			return success;
		}
		public virtual void ProcessMessage(Message m, IntPtr vfwhndl)
		{			
			VEO_Base veo = null;
			bool success=false;
			if (m.Msg==(int)VEMessageNS.MessageOps.POSQUERY)
			{
				// POSQUERY is only called to get initial procedure - make the main
				// form invisible when this request is made. Wait until now so that
				// the main form is visible until vfw window is just about to be
				// made visible.
				IntPtr handle = 
					System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
				Form mainForm = (Form)Form.FromHandle(handle);
				mainForm.Visible = false;
				COPYDATASTRUCT cds = new COPYDATASTRUCT();
				PosUpdateStr pu;						
				pu.sender = "NEWBROWSER";
				VEO_Proc prc = null;
				if (!(this is VEO_Proc))
				{
					// get to a proc, this is either a set or 'procedures' node
					// from an approve selected.
					VEO_ProcSet ps = null;
					if (this is VEO_ProcSet)
						ps = (VEO_ProcSet) this;
					else if (this is VEO_DummySet)
						ps = (VEO_ProcSet) this.parentObj;
					else
					{
						MessageBox.Show("Error finding procedure for edit","VE-PROMS");
						return;
					}
					prc=ps.AppSelForEdit;
				}
				else
					prc = (VEO_Proc) this;
				VEO_DummySet ds = (VEO_DummySet) prc.parentObj;
				pu.directory = ds._Location;
				pu.docnumindx = ds.Children.IndexOf(prc);
				pu.recID = System.Convert.ToInt32(prc.recid,10);
				IntPtr p=Marshal.AllocHGlobal(130);
				Marshal.StructureToPtr(pu,p,true);
						
				cds.dwData = (IntPtr)VEMessageNS.MessageOps.POSUPDATE;
				cds.cbData = 130;
				cds.lpData = p;
				try
				{
					SendMessage(vfwhndl,WM_COPYDATA,(UInt32)8, ref cds);
				}
				catch (Exception e)
				{
					MessageBox.Show(e.Message.ToString());
				}
				Marshal.FreeHGlobal(p);
			}
			else if (m.Msg==WM_COPYDATA)
			{
				bool reply = true;
				Int16 int1=0, int2=0;
				int int3=0;
				int lvl = 0;
				COPYDATASTRUCT cds = (COPYDATASTRUCT) m.GetLParam(typeof(COPYDATASTRUCT));
				int tmpmsg = (int)cds.dwData;
				VEMessageNS.MessageOps msg = (VEMessageNS.MessageOps) tmpmsg;				
				RqstAttachMsg ramsg = (RqstAttachMsg)Marshal.PtrToStructure(cds.lpData,typeof(RqstAttachMsg));
				// get level veobject associated with this request, i.e. system, plant, or procset
				// and then connect at level
				lvl = ramsg.level;
				try
				{
					veo = GetObjectAtLevel(lvl);
					switch (msg)
					{
						case VEMessageNS.MessageOps.RQSTATTACH:
							success = veo.Connection.Enter(false);
							int2 = (short)(success?1:0);						
							break;
						case VEMessageNS.MessageOps.RQSTFILEOFFSET:
							if (ramsg.flag==0)
								int3 = (int)veo.Connection.FileOffset;
							//else
							//	MessageBox.Show("Set file offset" + ramsg.longval.ToString());
							break;
						case VEMessageNS.MessageOps.RQSTFILEMODE:
							int2 = (short) veo.Connection.GetVfwMode();
							break;
						case VEMessageNS.MessageOps.RQSTFILEOWNER:
							int2 = 1;
							break;
						case VEMessageNS.MessageOps.RQSTFILESEEK:
							int3 = (int)veo.Connection.Seek(ramsg.longval, ramsg.flag);
							break;
						case VEMessageNS.MessageOps.RQSTFILECLOSE:	//used in lock/unlock
							veo.Connection.Close();
							break;
						case VEMessageNS.MessageOps.RQSTFILEOPEN:	//used in lock/unlock
							int2 = (short) veo.Connection.Open(ramsg.flag);
							break;
							// RQSTFILEREAD - not used in vfw <-> browser communication
						case VEMessageNS.MessageOps.RQSTFILEREAD:		
							MessageBox.Show("RqstFileRead"); 
							break;
						case VEMessageNS.MessageOps.RQSTFILEWRITE:	//used in lock/unlock
							MessageBox.Show("RqstFileWrite");
							break;
						case VEMessageNS.MessageOps.RQSTPROCESSRECORD:
							reply=false;
							success=ReturnProcRecord(veo,ramsg.flag,vfwhndl);
							break;
						case VEMessageNS.MessageOps.SETLOCKBYUSER:	//do set lock yet.
							if (ramsg.flag >= 0)
							{
								if (ramsg.flag==0)
									veo.Lock.LockStatus=VENetwork.Status.NoLock;
								else
									veo.Lock.LockStatus=VENetwork.Status.Locked;
							}
							int2 = (Int16)((veo.Lock.LockStatus==VENetwork.Status.Locked)?1:0);
							break;
					}
					if (reply) success = Reply(int1,int2,int3,vfwhndl);
				}
				catch (Exception e)
				{
					MessageBox.Show(e.Message);
				}
			}
		}
	}
}