using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.IO;
using System.Net;
using ICSharpCode.SharpZipLib.Zip;
using ICSharpCode.SharpZipLib.Checksums;

namespace testEditor
{
	/// <summary>
	/// Summary description for WebForm1.
	/// </summary>
	public class LOedit : System.Web.UI.Page
	{
		protected System.Web.UI.HtmlControls.HtmlInputButton Submit1;
		protected System.Web.UI.WebControls.Button BtnSave;
		protected FredCK.FCKeditorV2.FCKeditor FCKeditor1;
		protected System.Web.UI.WebControls.Label lblMsg;
		protected System.Web.UI.WebControls.Label lblPage;
		protected System.Web.UI.WebControls.Button BtnCancel;
		protected System.Web.UI.WebControls.Literal LiteralIfrContent;
		protected System.Web.UI.WebControls.TextBox TextBoxSummary;
		protected System.Web.UI.WebControls.Label Label1;
		protected System.Web.UI.HtmlControls.HtmlGenericControl divSaveOption;
		protected System.Web.UI.WebControls.RequiredFieldValidator RFValidatorSum;
		protected System.Web.UI.WebControls.ValidationSummary ValidationSummary1;
		protected System.Web.UI.WebControls.RadioButton RadioBtnMinor;
		protected System.Web.UI.WebControls.RadioButton RadioBtnMajor;
		protected System.Web.UI.WebControls.CustomValidator CustomValidator1;
		protected System.Web.UI.WebControls.Literal Literal1;
		protected System.Web.UI.WebControls.ImageButton ImgBtnLock;
		protected System.Web.UI.WebControls.HyperLink Hlmeta;
		protected System.Web.UI.WebControls.Label Lblmeta;
		protected System.Web.UI.WebControls.HyperLink Hlmetanew;
		protected testEditor.webheader ucHeader;

		
		private void Page_Load(object sender, System.EventArgs e)
		{
			if (Session["relativePath"] != null)
				Trace.Write("Show LO: Session path", Session["relativePath"].ToString());

			// Put user code to initialize the page here			
			if(!IsPostBack)
			{	
				//customised config file to overwrite the settings in fckconfig.js
			    FCKeditor1.CustomConfigurationsPath = FCKeditor1.BasePath + "myconfig.js";			
			
				//initilize the button with a javascript function
				/* this is not needed now, as i use window.onunload javascript instead to 
				 * react to all actions tryiing to leave the window without saving changes
				 * a bug remains concerning isDirty() value in IE
				BtnCancel.Attributes.Remove("onclick");
		    	BtnCancel.Attributes.Add("onclick", "return CheckIsChanged();");	//return is important here
				*/

				//to avoid the msg box pop up for saving (submit)button as others 
				BtnSave.Attributes.Remove("onclick");
				BtnSave.Attributes.Add("onclick", "return warningMeta();");

				ImgBtnLock.Attributes.Remove("onclick");
				ImgBtnLock.Attributes.Add("onclick","unsetValue();");

				//>>>>>>>>>>>>>>>>>>>>>>>>
				//BtnMetaEdit.Attributes.Remove("onclick");
				//BtnMetaEdit.Attributes.Add("onclick", "unsetValue();");
                //>>>>>>>>>>>>>>>>>>>>>
		
				ucHeader.pageTitle = "Learning Object Editing Toolkit";
		
				FCKeditor1.Value = "Please enter your Learning Object content here";

				if (Request["title"] == null || Request["action"] == null) 
				{
					Response.Redirect("default.aspx");
				} 
				else 
				{
					
					// show editor for viewing existing LO
					string loTitle = Request["title"];
					string loAct = Request["action"];

                    getVariantNum(loTitle);   //set the session with variant number

					//this is for the case when old variants are shown
					string variantNum = null;
					if (Request["var"] != null)
						variantNum = Request["var"];

					Session["lotitle"] = loTitle;  //set the session to be used in variants page
					
					showLO(loTitle,loAct,variantNum);
					
				}


                //try to set broswer path dynamiclly
				/*  but it works differently on IE and firefox. In IE, it wouldn't catch the Session
				 * "userfilepath"], only the configuration in web.config. So it either get value from 
				 * web.config, or the DEFAULT "/UserFiles/" if nothing set in web.config. Therefore the 
				 * current solution is to set userfilepath in web.config (not dynamic), but copy image/flash
				 * later to the LO folder from the root image/flash folder. Need to change FCK.net dll to 
				 * allow uploader to save files to image/flash folder instead of root userfile folder.
				 * 
				string userfilePath = (string)System.Configuration.ConfigurationSettings.AppSettings["FCKeditor:UserFilesPath"];
				if (Session["relativePath"] != null)
				{
					string relpath = Session["relativePath"].ToString();
					if (relpath.IndexOf("\\") >= 0)
					{
						relpath = relpath.Replace("\\","/");
					}

					Session["FCKeditor:UserFilesPath"] = userfilePath + Request["title"]+ relpath;
				}
				else
				{
					Session["FCKeditor:UserFilesPath"] = userfilePath + Request["title"];
				}
				*/

			}             		
			
		}

		
		#region Web Form Designer generated code
		override protected void OnInit(EventArgs e)
		{
			//
			// CODEGEN: This call is required by the ASP.NET Web Form Designer.
			//
			InitializeComponent();
			//test for dynamicaly changing file path
			//Session["FCKeditor:UserFilesPath"] = "/testEditor/UserFiles/" + Request["title"];
			base.OnInit(e);
		}
		
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{    
			this.BtnSave.Click += new System.EventHandler(this.BtnSave_Click);
			this.BtnCancel.Click += new System.EventHandler(this.BtnCancel_Click);
			this.ImgBtnLock.Click += new System.Web.UI.ImageClickEventHandler(this.ImgBtnLock_Click);
			this.Load += new System.EventHandler(this.Page_Load);

		}
		#endregion


