using System;
using System.Web;
using System.Web.UI.WebControls;
using ASPNET.StarterKit.TimeTracker.BusinessLogicLayer;

namespace ASPNET.StarterKit.TimeTracker.Web
{
	//*********************************************************************
	//
	// ProjectDetails.aspx
	//
	// The ProjectDetails.aspx page is used to add new projects or make changes to
	// existing projects.  Project membership are associated using this page.
	// In addition, project categories are added and changed on this page.
	// Only members of the Admin or Manager Role can access this page.
	// Members of the Admin group can modify all projects, while members of the
	// Managers group can only modify the projects they are managing.
	//
	//*********************************************************************

	public class ProjectDetails : System.Web.UI.Page
	{
		protected System.Web.UI.WebControls.TextBox	ProjectName;
		protected System.Web.UI.WebControls.TextBox	Description;
		protected System.Web.UI.WebControls.TextBox CompletionDate;
		protected System.Web.UI.WebControls.TextBox	Duration;
		protected System.Web.UI.WebControls.ListBox	Members;
		protected System.Web.UI.WebControls.Button CopyButton;
		protected System.Web.UI.WebControls.Button AddButton;
		protected System.Web.UI.WebControls.Button SaveButton;
		protected System.Web.UI.WebControls.DropDownList Managers;
		protected System.Web.UI.WebControls.DropDownList Projects;
		protected System.Web.UI.WebControls.TextBox CategoryName;
		protected System.Web.UI.WebControls.TextBox	Abbrev;
		protected System.Web.UI.WebControls.TextBox	CatDuration;
		protected System.Web.UI.WebControls.DataGrid CategoriesGrid;
		protected System.Web.UI.WebControls.RequiredFieldValidator ProjectNameRequiredfieldvalidator;
		protected System.Web.UI.WebControls.RequiredFieldValidator ManagerRequiredFieldValidator;
		protected System.Web.UI.WebControls.CompareValidator CompletionDateCompareValidator;
		protected System.Web.UI.WebControls.RequiredFieldValidator CompletionDateRequiredFieldValidator;
		protected System.Web.UI.WebControls.CompareValidator DurationCompareValidator;
		protected System.Web.UI.WebControls.RequiredFieldValidator DurationRequiredFieldValidator;
		protected System.Web.UI.WebControls.CustomValidator	ProjectsGridCustomValidator;
		protected System.Web.UI.WebControls.RegularExpressionValidator RegularExpressionValidatorAbbrev;
		protected System.Web.UI.WebControls.CompareValidator CatDurationValidator;
		protected System.Web.UI.WebControls.CustomValidator AbbrevCustomValidator;
		protected System.Web.UI.WebControls.Label ErrorMessage;
		protected System.Web.UI.WebControls.Button CancelButton;
		protected System.Web.UI.WebControls.Button CancelButton2;
		protected System.Web.UI.WebControls.Button CancelButton3;
		protected System.Web.UI.WebControls.Button DeleteButton;
		protected System.Web.UI.WebControls.Button DeleteButton2;
		protected System.Web.UI.WebControls.Button SaveButton2;
		protected System.Web.UI.WebControls.RangeValidator RangeValidator1;
		protected System.Web.UI.WebControls.RangeValidator RangeValidator2;
		protected System.Web.UI.WebControls.Label CategoryErrorMessage;
		
		// Contains the id of current project
		private int _projID;
	
		private void Page_Load(object sender, System.EventArgs e)
		{
			// Ensure that the visiting user has access to view the current page
			if (TTSecurity.IsInRole(TTUser.UserRoleAdminPMgr) == false) 
			{
				Response.Redirect("AccessDenied.aspx?Index=-1", true);
			}

			// Obtain project id from the QueryString.
			_projID = (Request.QueryString["id"]==null) ? 0 : Convert.ToInt32(Request.QueryString["id"]);

			if (!IsPostBack)
			{
				// The categories collection is stored in session
				// Also the categories are not saved in the database until the user clicks Save for the whole Project
				Session["catArray"] = new CategoriesCollection();

				BindManagers();
				BindMembers();
				BindOtherProjects();

				// Load project with _projID when project id exists in the QueryString
				if (_projID != 0)
					BindProject();
			}

			// Add warning and confirmation when user tries to delete a project
			DeleteButton.Attributes.Add("onclick", "return confirm('Deleting a project will also delete all the time entries and categories associated with the project. This deletion cannot be undone. Are you sure you want to delete this project?')");
			DeleteButton2.Attributes.Add("onclick", "return confirm('Deleting a project will also delete all the time entries and categories associated with the project. This deletion cannot be undone. Are you sure you want to delete this project?')");
		}

