using System;
using System.Collections;
using System.Data;
using System.Text;
using System.Web.UI.WebControls;
using ASPNET.StarterKit.TimeTracker.BusinessLogicLayer;

namespace ASPNET.StarterKit.TimeTracker.Web
{
	//*********************************************************
	//
	// Main page of the Application, for users to enter time entries.
	//
	//*********************************************************

	public class TimeEntry : System.Web.UI.Page
	{
		//Use Time Entry component to capture user's input during edit mode
		protected BusinessLogicLayer.TimeEntry	_userInput = new BusinessLogicLayer.TimeEntry(0, 0, 0, 0, DateTime.MinValue, null, -1M);
		protected DataTable						_dayListTable = null;

		protected System.Web.UI.WebControls.Button					AddEntry;
		protected System.Web.UI.WebControls.CompareValidator		CompareValidator1;
		protected System.Web.UI.WebControls.Button					Cancel;
		protected System.Web.UI.WebControls.DropDownList			CategoryList;
		protected System.Web.UI.WebControls.DropDownList			Days;
		protected System.Web.UI.WebControls.TextBox					Description;
		protected System.Web.UI.WebControls.TextBox					Hours;
		protected System.Web.UI.WebControls.DropDownList			ProjectList;
		protected System.Web.UI.WebControls.RangeValidator			RangeValidator1;
		protected System.Web.UI.WebControls.RequiredFieldValidator	Requiredfieldvalidator1;
		protected System.Web.UI.WebControls.RequiredFieldValidator	RequiredFieldValidator2;
		protected System.Web.UI.WebControls.DataGrid				TimeEntryGrid;
		protected System.Web.UI.WebControls.Image					TimeGraph;
		protected System.Web.UI.WebControls.DropDownList			UserList;
		protected System.Web.UI.WebControls.TextBox					WeekEnding;
		
		private TTUser	 _user;
		private DateTime _weekEndingDate = DateTime.Today;
		private DateTime _weekStartingDate = DateTime.Today;

		//*******************************************************
		//
		// The Page_Load server event handler on this page is used
		// to populate information fields on this page
		//
		//*******************************************************

		private void Page_Load(object sender, System.EventArgs e)
		{
			_user = new TTUser(
				TTSecurity.GetUserID(), 
				User.Identity.Name, 
				TTSecurity.GetName(), 
				TTSecurity.GetUserRole());

			if (!Page.IsPostBack)
			{
				// If page is loaded for the first time, select the current week.
				BusinessLogicLayer.TimeEntry.FillCorrectStartEndDates(DateTime.Today, 
					ref _weekStartingDate, ref _weekEndingDate);
				WeekEnding.Text = _weekEndingDate.ToShortDateString();

				BindUserList();
				BindEntryFields();
				BindTimeSheet(_user.UserID, _weekStartingDate, _weekEndingDate);
				
			}
			else
			{
				// Selects the weekending date, corresponding to week user selected.
				BusinessLogicLayer.TimeEntry.FillCorrectStartEndDates(Convert.ToDateTime(WeekEnding.Text), 
					ref _weekStartingDate, ref _weekEndingDate);
				_dayListTable = BusinessLogicLayer.TimeEntry.GetWeek(Convert.ToDateTime(WeekEnding.Text));
				
			}
			
		}
		
		//*******************************************************
		//
		// BindCategoryList method databinds data to CategoryList dropdown.
		//
		//*******************************************************

		private void BindCategoryList()
		{
			if (ProjectList.SelectedItem != null) 
			{
				// CategoryList is different for each project, Project.GetCategories gets a list of 
				// categories based on the project.
				CategoryList.DataSource = Project.GetCategories(Convert.ToInt32(ProjectList.SelectedItem.Value));
				CategoryList.DataValueField = "CategoryID";
				CategoryList.DataTextField = "Abbreviation";
				CategoryList.DataBind();
			}
		}