		protected void ServerValidation(object source, ServerValidateEventArgs args)
		{
			try
			{
				if (!RadioBtnMinor.Checked && !RadioBtnMajor.Checked)
					args.IsValid = false;
				else
					args.IsValid = true;

			}
			catch(Exception ex)
			{
				args.IsValid = false;
			}
		}

		private void BtnSave_Click(object sender, System.EventArgs e)
		{

			if (!Page.IsValid)
				return;

			Trace.Write("Button Save click:", "start saving...");
			string LOname = null;
			string fpath = null;
            string upath = "";

			string copyPath = "";  //just for copyfile purpose in replacehtml

			// action = new is obsolete. it is conducted by importPage now
			/*
			if(Request["action"] == "new")
			{
				LOname = TextBoxLO.Text;
				fpath = Request.PhysicalApplicationPath + "UserFiles\\" + LOname;
				if (!Directory.Exists(fpath))
				{
					Directory.CreateDirectory(fpath);
				}
			}
			else
			{
				LOname = Request["title"];
				fpath = Request.PhysicalApplicationPath + "UserFiles\\" + LOname;
			}
			*/

			LOname = Request["title"];
			if (Request["title"] != null)
                Trace.Write("Button Save click:Request value", Request["title"]);
			fpath = Request.PhysicalApplicationPath + "UserFiles\\" + LOname;
			
			if (Request["var"]!= null)
			{
				upath = Request.Path.Substring(0,Request.Path.LastIndexOf("/")+1);
				upath += "variants/" + LOname + "/templo/" + LOname + "_v" + Request["var"];
				copyPath = (string)System.Configuration.ConfigurationSettings.AppSettings["FCKeditor:UserFilesPath"]+ LOname;
			}
			else
			{
				upath = (string)System.Configuration.ConfigurationSettings.AppSettings["FCKeditor:UserFilesPath"]+ LOname;
			}

			if (Session["relativePath"] != null)
                Trace.Write("Button Save click: Session Relpath", Session["relativePath"].ToString());

			if (Session["relativePath"] != null)
			{
				fpath = fpath + Session["relativePath"].ToString();
				
				string relpath = Session["relativePath"].ToString();
				if (relpath.IndexOf("\\") >= 0)
				{
					relpath = relpath.Replace("\\","/");
				}
				upath = upath + relpath + "/";

				copyPath = copyPath + relpath + "/";
				
			}
			else
			{
				upath = upath + "/";

				copyPath = copyPath + "/";
				
			}
			
			string fname = "";

			if (File.Exists(fpath + "\\index.html"))
			{
				fname = fpath + "\\index.html";
			}
			else if (File.Exists(fpath + "\\index.htm"))
			{
				fname = fpath + "\\index.htm";
			}

			Trace.Write("Button Save:file path", fpath);
			Trace.Write("Button Save:file name", fname);
			
			/* to work out the local server directory path, as the server.mappath is not accessible from
			 * replaceHtml.cs file */
			string userfileDir = upath.Replace("/", "\\");
			string serverDirRoot = Server.MapPath(upath);

			Trace.Write("Button Save click: value of userfileDir", userfileDir);
			Trace.Write("Button Save click: value of serverDirRoot", serverDirRoot);

			//indexof function case sensitive so need to deal with that
			int idx1 = serverDirRoot.ToLower().IndexOf(userfileDir.ToLower());

			serverDirRoot = serverDirRoot.Substring(0, idx1); // no ending "\"
			
            string source = FCKeditor1.Value;

			if (Request["var"] == null)
			{
				copyPath = upath;
			}
			source = replaceHtml.getStdLOHtml(source,upath,serverDirRoot,copyPath);

			//Response.Write(TextBoxSummary.Text);
		
			StreamWriter swriter = null;
			//FileStream fs = null;
			Trace.Write("Button Save:filepath", fname);
			if (!File.Exists(fname))   //in the case that the file path is wrong
			{
				Response.Write("The LO file path is wrong " + fname);
				return;
			}
			try
			{
				//fs = new FileStream(path1,FileMode.Create,FileAccess.Write);
				swriter = new StreamWriter(fname,false);
				swriter.Write(source);
			}
			catch(IOException ex)
			{
				throw ex;
			}
			finally
			{
			    if (swriter != null)
                    swriter.Close();
			}


			//%%%%%%%%%%%%% this must be donce before the zip file is created, otherwise the lock file 
			//will be inside zip, which is not what we want
			//remove the lock file for this LO
			string lockfile = Request.PhysicalApplicationPath + "UserFiles\\" + Request["title"] + "\\currentlo.lock";			
			if (File.Exists(lockfile))
			{
				File.Delete(lockfile);
			}


			//TODO*****next..zip the whole folder and move it to variant dir
			//and assign a version number. and save changs in a log file
			saveAsOldVariant(LOname);

			if (Session["varNum"] != null)
                Trace.Write("Button Save click:Session var", Session["varNum"].ToString());

			//upadate the log file with new version and edit summary etc. 
			string changeType = "";
			if (RadioBtnMinor.Checked)
			{
				changeType = "minor";
			}
			else if (RadioBtnMajor.Checked)
			{
				changeType = "major";
			}
			addLogEntry(LOname,TextBoxSummary.Text,changeType);

			Trace.Write("Button Save click:","save ending...");
			Response.Redirect("LOedit.aspx?title=" + Request["title"] + "&action=view"); 			
		
		}


