    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;
            protected System.Web.UI.WebControls.RegularExpressionValidator CategoryNameValidator;
            protected System.Web.UI.WebControls.RegularExpressionValidator RegularExpressionValidator1;

            // 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);
            }

            ///
            /// Required method for Designer support - do not modify
            /// the contents of this method with the code editor.
            ///
            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.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 = "Category name is required.";
                    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 = "Duplicate categories abbreviations are not allowed";
                            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('Deleting an existing category will also delete all the time entries and categories associated with the category. Are you sure you want to delete this category?')");
                }
            }

            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;
                }
            }
        }
    }