		//*******************************************************
		//
		// BindDates method populates the DropDownList Days to show each day of the week
		//
		//*******************************************************

		private void BindDates()
		{
			_dayListTable = BusinessLogicLayer.TimeEntry.GetWeek(Convert.ToDateTime(WeekEnding.Text));
			Days.DataSource = _dayListTable;
			Days.DataValueField = "Date";
			Days.DataTextField = "Day";
			Days.DataBind();
			//Days.Items.FindByText(Convert.ToDateTime(DateTime.Today).ToString("ddd")).Selected= true;
			Days.Items.FindByText(DateTime.Today.ToString("ddd")).Selected=true;
			
		}

		//*******************************************************
		//
		// BindEntryFields calls methods that populate the drop down lists with data.
		//
		//*******************************************************

		private void BindEntryFields()
		{
			BindDates();
			BindProjectList();
			BindCategoryList();
		}
		
		//*******************************************************
		//
		// BindProjectList method populates the DropDownList ProjectList to show
		// list of projects that belongs to the user.
		//
		//*******************************************************

		private void BindProjectList()
		{
			// Preserve the selected value so information is still there after postback.
			if (ProjectList.SelectedItem != null)
				_userInput.ProjectID = Convert.ToInt32(ProjectList.SelectedItem.Value);

			ProjectList.DataSource = ListUserProjects();
			ProjectList.DataTextField = "Name";
			ProjectList.DataValueField = "ProjectID";
			ProjectList.DataBind();

			// Select the correct item from ProjectList.
			if (ProjectList.Items.FindByValue(_userInput.ProjectID.ToString()) != null)
				ProjectList.Items.FindByValue(_userInput.ProjectID.ToString()).Selected = true;
		}

		
		//*******************************************************
		//
		// An overload of BindTimeSheet method
		// this is called when the logged on user in querying time entry about themself.
		//
		//*******************************************************

		private void BindTimeSheet(int userID, DateTime start, DateTime end)
		{
			BindTimeSheet(userID, userID, start, end);
		}

		//*******************************************************
		//
		// BindTimeSheet method databinds the TimeEntryGrid based on the user
		// currently logged in and user which is being queried.
		//
		//*******************************************************

		private void BindTimeSheet(int queryUserID, int userID, DateTime start, DateTime end)
		{
			TimeEntriesCollection entryList = BusinessLogicLayer.TimeEntry.GetEntries(queryUserID, userID, start, end);

			// Sort datagrid if it is not empty.
			if (entryList != null) 
			{
				SortGridData(entryList, SortField, SortAscending);
			}
			// Draw time graph with new dataset.
			DrawTimeGraph(entryList, _dayListTable);

			TimeEntryGrid.DataSource = entryList;
			TimeEntryGrid.DataBind();
			
		}

		//*******************************************************
		//
		// BindUserList method databinds a list of users to the UserList;
		// This is based on the user currently logged in.
		//
		//*******************************************************

		private void BindUserList()
		{
			UserList.DataSource = TTUser.GetUsers(_user.UserID, _user.Role);
			UserList.DataBind();

			//Select User Drop Down to the correct value.
			UserList.Items.FindByValue(_user.UserID.ToString()).Selected=true;
		}

		//*******************************************************
		//
		// ClearEntryFields method clears all the entry fields on this page.
		//
		//*******************************************************

		private void ClearEntryFields()
		{
			BindEntryFields();

			// Make sure that no items appear in edit mode in the TimeEntryGrid.
			TimeEntryGrid.EditItemIndex = -1;

			BindTimeSheet(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value), _weekStartingDate, _weekEndingDate);
			Description.Text = string.Empty;
			Hours.Text = string.Empty;
		}
	
		//*******************************************************
		//
		// DrawTimeGraph method creates the bar graph on the fly.
		// Binds accordingly to number hours in each day of the week.
		//
		//*******************************************************