		/* locate the index.html file in the directory which is the main
		 * entry file for hte lo, the suffix is either html or htm
		 *  */
		private string locateIndex(string loPath)
		{
			string idxPath = null;
			if (Directory.Exists(loPath))
			{
				string[] files = Directory.GetFiles(loPath);
				foreach (string filenm in files)
				{
					string fn = Path.GetFileName(filenm);
					if (fn == "index.htm" || fn == "index.html")
					{
						idxPath = loPath;
						return idxPath;
					}
				}
				string[] dirs = Directory.GetDirectories(loPath);
				foreach (string dir in dirs)
				{
					string idir = Path.GetFileName(dir);

					idxPath = locateIndex(loPath + "\\" + idir);
					if (idxPath != null)  //when it get the index file from foreeach file loop
					{
						return idxPath;		
					}
				}
			}
			return idxPath;  //for compile purpose, but not necessary...
		}



		/* use this to locate the xml metadata file. */
		private string locateMeta(string mPath)
		{
			string metaPath = null;
			if (Directory.Exists(mPath))
			{
				string[] files = Directory.GetFiles(mPath);
				foreach (string filenm in files)
				{
					string fn = Path.GetFileName(filenm);
					if (fn == "imsmanifest.xml")
					{
						metaPath = mPath;
						return metaPath;
					}
				}
				string[] dirs = Directory.GetDirectories(mPath);
				foreach (string dir in dirs)
				{
					string idir = Path.GetFileName(dir);

					metaPath = locateMeta(mPath + "\\" + idir);
					if (metaPath != null)  //when it get the index file from foreeach file loop
					{
						return metaPath;		
					}
				}
			}
			return metaPath;  //for compile purpose, but not necessary...
		}