		#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();
			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.SaveButton.Click += new System.EventHandler(this.SaveButton_Click);
			this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click);
			this.DeleteButton.Click += new System.EventHandler(this.DeleteButton_Click);
			this.Abbrev.TextChanged += new System.EventHandler(this.Abbrev_TextChanged);
			this.AddButton.Click += new System.EventHandler(this.AddButton_Click);
			this.AbbrevCustomValidator.ServerValidate += new System.Web.UI.WebControls.ServerValidateEventHandler(this.AbbrevCustomValidator_ServerValidate);
			this.CopyButton.Click += new System.EventHandler(this.CopyButton_Click);
			this.CategoriesGrid.ItemCreated += new System.Web.UI.WebControls.DataGridItemEventHandler(this.CategoriesGrid_ItemCreated);
			this.CategoriesGrid.SortCommand += new System.Web.UI.WebControls.DataGridSortCommandEventHandler(this.CategoriesGrid_Sort);
			this.ProjectsGridCustomValidator.ServerValidate += new System.Web.UI.WebControls.ServerValidateEventHandler(this.ValidateCategories);
			this.SaveButton2.Click += new System.EventHandler(this.SaveButton_Click);
			this.CancelButton2.Click += new System.EventHandler(this.CancelButton_Click);
			this.DeleteButton2.Click += new System.EventHandler(this.DeleteButton_Click);
			this.Load += new System.EventHandler(this.Page_Load);

		}
		#endregion

		//*********************************************************************
		//
		// The BindManagers method retrieves the list of users that are in the Managers Role
		// and then databinds them to the Managers dropdownlist.
		//
		//*********************************************************************

		private void BindManagers()
		{
			Managers.DataSource = TTUser.ListManagers();
			Managers.DataValueField = "UserID";
			Managers.DataTextField = "Name";
			Managers.DataBind();

			// insert blank choice on top of the managers dropdownlist.
			Managers.Items.Insert(0, new ListItem("Select Manager...", String.Empty));
		}

		//*********************************************************************
		//
		// The BindMembers method retrieves the list of Users the current user can view and 
		// then databinds them to the Members listbox.
		//
		//*********************************************************************

		private void BindMembers()
		{
			Members.DataSource = TTUser.GetAllUsers(TTSecurity.GetUserID());
			Members.DataValueField = "UserID";
			Members.DataTextField = "Name";
			Members.DataBind();
		}

		//*********************************************************************
		//
		// The BindOtherProjects method retrieves the list of all projects, databinds to
		// the Projects dropdownlist, and then remove the current project from that list.
		//
		//*********************************************************************

		private void BindOtherProjects()
		{
			Projects.DataSource = Project.GetProjects();
			Projects.DataTextField = "Name";
			Projects.DataValueField = "ProjectID";
			Projects.DataBind();

			// remove current project from list of projects
			Projects.Items.Remove(Projects.Items.FindByValue(_projID.ToString()));

			// Disable "Copy" functionality if there are no projects in the list
			if (Projects.Items.Count == 0)
				CopyButton.Enabled = false;
		}

		//*********************************************************************
		//
		// The BindProject method retrieves the project with _projID and populates
		// the web controls with the project's info.
		//
		//*********************************************************************

		private void BindProject()
		{
			Project project = new Project(_projID);
			project.Load();

			// Populate web controls with the project's info.
			ProjectName.Text = project.Name;
			Description.Text = project.Description;
			CompletionDate.Text = project.EstCompletionDate.ToShortDateString();
			Duration.Text = project.EstDuration.ToString();
			Managers.Items.FindByValue(project.ManagerUserID.ToString()).Selected = true;

			// Sets the selected members of the project.
			foreach (TTUser user in project.Members)
			{
				Members.Items.FindByValue(user.UserID.ToString()).Selected = true;
			}

			BindCategoriesGrid(project.Categories);
		}

		//*********************************************************************
		//
		// The BindCategoriesGrid method is a helper take takes in a collection of categories 
		// and databinds the Categories datagrid with that collection.
		//
		// The categories are also then stored in session.
		//
		//*********************************************************************

		private void BindCategoriesGrid(CategoriesCollection cats)
		{
			// Call method to sort the data before databinding
			SortGridData(cats, SortField, SortAscending);

			CategoriesGrid.DataSource = cats;
			CategoriesGrid.DataBind();

			// Store the list of categories in session
			Session["catArray"] = cats;
		}

		//*********************************************************************
		//
		// The ReturnToProjectList method redirects to the ProjectList.aspx page.
		//
		//*********************************************************************

		private void ReturnToProjectList()
		{
			// Clear the current project categories that are stored in session.
			Session["catArray"] = null;

			// Return user to Project List Page.
			int mainIndex = (Request["index"]==null) ? 0 : Convert.ToInt32(Request["index"]);
			int adminIndex = (Request["adminindex"]==null) ? 0 : Convert.ToInt32(Request["adminindex"]);
			Response.Redirect(String.Format("ProjectList.aspx?index={0}&adminindex={1}", mainIndex, adminIndex), false);
		}

		//*********************************************************************
		//
		// The SortGridData method is a helper method called when databinding the Categories grid 
		// to sort the columns of the grid.
		//
		//*********************************************************************

		private void SortGridData(CategoriesCollection list, string sortField, bool asc)
		{
			CategoriesCollection.CategoryFields sortCol = CategoriesCollection.CategoryFields.InitValue;

			switch(sortField)
			{
				case "Name":
					sortCol = CategoriesCollection.CategoryFields.Name;
					break;
				case "Abbrev":
					sortCol = CategoriesCollection.CategoryFields.Abbreviation;
					break;
				case "Duration":
					sortCol = CategoriesCollection.CategoryFields.Duration;
					break;
			}

			list.Sort(sortCol, asc);
		}

		private void CancelButton_Click(object sender, System.EventArgs e)
		{
			ReturnToProjectList();
		}

		//*********************************************************************
		//
		// The DeleteButton_Click event handler calls a method that deletes the project, 
		// all of its categories, projectmembers and their time entries.
		//
		//*********************************************************************
		
		private void DeleteButton_Click(object sender, System.EventArgs e)
		{
			Project.Remove(_projID);
			ReturnToProjectList();
		}

		//*********************************************************************
		//
		// The SaveButton_Click event handler saves the configuration of the project.
		// It updates if the current project id is not 0 and inserts if it is 0.
		//
		//*********************************************************************

		private void SaveButton_Click(object sender, System.EventArgs e)
		{
			// Add the project manager as member of the project.
			Members.Items.FindByValue(Managers.SelectedItem.Value).Selected = true;

			// Make sure that there is at least one category.
			ProjectsGridCustomValidator.Validate();
			if (!ProjectsGridCustomValidator.IsValid)
				return;
			
			// Make sure that there are no duplicate categories.
			AbbrevCustomValidator.Validate();
			if (!AbbrevCustomValidator.IsValid)
				return;

			// Retrieve the current list of categories from session.
			CategoriesCollection catArray = (CategoriesCollection)Session["catArray"];

			// Get list of members the user selected.
			UsersCollection selectedMembers = new UsersCollection();
			foreach (ListItem li in Members.Items)
			{
				if (li.Selected)
				{
					TTUser user = new TTUser();
					user.UserID = Convert.ToInt32(li.Value);
					selectedMembers.Add(user);
				}
			}

			Project prj = new Project(
				_projID, 
				TTSecurity.CleanStringRegex(ProjectName.Text),
				TTSecurity.CleanStringRegex(Description.Text), 
				Convert.ToInt32(Managers.SelectedItem.Value), 
				Convert.ToDateTime(CompletionDate.Text),
				Convert.ToDecimal(Duration.Text)
				);
			prj.Categories = catArray;
			prj.Members = selectedMembers;
			
			if (prj.Save())
			{
				ReturnToProjectList();
			}
			else
			{
				ErrorMessage.Text = "There was an error.  You cannot remove a member with time entries from this project.";
			}
		}

		//*********************************************************************
		//
		// The AddButton_Click event handler adds a category to the categories collection
		// in session and displays it in the datagrid.
		//
		//*********************************************************************

		private void AddButton_Click(object sender, System.EventArgs e)
		{
			// check to make sure duplicated category abbreviations are avoided.
			if (!AbbrevCustomValidator.IsValid)
				return;

			// a required field validator is not used for this case because only the add button click should 
			// check for this and not the save button.
			if (CategoryName.Text == "")
			{
				CategoryErrorMessage.Text = "目录名不能为空";
				return;
			}

			// a required field validator is not used for this case because only the add button click should 
			// check for this and not the save button.
			if (Abbrev.Text == "")
			{
				CategoryErrorMessage.Text = "Category abbreviation is required.";
				return;
			}

			CategoriesCollection catArray = (CategoriesCollection)Session["catArray"];
			if (catArray == null) 
				catArray = new CategoriesCollection();

			// categories need a unique identifier when added, since they are not yet saved to 
			// the database but in session, a counter is used to populate their id.
			int catID = (Session["catID"] != null) ? (Convert.ToInt32(Session["catID"]) - 1) : -1;
			Session["catID"] = catID;

			Category cat = new Category();
			cat.CategoryID = catID;
			cat.Name = TTSecurity.CleanStringRegex(CategoryName.Text);
			cat.Abbreviation = TTSecurity.CleanStringRegex(Abbrev.Text);
			cat.EstDuration = (CatDuration.Text.Length==0) ? 0 : Convert.ToDecimal(CatDuration.Text);
			catArray.Add(cat);

			// Re-Validate now that users have added a new category.
			ProjectsGridCustomValidator.Validate();

			BindCategoriesGrid(catArray);

			CategoryName.Text = string.Empty;
			Abbrev.Text = string.Empty;
			CatDuration.Text = string.Empty;
		}

		//*********************************************************************
		//
		// The CopyButton_Click event handler copies the list of categories from the 
		// selected project in the Projects dropdownlist.
		//
		//*********************************************************************

		private void CopyButton_Click(object sender, System.EventArgs e)
		{
			int catID;
			CategoriesCollection catArray = (CategoriesCollection)Session["catArray"];
			if (catArray == null) 
				catArray = new CategoriesCollection();

			CategoriesCollection projcatArray = Project.GetCategories(Convert.ToInt32(Projects.SelectedItem.Value));
			foreach (Category cat in projcatArray)
			{
				// categories need a unique identifier when added, since they are not yet saved to 
				// the database but in session, a counter is used to populate their id.
				catID = (Session["catID"] != null) ? (Convert.ToInt32(Session["catID"]) + 1) : 1;
				Session["catID"] = catID;
				cat.CategoryID = catID;
				catArray.Add(cat);
			}

			// Re-Validate to make sure duplicate categories are not copied.
			ProjectsGridCustomValidator.Validate();

			Session["catArray"] = catArray;

			BindCategoriesGrid(catArray);
		}

		//*********************************************************************
		//
		// The CategoriesGrid_OnDelete event handler deletes a category from the categories grid.
		//
		//*********************************************************************

		protected void CategoriesGrid_OnDelete(Object sender, DataGridCommandEventArgs e)
		{
			int catID = Convert.ToInt32(CategoriesGrid.DataKeys[(int)e.Item.ItemIndex]);
			CategoriesCollection catArray = (CategoriesCollection)Session["catArray"];

			for (int i = 0;i < catArray.Count;i++)
			{
				if (((Category)catArray[i]).CategoryID == catID)
				{
					catArray.RemoveAt(i);
				}
			}

			BindCategoriesGrid(catArray);
		}

		//*********************************************************************
		//
		// The CategoriesGrid_OnCancel event handler cancels an edit on the categories grid.
		//
		//*********************************************************************

		protected void CategoriesGrid_OnCancel(Object sender, DataGridCommandEventArgs e)
		{
			CategoriesGrid.EditItemIndex = -1;

			BindCategoriesGrid((CategoriesCollection)Session["catArray"]);		
		}

		//*********************************************************************
		//
		// The CategoriesGrid_OnUpdate event handler updates changes on an edit of the categories grid.
		//
		//*********************************************************************

		protected void CategoriesGrid_OnUpdate(Object sender, DataGridCommandEventArgs e)
		{
			// set up the editted category
			Category editCat = new Category();
			editCat.CategoryID = Convert.ToInt32(CategoriesGrid.DataKeys[(int)e.Item.ItemIndex]);
			editCat.Name = TTSecurity.CleanStringRegex(((TextBox) e.Item.FindControl("EditName")).Text);
			editCat.Abbreviation = TTSecurity.CleanStringRegex(((TextBox) e.Item.FindControl("EditAbbreviation")).Text);
			editCat.EstDuration = Convert.ToDecimal(((TextBox) e.Item.FindControl("EditDuration")).Text);

			// check to make sure that category abbreviations are not being duplicated.
			CategoriesCollection catArray = (CategoriesCollection)Session["catArray"];
			if (catArray != null) 
			{
				string editAbbrev = ((TextBox) e.Item.FindControl("EditAbbreviation")).Text;
				foreach (Category cat in catArray)
				{
					if (cat.Abbreviation == editCat.Abbreviation && cat.CategoryID != editCat.CategoryID)
					{
						CategoryErrorMessage.Text = "目录缩写名不能重复";
						return;
					}
				}
			}

			// container for new list of categories
			CategoriesCollection catNew = new CategoriesCollection();

			// Add the editted category to our a new arraylist
			catNew.Add(editCat);

			if (catArray == null) 
				catArray = new CategoriesCollection();
			foreach (Category cat in catArray)
			{
				// category id does not exist when users are creating new projects so we cannot use it as comparer here
				// since categories abbreviation must be unique per project we can use it as a comparer
				if (cat.CategoryID != editCat.CategoryID)
				{
					catNew.Add(cat);
				}
			}

			CategoriesGrid.EditItemIndex = -1;
			BindCategoriesGrid(catNew);
		}
		
		//*********************************************************************
		//
		// The CategoriesGrid_OnEdit event handler sets up editing for the categories grid
		//
		//*********************************************************************

		protected void CategoriesGrid_OnEdit(Object sender, DataGridCommandEventArgs e)
		{
			CategoriesGrid.EditItemIndex = e.Item.ItemIndex;

			BindCategoriesGrid((CategoriesCollection)Session["catArray"]);
		}

		//*********************************************************************
		//
		// CategoriesGrid_Sort handles sorting of the categories grid.
		//
		//*********************************************************************

		private void CategoriesGrid_Sort(object source, System.Web.UI.WebControls.DataGridSortCommandEventArgs e)
		{
			SortField = e.SortExpression;

			BindCategoriesGrid((CategoriesCollection)Session["catArray"]);
		}

		//*********************************************************************
		//
		// Custom Validator event handler to make sure that there are categories in the categories grid.
		//
		//*********************************************************************

		private void ValidateCategories(object source, System.Web.UI.WebControls.ServerValidateEventArgs args)
		{
			CategoriesCollection catArray = (CategoriesCollection)Session["catArray"];
			if (catArray != null) 
			{
				if (catArray.Count > 0)
				{
					args.IsValid = true;
					return;
				}
			}
			args.IsValid = false;
		}

		//*********************************************************************
		//
		// Custom Validator event handler to make sure that category abbreviations are not duplicated.
		//
		//*********************************************************************

		private void AbbrevCustomValidator_ServerValidate(object source, System.Web.UI.WebControls.ServerValidateEventArgs args)
		{
			CategoriesCollection catArray = (CategoriesCollection)Session["catArray"];
			if (catArray.Count != 0) 
			{
				catArray.Sort(CategoriesCollection.CategoryFields.Abbreviation, true);
				for(int i=0; i < catArray.Count; i++)
				{
					if (((Category)catArray[i]).Abbreviation == Abbrev.Text)
					{
						args.IsValid = false;
						return;
					}
					if (i!=0 && ((Category)catArray[i]).Abbreviation == ((Category)catArray[i-1]).Abbreviation )
					{
						args.IsValid = false;
						return;
					}
					
				}
				
				
			}
			args.IsValid = true;
		}

		private void CategoriesGrid_ItemCreated(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
		{
			// add confirmation on deletion of categories
			if (e.Item.ItemType	== ListItemType.Item || e.Item.ItemType	== ListItemType.AlternatingItem)
			{
				((ImageButton)e.Item.FindControl("CatDeleteButton")).Attributes.Add("onclick", "return confirm('删除该目录将删除所有与该目录相关的时间记录，是否确认？')");
			}
		}

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

		string SortField 
		{
			get 
			{
				object o = ViewState["SortField"];
				if (o == null) 
				{
					return String.Empty;
				}
				return (string)o;
			}

			set 
			{
				if (value == SortField) 
				{
					// same as current sort file, toggle sort direction
					SortAscending = !SortAscending;
				}
				ViewState["SortField"] = value;
			}
		}

		//*********************************************************************
		//
		// SortAscending property is tracked in ViewState
		//
		//*********************************************************************

		bool SortAscending 
		{
			get 
			{
				object o = ViewState["SortAscending"];
				if (o == null) 
				{
					return true;
				}
				return (bool)o;
			}

			set 
			{
				ViewState["SortAscending"] = value;
			}
		}
	}
}