		private void DrawTimeGraph(TimeEntriesCollection chartData, DataTable dtWeek)
		{
			this.TimeGraph.Visible=false;
			// Assume dtWeek is already in the correct order according to FirstDayOfWeek Setting.
			Hashtable htWeekSummary = new Hashtable(7);	//7 days of data - key: EntryDate | value: Duration.

			// x & y values holder
			StringBuilder xValues = new StringBuilder();
			StringBuilder yValues = new StringBuilder();

			//Initialize htWeekSummary to start with first day of week setting.
			foreach (DataRow row in dtWeek.Rows)
			{
				htWeekSummary.Add(Convert.ToDateTime(row["Date"]).ToShortDateString(), 0m);
				
			}

			// Summarize data from grid Group By Days of Week.
			foreach (BusinessLogicLayer.TimeEntry time in chartData)
			{
				string key = time.EntryDate.ToShortDateString();
				
				Decimal dayDuration = Convert.ToDecimal(htWeekSummary[key]);
				dayDuration += time.Duration;
				htWeekSummary[key] = dayDuration;
			}

			//Build x & y values for graph.
			int index = 1;
			foreach (DataRow row in dtWeek.Rows)
			{
				string key = Convert.ToDateTime(row["Date"]).ToShortDateString();
				string currentDay = Convert.ToDateTime(key).DayOfWeek.ToString();
				string currentHour = Convert.ToString(htWeekSummary[key]);
//this.Response.Write(currentDay);
				xValues.Append(currentDay);
				yValues.Append(currentHour);

				//Don't append separator if this is the last item.
				if (index != dtWeek.Rows.Count)
				{
					xValues.Append("|");
					yValues.Append("|");
				}
				index++;
			}

			//Attach image generator to image.
			string a= "TimeEntryBarChart.aspx?" + 
				"xValues=" + xValues.ToString() + 
				"&yValues=" + yValues.ToString();
			//this.Response.Redirect(a);
			//this.Response.Write(a);
			
			TimeGraph.ImageUrl =a;//"TimeEntryBarChart.aspx?xValues=%d2%bb|%da%b6|||||&yValues=0|4.00|5.00|0|0|0|0";
			TimeGraph.Visible=true;
		}
		
		//*******************************************************
		//
		// ListGridCategories method returns a List of categories based on the projectID.
		//
		//*******************************************************

		protected CategoriesCollection ListGridCategories(int projectID)
		{
			return Project.GetCategories(projectID);
		}

		//*******************************************************
		//
		// ListUserProjects method returns a ArrayList of available projects.
		//
		//*******************************************************