		private void showLO(string title,string action,string varNum)
		{
			string loRootPath = "";
			if (varNum != null)
			{
				loRootPath = Request.PhysicalApplicationPath + "variants\\" + title + "\\templo\\"; 
				if (!Directory.Exists(loRootPath))
				{
					Directory.CreateDirectory(loRootPath);
				}
				loRootPath += title + "_v" + varNum;
				//create the next layer again
				if (!Directory.Exists(loRootPath))
				{
					Directory.CreateDirectory(loRootPath);
				}
				string zipfile = Request.PhysicalApplicationPath + "variants\\" + title + "\\" + title + "_v" + varNum + ".zip";
				//unzip the old variant to a folder templo
				utilities.unzipLOPackage(zipfile,loRootPath);
			}
			else
			{
				loRootPath = Request.PhysicalApplicationPath + "UserFiles\\" + title;
			}
			//probe for index file
			string lopath = locateIndex(loRootPath);

			//>>>>>>>>>>>>>>>>>>>>>>>>
			//set the path for getting metadata file
			string metaFile = locateMeta(loRootPath);
			if (!Directory.Exists(loRootPath))
			{
				lblMsg.Visible = true;
				lblMsg.Text = "The Learning Object does not exist or some files are missing!";
				FCKeditor1.Visible = false;
				divSaveOption.Visible = false;
				return;
			}
			if (metaFile == null)
			{
				lblMsg.Visible = true;
				lblMsg.Text = "The metadata is missing, you can't edit the metadata!";
				Lblmeta.Visible = false;
				Hlmeta.Visible = false;

			}
			else
			{

				if (metaFile.Equals(loRootPath))
				{
					//in the case the old value still exist in the next session?????
					if (Session["relativeMetaPath"] != null)
						Session.Remove("relativeMetaPath");
				
				}
				else 
				{
					Session["relativeMetaPath"] = metaFile.Substring(loRootPath.Length).Replace("\\", "/"); 
				
				}
				//to be used as the path to read xml file from
				string mReadPath = metaFile.Substring(Request.PhysicalApplicationPath.Length);
				mReadPath = mReadPath.Replace("\\","/");
				//todo: this can be from web config file
				string defaultPath = "//dash/wwwroot/murlloEditing/";
				Session["metaReadPath"] = defaultPath + mReadPath + "/imsmanifest.xml";
				if (Session["relativeMetaPath"] != null)
				{
					Session["metaWritePath"] = defaultPath + "UserFiles/" + title + Session["relativeMetaPath"]+ "/imsmanifest.xml";
				}
				else
				{
					Session["metaWritePath"] = defaultPath + "UserFiles/" + title + "/imsmanifest.xml";;
				}

				string remoteUrl = "http://dash.ecs.soton.ac.uk/metaEditorService/Default.aspx?fpath=" + Session["metaReadPath"];
				remoteUrl += "&freturn=" + Session["metaWritePath"];

				//##### add relation to this version of LO

				//get version with lo name
				string vnum = getVersionNum(title);
				string nameWithVersion = title + "_v" + vnum;
				// get relation
				string relation = "";
				if (RadioBtnMinor.Checked)
				{
					relation = "IsVersionOf";
				}
				else if (RadioBtnMajor.Checked)
				{
					relation = "IsBasedOn";
				}
				string newremote =  "http://dash.ecs.soton.ac.uk/metaEditorServiceNew/Default.aspx?fpath=" + Session["metaReadPath"];
				newremote += "&freturn=" + Session["metaWritePath"] + "&name=" + nameWithVersion + "&relation=" + relation;

				//Hlmetanew.NavigateUrl = newremote;
				//Hlmetanew.Target = "_blank";
				

				//##########


				//BtnMetaEdit.Attributes.Remove("onclick");
				//BtnMetaEdit.Attributes.Add("onclick","unsetValue(); window.open('" + remoteUrl +"', 'metaWindow')");

				//otherwise using link
				Hlmeta.NavigateUrl = remoteUrl;
				Hlmeta.Target = "_blank";
			}
			//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

			if (!Directory.Exists(loRootPath) || lopath == null)
			{				
				lblMsg.Visible = true;
				if (varNum != null)
					lblMsg.Text = "The version " + varNum + " of the LO " + title + " doesn't exist!";
				else
                    lblMsg.Text = "This Learning Object " + title + " doesn't exist!";
				FCKeditor1.Visible = false;
				//%%%%%%%%%%%%%%%%%%%%%%%%% add this
				divSaveOption.Visible = false;
				return;
				//ToDo: do you want to create?
			}
Trace.Write("Show LO: lopath", lopath);
			if (lopath.Equals(loRootPath))
			{
				//in the case the old value still exist in the next session?????
				if (Session["relativePath"] != null)
                    Session.Remove("relativePath");
				Trace.Write("Removing relativePath from Session");
			}
			else 
			{
				Session["relativePath"] = lopath.Substring(loRootPath.Length); 
				Trace.Write("Setting relativePath to "+lopath.Substring(loRootPath.Length));
			}

			string lofile = "";
			if (File.Exists(lopath + "\\index.html"))
			{
				lofile = lopath + "\\index.html";
			}
			else if (File.Exists(lopath + "\\index.htm"))
			{
				lofile = lopath + "\\index.htm";
			}

			if (Request["title"] != null)
                Trace.Write("Show LO: Request value", Request["title"]);
			if (Session["relativePath"] != null)
                Trace.Write("Show LO: Session path", Session["relativePath"].ToString());
				
			if (action == "view")
			{
				viewPage(lofile, title, varNum);
				//BtnSave.Visible = false;
				//BtnCancel.Visible = false;
				divSaveOption.Visible = false;
				
			}
			else if (action == "edit")
			{
				/* to see if the page is being edited by others at the time
				 * it is always at userfile/tilte folder even the one edited is a old variant */
				//%%%%% lock file either at userfile folder or at variant folder ** it should always 
				//at the userfile folder. so any files for the LO including variants can only be edited 				//by one user at one time
				//string filelock = loRootPath + "\\currentlo.lock";
				string filelock = Request.PhysicalApplicationPath + "UserFiles\\" + title + "\\currentlo.lock";
				if (isLocked(filelock))
				{
					lblMsg.Text = "This LO \"" + title + "\" is being edited by somebody else. Please try later.";
					lblMsg.Visible = true;
					FCKeditor1.Visible = false;
					divSaveOption.Visible = false;
					return;
				}
			
				editPage(lofile,title,varNum);
				//BtnSave.Visible = true;
				//BtnCancel.Visible = true;
				divSaveOption.Visible = true;
				
			}				
			
		}


		private bool isLocked(string lockfile)
		{	
			if (!File.Exists(lockfile))
			{
				return false;
			}
			else
			{
				/* use lastmodifed time because sometimes event the lock file 
				 * is deleted and then recreated, the created time is still kept the same 
				 * as it first created. this can be a problem for calculating how long the lock
				 * file has been created */
			    DateTime creationTime = File.GetCreationTime(lockfile);
				DateTime lastModified = File.GetLastWriteTime(lockfile);
				
				TimeSpan tdiff = lastModified.Subtract(creationTime);
				TimeSpan filelife;
				if (tdiff.TotalMinutes > 0)
				{
					filelife = DateTime.Now.Subtract(lastModified);
				}
				else
				{
					filelife = DateTime.Now.Subtract(creationTime);
				}
			
				if (filelife.Minutes >= 15)
				{
					File.Delete(lockfile);
					return false;
				}
				else 
				{
					StreamReader ipReader = null;
					string ipadd = "";
					try 
					{
						ipReader = new StreamReader(lockfile);
						ipadd = ipReader.ReadLine();		
	                    				
					} 
					catch (IOException x) 
					{
						Response.Write(x.ToString());
					}
					finally
					{
						ipReader.Close();
					}

					//if the same user (same ip) edit the lo again (in 15mins)
					if (ipadd.Equals(Request.UserHostAddress))
					{
						return false;
					}
					else
					{					
						return true;
					}
				}
			 }
		 }