		protected ProjectsCollection ListUserProjects()
		{
			return BusinessLogicLayer.Project.GetProjects(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value));
		}

		//*******************************************************
		//
		// SortGridData methods sorts the TimeEntryGrid based on which
		// sort field is being selected.  Also does reverse sorting based on the boolean.
		//
		//*******************************************************

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

			switch(sortField)
			{
				case "EntryDate":
					sortCol = TimeEntriesCollection.TimeEntryFields.Day;
					break;
				case "ProjectName":
					sortCol = TimeEntriesCollection.TimeEntryFields.Project;
					break;
				case "CategoryName":
					sortCol = TimeEntriesCollection.TimeEntryFields.Category;
					break;
				case "Duration":
					sortCol = TimeEntriesCollection.TimeEntryFields.Hours;
					break;
				case "Description":
					sortCol = TimeEntriesCollection.TimeEntryFields.Description;
					break;
				default:
					break;
			}

			list.Sort(sortCol, asc);
		}

		#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.WeekEnding.TextChanged += new System.EventHandler(this.WeekEnding_TextChanged);
			this.ProjectList.SelectedIndexChanged += new System.EventHandler(this.ProjectList_SelectedIndexChanged);
			this.AddEntry.Click += new System.EventHandler(this.AddEntry_Click);
			this.Cancel.Click += new System.EventHandler(this.Cancel_Click);
			this.UserList.SelectedIndexChanged += new System.EventHandler(this.UserList_OnChange);
			this.TimeEntryGrid.CancelCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.TimeEntryGrid_OnCancel);
			this.TimeEntryGrid.EditCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.TimeEntryGrid_OnEdit);
			this.TimeEntryGrid.SortCommand += new System.Web.UI.WebControls.DataGridSortCommandEventHandler(this.TimeEntryGrid_Sort);
			this.TimeEntryGrid.UpdateCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.TimeEntryGrid_OnUpdate);
			this.TimeEntryGrid.DeleteCommand += new System.Web.UI.WebControls.DataGridCommandEventHandler(this.TimeEntryGrid_OnDelete);
			this.TimeEntryGrid.ItemDataBound += new System.Web.UI.WebControls.DataGridItemEventHandler(this.TimeEntryGrid_Itembound);
			this.TimeEntryGrid.SelectedIndexChanged += new System.EventHandler(this.TimeEntryGrid_SelectedIndexChanged);
			this.Load += new System.EventHandler(this.Page_Load);

		}
		#endregion
		
		//*******************************************************
		//
		// AddEntry_Click server event handler on this page is used to 
		// saves a time entry and adds it to the system.
		//
		//*******************************************************

		private void AddEntry_Click(object sender, System.EventArgs e)
		{
			// check validation.
			Requiredfieldvalidator1.Validate();
			RequiredFieldValidator2.Validate();
			RangeValidator1.Validate();
			CompareValidator1.Validate();
			
			// proceeds if data is valid then create an obejct and save it.
			if (Requiredfieldvalidator1.IsValid && RequiredFieldValidator2.IsValid && CompareValidator1.IsValid && RangeValidator1.IsValid) 
			{
				BusinessLogicLayer.TimeEntry te = new BusinessLogicLayer.TimeEntry(0, 
					Convert.ToInt32(UserList.SelectedItem.Value), 
					Convert.ToInt32(ProjectList.SelectedItem.Value),
					Convert.ToInt32(CategoryList.SelectedItem.Value), 
					Convert.ToDateTime(Days.SelectedItem.Value), 
					TTSecurity.CleanStringRegex(Description.Text), Convert.ToDecimal(Hours.Text));
				te.Save(); 
				
				ClearEntryFields();
			}
		}	
		
		//*******************************************************
		//
		// Cancel_Click server event handler on this page is used to
		// clear all entry fields on page.
		//
		//*******************************************************

		private void Cancel_Click(object sender, System.EventArgs e)
		{
			ClearEntryFields();
		}

		//*******************************************************
		//
		// ProjectList_SelectedIndexChanged server event handler on this page is used to 
		// update the CategoryList dropdown when a different project is selected
		//
		//*******************************************************

		private void ProjectList_SelectedIndexChanged(object sender, System.EventArgs e)
		{
			BindCategoryList();
		}

		//*******************************************************
		//
		// TimeEntryGrid_Itembound server event handler on this page is used
		// during TimeEntryGrid databind
		//
		//*******************************************************

		private void TimeEntryGrid_Itembound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
		{
			// During in-line-editing mode this selects corresponding items for dropdowns and text boxes.
			// The _userInput object is created in TimeEntryGrid_OnEdit().
			if (e.Item.ItemType == ListItemType.EditItem) 
			{
				DropDownList currentCbo = (DropDownList) e.Item.FindControl("EntryDays");
				currentCbo.SelectedIndex = currentCbo.Items.IndexOf(currentCbo.Items.FindByText(_userInput.Day));

				currentCbo = (DropDownList) e.Item.FindControl("EntryProjects");
				currentCbo.SelectedIndex = currentCbo.Items.IndexOf(currentCbo.Items.FindByText(_userInput.ProjectName));

				currentCbo = (DropDownList) e.Item.FindControl("EntryCategories");
				currentCbo.SelectedIndex = currentCbo.Items.IndexOf(currentCbo.Items.FindByText(_userInput.CategoryName));				
			}
		}

		//*******************************************************
		//
		// TimeEntryGrid_OnCancel server event handler on this page is used to 
		// quit in-line-editing mode. and Undo all changes
		//
		//*******************************************************

		protected void TimeEntryGrid_OnCancel(Object sender, DataGridCommandEventArgs e)
		{
			TimeEntryGrid.EditItemIndex = -1;
			BindTimeSheet(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value), _weekStartingDate, _weekEndingDate);
		}
		
		//*******************************************************
		//
		// TimeEntryGrid_OnDelete server event handler on this page is used to 
		// delete a item from the database.
		//
		//*******************************************************

		protected void TimeEntryGrid_OnDelete(Object sender, DataGridCommandEventArgs e)
		{
			int entryLogID = Convert.ToInt32(TimeEntryGrid.DataKeys[(int)e.Item.ItemIndex]);
			BusinessLogicLayer.TimeEntry.Remove(entryLogID);
			BindTimeSheet(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value), _weekStartingDate, _weekEndingDate);
		}

		//*******************************************************
		//
		// TimeEntryGrid_OnEdit server event handler on this page is used to
		// start in-line-editing mode for TimeEntryGrid.
		//
		//*******************************************************

		protected void TimeEntryGrid_OnEdit(Object sender, DataGridCommandEventArgs e)
		{
			TimeEntryGrid.EditItemIndex = e.Item.ItemIndex;
			// Saves current info on temporary, use later in TimeEntryGrid_Itembound()
			_userInput.Day = ((Label) e.Item.FindControl("EntryDay")).Text;
			_userInput.ProjectName = ((Label) e.Item.FindControl("EntryProject")).Text.Trim();
			_userInput.ProjectID = Convert.ToInt32(((Label) e.Item.FindControl("EntryProjectID")).Text);
			_userInput.CategoryName = ((Label) e.Item.FindControl("EntryCategory")).Text.Trim();
			_userInput.Duration = Convert.ToDecimal(((Label) e.Item.FindControl("EntryDuration")).Text);
			_userInput.Description = ((Label) e.Item.FindControl("GridDescription")).Text.Trim();
			BindTimeSheet(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value), _weekStartingDate, _weekEndingDate);
		}

		//*******************************************************
		//
		// TimeEntryGrid_OnUpdate server event handler on this page is used to
		// update the changes made from in-line-editing.
		//
		//*******************************************************

		protected void TimeEntryGrid_OnUpdate(Object sender, DataGridCommandEventArgs e)
		{
			DataGridItem item = TimeEntryGrid.Items[TimeEntryGrid.EditItemIndex];

			RequiredFieldValidator required = (RequiredFieldValidator) item.FindControl("RequiredFieldValidatorGridHours");
			CompareValidator comparer = (CompareValidator) item.FindControl("CompareValidatorGridHours");

			required.Validate();
			comparer.Validate();
			
			// Check for validation, if all valid, update the object to data entered and save it.
			if (required.IsValid && comparer.IsValid) 
			{
				int entryLogID;
				int userID;
				int projectID;
				int categoryID;
				DateTime taskDate;
				string description;
				decimal duration;
				BusinessLogicLayer.TimeEntry te = null;

				// Fill in properties for TimeEntry object.
				entryLogID = Convert.ToInt32(TimeEntryGrid.DataKeys[(int)e.Item.ItemIndex]);
				userID = Convert.ToInt32(UserList.SelectedItem.Value);
				projectID = Convert.ToInt32(((DropDownList) e.Item.FindControl("EntryProjects")).SelectedItem.Value);
				categoryID = Convert.ToInt32(((DropDownList) e.Item.FindControl("EntryCategories")).SelectedItem.Value);
				taskDate = Convert.ToDateTime(((DropDownList) e.Item.FindControl("EntryDays")).SelectedItem.Value);
				description = TTSecurity.CleanStringRegex(((TextBox) e.Item.FindControl("EntryDescription")).Text);
				duration = Convert.ToDecimal(((TextBox) e.Item.FindControl("EntryHours")).Text);

				// Save the TimeEntry object.
				te = new BusinessLogicLayer.TimeEntry(entryLogID, userID, projectID, categoryID, taskDate, description, duration);
				te.Save();

				// Quit in-line-editing mode.
				TimeEntryGrid.EditItemIndex = -1;
				BindTimeSheet(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value), _weekStartingDate, _weekEndingDate);
			}
		}

		//*******************************************************
		//
		// TimeEntryGrid_Sort server event handler on this page is used to
		// sorts the TimeEntryGrid according to the sort field
		// being selected.
		//
		//*******************************************************
	
		private void TimeEntryGrid_Sort(object source, System.Web.UI.WebControls.DataGridSortCommandEventArgs e)
		{
			TimeEntryGrid.EditItemIndex = -1;
			SortField = e.SortExpression;
			BindTimeSheet(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value), _weekStartingDate, _weekEndingDate);
		}

		//*******************************************************
		//
		// UserProjects_OnChange server event handler on this page is called when user makes
		// a seletion change from project dropdown within in-line-editing, is used to 
		// update the category dropdown items according to the project selected
		//
		//*******************************************************

		protected void UserProjects_OnChange(object sender, System.EventArgs e)
		{
			DataGridItem item = TimeEntryGrid.Items[TimeEntryGrid.EditItemIndex];
			
			// Save temporary inputs so that data would be still there when user makes a Project selection
			// and cause postback.
			if (item != null)
			{
				TextBox txt = (TextBox) item.FindControl("EntryHours");
				_userInput.Duration = Convert.ToDecimal(txt.Text);
				
				TextBox txtDesc = (TextBox) item.FindControl("EntryDescription");
				_userInput.Description = txtDesc.Text;

				DropDownList EntryDays = (DropDownList)item.FindControl("EntryDays");
				_userInput.EntryDate = Convert.ToDateTime(EntryDays.SelectedItem.Value);
				_userInput.Day = EntryDays.SelectedItem.Text;

			}
			DropDownList EntryProjects = (DropDownList) sender;
			_userInput.ProjectID = Convert.ToInt32(EntryProjects.SelectedItem.Value);
			_userInput.ProjectName = EntryProjects.SelectedItem.Text;
			BindTimeSheet(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value), _weekStartingDate, _weekEndingDate);
		}
		
		//*******************************************************
		//
		// UserList_OnChange server event handler on this page is used
		// when a selection is changed in UserList DropDown
		// UserList items are limited to Role of the current user logged on the system.
		//
		//*******************************************************

		private void UserList_OnChange(object sender, System.EventArgs e)
		{
			TimeEntryGrid.EditItemIndex = -1;
			BindTimeSheet(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value), _weekStartingDate, _weekEndingDate);
			BindEntryFields();
		}

		//*******************************************************
		//
		// WeekEnding_TextChanged server event handler on this page is used to
		// select the correct week starting date and week ending date
		// it gets called when user makes a selection change on calendar.
		//
		//*******************************************************

		private void WeekEnding_TextChanged(object sender, System.EventArgs e)
		{
			TimeEntryGrid.EditItemIndex = -1;
			BusinessLogicLayer.TimeEntry.FillCorrectStartEndDates(Convert.ToDateTime(WeekEnding.Text), 
				ref _weekStartingDate, ref _weekEndingDate);
			WeekEnding.Text = _weekEndingDate.ToShortDateString();
			BindEntryFields();
			BindTimeSheet(_user.UserID, Convert.ToInt32(UserList.SelectedItem.Value), _weekStartingDate, _weekEndingDate);
		}

		private void TimeEntryGrid_SelectedIndexChanged(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;
			}
		}

	}
}