		private void editPage(string path,string title, string variant)
		{
			//string upath = (string)System.Configuration.ConfigurationSettings.AppSettings["FCKeditor:UserFilesPath"] + title;

			string upath = "";
			if (variant != null)
			{
				upath = Request.Path.Substring(0,Request.Path.LastIndexOf("/")+1);
				upath += "variants/" + title + "/templo/" + title + "_v" + variant;
			}
			else
			{
				upath = (string)System.Configuration.ConfigurationSettings.AppSettings["FCKeditor:UserFilesPath"] + title;
			}

			if (Session["relativePath"] != null)
			{
				Trace.Write("editPage: relPath", Session["relativePath"].ToString());
				string relpath = Session["relativePath"].ToString();
				if (relpath.IndexOf("\\") >= 0)
				{
					relpath = relpath.Replace("\\","/");
				}
				upath = upath + relpath + "/";
			}
			else
			{
				upath = upath + "/";
			}

			Trace.Write("EditPage:current path", upath);
			
			StreamReader loReader = null;
			try 
			{
				loReader = new StreamReader(path);
				string loContents = loReader.ReadToEnd();

				/*
				//Todo:********modification of replace&search using regex
				//replace src with a differnt path, need to do more....
				int idx = 0;
				while (idx < loContents.LastIndexOf("src="))
				{
					idx = loContents.IndexOf("src=",idx);
					idx += 5;
					loContents = loContents.Insert(idx, upath);
				}
               */

				loContents = replaceHtml.getEditorHtml(loContents, upath);
				
				FCKeditor1.Visible = true;
				FCKeditor1.Value = loContents;			
				
			} 
			catch (IOException x) 
			{
				Response.Write(x.ToString());
			}
			finally
			{
				loReader.Close();
			}


			//set a timer for the length of holding a LO for editing
			setTimer();


			/* set a lock for this page, either on timer for 15mins, or when user release it */
			StreamWriter swriter = null;	
			string lockfile = Request.PhysicalApplicationPath + "UserFiles\\" + title + "\\currentlo.lock";			
			if (File.Exists(lockfile))
			{
				//Not to create the file again if it is existing, in this case a same user wouldn't
				//reacquire another 15mins and he/she only has 15 mins in total even leaves LO editor
				//for a while
				return;
			}
			try
			{				
				swriter = new StreamWriter(lockfile,false);
				swriter.WriteLine(Request.UserHostAddress);
				//set a session obj for the current lock file path
				//is it stored on server side and how about two locks at the same time
				//from different users? (this is not necessary now!!!)			
				//Session[lockid] = path + ".lock";
			}
			catch(IOException ex)
			{
				throw ex;
			}
			finally
			{				
				swriter.Close();
			}			
			
		}


		/* Set a timer for the case when a user spendsover 15mins (default locking time)
		 * working on a LO. To give a warning that they can reacquire a lock for this LO */
		private void setTimer()
		{
			string alertMsg = "Your time slot for editing this LO is running out, please click Acquire Lock image button at bottom right corner if you need more time; otherwise please cancel or save your changes";
			string timerScript = "<script language='javascript'>" + "window.setTimeout('alert(\"" + alertMsg + "\")', 900000);</script>";

			Literal1.Text = timerScript;
		}


		private void viewPage(string fpath, string title, string variant)
		{
			//loRootPath = Request.PhysicalApplicationPath + "variants\\" + title + "\\templo\\" + title + "_v" + varNum;
			string upath = "";
			if (variant != null)
			{
				upath = Request.Path.Substring(0,Request.Path.LastIndexOf("/")+1);
				upath += "variants/" + title + "/templo/" + title + "_v" + variant;
			}
			else
			{
				upath = (string)System.Configuration.ConfigurationSettings.AppSettings["FCKeditor:UserFilesPath"]+ title;
			}

			//index.htm or html
			string fname = fpath.Substring(fpath.LastIndexOf("\\")+1);

			if (Session["relativePath"] != null)
			{
				string relpath = Session["relativePath"].ToString();
				if (relpath.IndexOf("\\") >= 0)
				{
					relpath = relpath.Replace("\\","/");
				}
				
				upath = upath + relpath + "/";
			}
			else
			{
				upath = upath + "/";
			}

			fname = upath + fname;

			FCKeditor1.Visible = false;

			/*  better not to use label as html content holder, use iFrame instead
			StreamReader loReader = null;
			try 
			{
				loReader = new StreamReader(fpath);
				string loContents = loReader.ReadToEnd();

				loContents = replaceHtml.getViewHtml(loContents,upath);

				lblPage.Text = loContents;			
				
			} 
			catch (IOException x) 
			{
				Response.Write(x.ToString());
			}
			finally
			{
				loReader.Close();
			}
			*/

			LiteralIfrContent.Text = "<iframe id=\"test_frame\" src=\"" + fname + "\" frameBorder=\"no\" width=\"100%\" height=\"800px\">You have an old browser, please click <a href=\"" + fname + "\">this link</a> to view the learning object</iframe>";

			

		}

		private void BtnCancel_Click(object sender, System.EventArgs e)
		{
			//remove the lock file once it is canceled
			//.........			
			string lockfile = Request.PhysicalApplicationPath + "UserFiles\\" + Request["title"] + "\\currentlo.lock";						
			if (File.Exists(lockfile))
			{
				File.Delete(lockfile);				

				//Session[lockid] = null;
			}
		
			Response.Redirect("LOedit.aspx?title=" + Request["title"] + "&action=view");
			
		
		}


		private void saveAsOldVariant(string loname)
		{
			string zipPath = Request.PhysicalApplicationPath + "UserFiles\\" + loname;
			//string zipFile = zipPath + "\\" + loname + ".zip";

			//TODO: get current version number from change log file or from file names

			ZipOutputStream zs = createZipFile(zipPath,loname,null);

			zs.Finish();
			zs.Close();			

		}


		private void addLogEntry(string loName,string editSum,string changeType)
		{
			string logfile = Request.PhysicalApplicationPath + "variants\\" + loName + "\\changes.log";

			int varNum = 0;
			if (Session["varNum"] != null)
			{
				varNum = Convert.ToInt32(Session["varNum"]);  //session has been updated by now

			}
			
			/* attributes needed to be saved to log file for later use
			 * format: LO_id [client_ip] [user] version_num edit_sum  minor/major 
			 * */
			//in the case users enter new line in the summary
			editSum = editSum.Replace("\r\n",", "); //or use Environment.NewLine 
			
			string[] attrList = new string[6];
			attrList[0] = DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString();

			attrList[1] = loName;
			attrList[2] = varNum.ToString();  //the current version number by increment of 1
			attrList[3] = editSum;
			attrList[4] = changeType;   //not applicable (for the new version) minor/major
			attrList[5] = Request.UserHostAddress;

			string logline = String.Join("\t",attrList);

			StreamWriter swriter = null;						
			try
			{				
				swriter = new StreamWriter(logfile,true);
				swriter.WriteLine(logline);
			}
			catch(IOException ex)
			{
				throw ex;
			}
			finally
			{				
				swriter.Close();
			}
	        
		}



		//need to pass zipoutputstream for the recursive function, any other solutions?
		private ZipOutputStream createZipFile(string zpath, string lonm, ZipOutputStream zipStream)
		{
			string[] filenames = Directory.GetFiles(zpath);
	
		
			Crc32 crc = new Crc32();

			//initialize zipStream if it is not set.if should be set only once
			if (zipStream == null)
			{
				//string zfile =  Request.PhysicalApplicationPath + "UserFiles\\" + lonm + "\\" + lonm + ".zip";
				int varNum;
				
				if (Session["varNum"] != null)
				{
					varNum = Convert.ToInt32(Session["varNum"]) + 1;
					Session["varNum"] = varNum.ToString();  //update session value

				}
				else
				{
					Trace.Write("createZipFile","Session varNum is null!! :(");
					return null;   //shouldn't happen
				}
				string zfile =  Request.PhysicalApplicationPath + "variants\\" + lonm + "\\" + lonm + "_v" + varNum.ToString() + ".zip";
				zipStream = new ZipOutputStream(File.Create(zfile));
		
				zipStream.SetLevel(6); // 0 - store only to 9 - means best compression
			}
		
			foreach (string file in filenames) 
			{
				FileStream fs = File.OpenRead(file);
			
				byte[] buffer = new byte[fs.Length];
				fs.Read(buffer, 0, buffer.Length);
				//important!...to position the subdirectory/files relation,
				//need to remove the root path,just leave relative path
				string lopath = Request.PhysicalApplicationPath + "UserFiles\\" + lonm;
				ZipEntry entry = new ZipEntry(file.Substring(lopath.Length+1));
			
				entry.DateTime = DateTime.Now;
			
				// set Size and the crc, because the information
				// about the size and crc should be stored in the header
				// if it is not set it is automatically written in the footer.
				// (in this case size == crc == -1 in the header)
				// Some ZIP programs have problems with zip files that don't store
				// the size and crc in the header.
				entry.Size = fs.Length;
				fs.Close();
			
				crc.Reset();
				crc.Update(buffer);
			
				entry.Crc  = crc.Value;
			
				zipStream.PutNextEntry(entry);
			
				zipStream.Write(buffer, 0, buffer.Length);
			
			}
			string[] subdirs = Directory.GetDirectories(zpath);
			foreach (string dir in subdirs)
			{
				createZipFile(dir,lonm,zipStream);				
			}

			return zipStream;
			
		
			//zipStream.Finish();
			//zipStream.Close();
		}



		private void getVariantNum(string loname)
		{
			string logfile = Request.PhysicalApplicationPath + "variants\\" + loname + "\\changes.log";

			if (!File.Exists(logfile))
			{
				return;
			}

			string lastLine = "";

			StreamReader sreader = null;						
			try
			{	//get the last line of the file to retrive lastest version			
				sreader = new StreamReader(logfile);
				string line;
				while ( (line = sreader.ReadLine()) != null)
				{
					lastLine = line;
				}
			}
			catch(IOException ex)
			{
				throw ex;
			}
			finally
			{				
				sreader.Close();
			}

			char[] delim = new char[]{'\t'};
			string[] attrs = lastLine.Split(delim);
			
			Session["varNum"] = attrs[2];  //the second attribute is version number

			
		}



		//############ to be used by add relation fucniton
		private string getVersionNum(string loname)
		{
			string logfile = Request.PhysicalApplicationPath + "variants\\" + loname + "\\changes.log";

			if (!File.Exists(logfile))
			{
				return String.Empty;
			}

			string lastLine = "";

			StreamReader sreader = null;						
			try
			{	//get the last line of the file to retrive lastest version			
				sreader = new StreamReader(logfile);
				string line;
				while ( (line = sreader.ReadLine()) != null)
				{
					lastLine = line;
				}
			}
			catch(IOException ex)
			{
				throw ex;
			}
			finally
			{				
				sreader.Close();
			}

			char[] delim = new char[]{'\t'};
			string[] attrs = lastLine.Split(delim);
			
			return attrs[2];  //the second attribute is version number

		}


		private void ImgBtnLock_Click(object sender, System.Web.UI.ImageClickEventArgs e)
		{
			string lockfile = Request.PhysicalApplicationPath + "UserFiles\\" + Request["title"] + "\\currentlo.lock";	
	
			if (File.Exists(lockfile))
			{
				/* two cases here: locked by self or locked by others */
				StreamReader ipReader = null;
				string ipaddess = "";
				try 
				{
					ipReader = new StreamReader(lockfile);
					ipaddess = ipReader.ReadLine();		                    				
				} 
				catch (IOException x) 
				{
					Response.Write(x.ToString());
				}
				finally
				{
					ipReader.Close();
				}

				//case1: if the lockfile is from the same user (IP)
				if (ipaddess.Equals(Request.UserHostAddress))
				{
					//give an extra 15 mins for this file
					//DateTime updatedTime = File.GetLastWriteTime(lockfile).AddMinutes(15);	
					DateTime updatedTime = DateTime.Now.AddMinutes(15);
					File.SetLastWriteTime(lockfile, updatedTime);
					setTimer();
				}                 
				else
				{					
					//case 2: the file is locked by a different user
					//judge if it is time out for this user now
					TimeSpan timeDiff = DateTime.Now.Subtract(File.GetLastWriteTime(lockfile));
					if (timeDiff.Minutes >= 15)
					{
						//reacquire this lock for the user who requests, reset time and owner
						StreamWriter swriter = null;						
						try
						{
							swriter = new StreamWriter(lockfile,false);
							swriter.WriteLine(Request.UserHostAddress);
						}
						catch(IOException ex)
						{
							throw ex;
						}
						finally
						{
							swriter.Close();
						}
						setTimer();
					}
					else
					{
						lblMsg.Text = "This LO \"" + Request["title"] + "\" is being edited by somebody else. Please wait and try later";
						lblMsg.Visible = true;
						FCKeditor1.Visible = false;
						divSaveOption.Visible = false;
						
					}

				}
			}
			else  //opposed to File.Exists(). might have been acquired and released by another user
			{
				StreamWriter swriter = null;						
				try
				{
					swriter = new StreamWriter(lockfile,false);
					swriter.WriteLine(Request.UserHostAddress);
				}
				catch(IOException ex)
				{
					throw ex;
				}
				finally
				{
					swriter.Close();
				}

				setTimer();

			}			
		
		}



		

	}
}
