Imports System
Imports System.Web
Imports System.Web.UI.WebControls
Imports 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
        Inherits System.Web.UI.Page
        Protected ProjectName As System.Web.UI.WebControls.TextBox
        Protected Description As System.Web.UI.WebControls.TextBox
        Protected CompletionDate As System.Web.UI.WebControls.TextBox
        Protected Duration As System.Web.UI.WebControls.TextBox
        Protected Members As System.Web.UI.WebControls.ListBox
        Protected WithEvents CopyButton As System.Web.UI.WebControls.Button
        Protected WithEvents AddButton As System.Web.UI.WebControls.Button
        Protected WithEvents SaveButton As System.Web.UI.WebControls.Button
        Protected Managers As System.Web.UI.WebControls.DropDownList
        Protected Projects As System.Web.UI.WebControls.DropDownList
        Protected CategoryName As System.Web.UI.WebControls.TextBox
        Protected Abbrev As System.Web.UI.WebControls.TextBox
        Protected CatDuration As System.Web.UI.WebControls.TextBox
        Protected WithEvents CategoriesGrid As System.Web.UI.WebControls.DataGrid
        Protected ProjectNameRequiredfieldvalidator As System.Web.UI.WebControls.RequiredFieldValidator
        Protected ManagerRequiredFieldValidator As System.Web.UI.WebControls.RequiredFieldValidator
        Protected CompletionDateCompareValidator As System.Web.UI.WebControls.CompareValidator
        Protected CompletionDateRequiredFieldValidator As System.Web.UI.WebControls.RequiredFieldValidator
        Protected DurationCompareValidator As System.Web.UI.WebControls.CompareValidator
        Protected DurationRequiredFieldValidator As System.Web.UI.WebControls.RequiredFieldValidator
        Protected WithEvents ProjectsGridCustomValidator As System.Web.UI.WebControls.CustomValidator
        Protected RegularExpressionValidatorAbbrev As System.Web.UI.WebControls.RegularExpressionValidator
        Protected CatDurationValidator As System.Web.UI.WebControls.CompareValidator
        Protected WithEvents AbbrevCustomValidator As System.Web.UI.WebControls.CustomValidator
        Protected ErrorMessage As System.Web.UI.WebControls.Label
        Protected WithEvents CancelButton As System.Web.UI.WebControls.Button
        Protected WithEvents CancelButton2 As System.Web.UI.WebControls.Button
        Protected WithEvents DeleteButton As System.Web.UI.WebControls.Button
        Protected WithEvents DeleteButton2 As System.Web.UI.WebControls.Button
        Protected WithEvents SaveButton2 As System.Web.UI.WebControls.Button
        Protected RangeValidator1 As System.Web.UI.WebControls.RangeValidator
        Protected RangeValidator2 As System.Web.UI.WebControls.RangeValidator
        Protected WithEvents CategoryNameValidator As System.Web.UI.WebControls.RegularExpressionValidator
        Protected WithEvents RegularExpressionValidator1 As System.Web.UI.WebControls.RegularExpressionValidator
        Protected WithEvents CategoryErrorMessage As System.Web.UI.WebControls.Label

        ' Contains the id of current project
        Private _projID As Integer

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            ' Ensure that the visiting user has access to view the current page
            If TTSecurity.IsInRole(TTUser.UserRoleAdminPMgr) = False Then
                Response.Redirect("AccessDenied.aspx?Index=-1", True)
            End If

            ' Obtain project id from the QueryString.
            If Request.QueryString("id") Is Nothing Then
                _projID = 0
            Else
                _projID = Convert.ToInt32(Request.QueryString("id"))
            End If
            If Not IsPostBack Then
                ' 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 Then
                    BindProject()
                End If
            End If
            ' 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?')")
        End Sub 'Page_Load

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

        Private Sub 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))
        End Sub 'BindManagers

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

        Private Sub BindMembers()
            Members.DataSource = TTUser.GetAllUsers(TTSecurity.GetUserID())
            Members.DataValueField = "UserID"
            Members.DataTextField = "Name"
            Members.DataBind()
        End Sub 'BindMembers

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

        Private Sub 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 Then
                CopyButton.Enabled = False
            End If
        End Sub 'BindOtherProjects

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

        Private Sub BindProject()
            Dim project As 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.
            Dim user As TTUser
            For Each user In project.Members
                Members.Items.FindByValue(user.UserID.ToString()).Selected = True
            Next user

            BindCategoriesGrid(project.Categories)
        End Sub 'BindProject

        '*********************************************************************
        '
        ' 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 Sub BindCategoriesGrid(ByVal cats As CategoriesCollection)
            ' 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
        End Sub 'BindCategoriesGrid

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

        Private Sub ReturnToProjectList()
            ' Clear the current project categories that are stored in session.
            Session("catArray") = Nothing

            ' Return user to Project List Page.
            Dim mainIndex As Integer
            If Request("index") Is Nothing Then
                mainIndex = 0
            Else
                mainIndex = Convert.ToInt32(Request("index"))
            End If
            Dim adminIndex As Integer
            If Request("adminindex") Is Nothing Then
                adminIndex = 0
            Else
                adminIndex = Convert.ToInt32(Request("adminindex"))
            End If
            Response.Redirect([String].Format("ProjectList.aspx?index={0}&adminindex={1}", mainIndex, adminIndex), False)
        End Sub 'ReturnToProjectList

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

        Private Sub SortGridData(ByVal list As CategoriesCollection, ByVal sortField As String, ByVal asc As Boolean)
            Dim sortCol As CategoriesCollection.CategoryFields = CategoriesCollection.CategoryFields.InitValue

            Select Case sortField
                Case "Name"
                    sortCol = CategoriesCollection.CategoryFields.Name
                Case "Abbrev"
                    sortCol = CategoriesCollection.CategoryFields.Abbreviation
                Case "Duration"
                    sortCol = CategoriesCollection.CategoryFields.Duration
            End Select

            list.Sort(sortCol, asc)
        End Sub 'SortGridData

        Private Sub CancelButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CancelButton.Click, CancelButton2.Click
            ReturnToProjectList()
        End Sub 'CancelButton_Click

        '*********************************************************************
        '
        ' The DeleteButton_Click event handler calls a method that deletes the project, 
        ' all of its categories, projectmembers and their time entries.
        '
        '*********************************************************************

        Private Sub DeleteButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DeleteButton.Click, DeleteButton2.Click
            Project.Remove(_projID)
            ReturnToProjectList()
        End Sub 'DeleteButton_Click

        '*********************************************************************
        '
        ' 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 Sub SaveButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles SaveButton.Click, SaveButton2.Click
            ' 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 Not ProjectsGridCustomValidator.IsValid Then
                Return
            End If
            ' Make sure that there are no duplicate categories.
            AbbrevCustomValidator.Validate()
            If Not AbbrevCustomValidator.IsValid Then
                Return
            End If
            ' Retrieve the current list of categories from session.
            Dim catArray As CategoriesCollection = CType(Session("catArray"), CategoriesCollection)

            ' Get list of members the user selected.
            Dim selectedMembers As New UsersCollection()
            Dim li As ListItem
            For Each li In Members.Items
                If li.Selected Then
                    Dim user As New TTUser()
                    user.UserID = Convert.ToInt32(li.Value)
                    selectedMembers.Add(user)
                End If
            Next li

            Dim prj As 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() Then
                ReturnToProjectList()
            Else
                ErrorMessage.Text = "There was an error.  You cannot remove a member with time entries from this project."
            End If
        End Sub 'SaveButton_Click

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

        Private Sub AddButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles AddButton.Click
            ' check to make sure duplicated category abbreviations are avoided.
            If Not AbbrevCustomValidator.IsValid Then
                Return
            End If
            ' 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 = "" Then
                CategoryErrorMessage.Text = "Category name is required."
                Return
            End If

            ' 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 = "" Then
                CategoryErrorMessage.Text = "Category abbreviation is required."
                Return
            End If

            Dim catArray As CategoriesCollection = CType(Session("catArray"), CategoriesCollection)
            If catArray Is Nothing Then
                catArray = New CategoriesCollection()
            End If
            ' 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.
            Dim catID As Integer
            If Not (Session("catID") Is Nothing) Then
                catID = Convert.ToInt32(Session("catID")) - 1
            Else
                catID = -1
            End If
            Session("catID") = catID

            Dim cat As New Category()
            cat.CategoryID = catID
            cat.Name = TTSecurity.CleanStringRegex(CategoryName.Text)
            cat.Abbreviation = TTSecurity.CleanStringRegex(Abbrev.Text)

            If CatDuration.Text.Length = 0 Then
                cat.EstDuration = 0
            Else
                cat.EstDuration = Convert.ToDecimal(CatDuration.Text)
            End If
            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
        End Sub 'AddButton_Click

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

        Private Sub CopyButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CopyButton.Click
            Dim catID As Integer
            Dim catArray As CategoriesCollection = CType(Session("catArray"), CategoriesCollection)
            If catArray Is Nothing Then
                catArray = New CategoriesCollection()
            End If
            Dim projcatArray As CategoriesCollection = Project.GetCategories(Convert.ToInt32(Projects.SelectedItem.Value))
            Dim cat As Category
            For Each 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.
                If Not (Session("catID") Is Nothing) Then
                    catID = Convert.ToInt32(Session("catID")) + 1
                Else
                    catID = 1
                End If
                Session("catID") = catID
                cat.CategoryID = catID
                catArray.Add(cat)
            Next cat

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

            Session("catArray") = catArray

            BindCategoriesGrid(catArray)
        End Sub 'CopyButton_Click

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

        Protected Sub CategoriesGrid_OnDelete(ByVal sender As [Object], ByVal e As DataGridCommandEventArgs) Handles CategoriesGrid.DeleteCommand
            Dim catID As Integer = Convert.ToInt32(CategoriesGrid.DataKeys(CInt(e.Item.ItemIndex)))
            Dim catArray As CategoriesCollection = CType(Session("catArray"), CategoriesCollection)

            Dim i As Integer
            For i = 0 To (catArray.Count - 1)
                If CType(catArray(i), Category).CategoryID = catID Then
                    catArray.RemoveAt(i)
                    Exit For
                End If
            Next i

            BindCategoriesGrid(catArray)
        End Sub 'CategoriesGrid_OnDelete

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

        Protected Sub CategoriesGrid_OnCancel(ByVal sender As [Object], ByVal e As DataGridCommandEventArgs) Handles CategoriesGrid.CancelCommand
            CategoriesGrid.EditItemIndex = -1

            BindCategoriesGrid(CType(Session("catArray"), CategoriesCollection))
        End Sub 'CategoriesGrid_OnCancel

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

        Protected Sub CategoriesGrid_OnUpdate(ByVal sender As [Object], ByVal e As DataGridCommandEventArgs) Handles CategoriesGrid.UpdateCommand
            ' set up the editted category
            Dim editCat As New Category()
            editCat.CategoryID = Convert.ToInt32(CategoriesGrid.DataKeys(CInt(e.Item.ItemIndex)))
            editCat.Name = TTSecurity.CleanStringRegex(CType(e.Item.FindControl("EditName"), TextBox).Text)
            editCat.Abbreviation = TTSecurity.CleanStringRegex(CType(e.Item.FindControl("EditAbbreviation"), TextBox).Text)
            editCat.EstDuration = Convert.ToDecimal(CType(e.Item.FindControl("EditDuration"), TextBox).Text)

            ' check to make sure that category abbreviations are not being duplicated.
            Dim catArray As CategoriesCollection = CType(Session("catArray"), CategoriesCollection)
            If Not (catArray Is Nothing) Then
                Dim editAbbrev As String = CType(e.Item.FindControl("EditAbbreviation"), TextBox).Text
                Dim thisCat As Category
                For Each thisCat In catArray
                    If thisCat.Abbreviation = editCat.Abbreviation And thisCat.CategoryID <> editCat.CategoryID Then
                        CategoryErrorMessage.Text = "Duplicate categories abbreviations are not allowed"
                        Return
                    End If
                Next thisCat
            End If

            ' container for new list of categories
            Dim catNew As New CategoriesCollection()

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

            If catArray Is Nothing Then
                catArray = New CategoriesCollection()
            End If
            Dim cat As Category
            For Each 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 Then
                    catNew.Add(cat)
                End If
            Next cat

            CategoriesGrid.EditItemIndex = -1
            BindCategoriesGrid(catNew)
        End Sub 'CategoriesGrid_OnUpdate

        '*********************************************************************
        '
        ' The CategoriesGrid_OnEdit event handler sets up editing for the categories grid
        '
        '*********************************************************************

        Protected Sub CategoriesGrid_OnEdit(ByVal sender As [Object], ByVal e As DataGridCommandEventArgs) Handles CategoriesGrid.EditCommand
            CategoriesGrid.EditItemIndex = e.Item.ItemIndex

            BindCategoriesGrid(CType(Session("catArray"), CategoriesCollection))
        End Sub 'CategoriesGrid_OnEdit

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

        Private Sub CategoriesGrid_Sort(ByVal [source] As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles CategoriesGrid.SortCommand
            SortField = e.SortExpression

            BindCategoriesGrid(CType(Session("catArray"), CategoriesCollection))
        End Sub 'CategoriesGrid_Sort

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

        Private Sub ValidateCategories(ByVal [source] As Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs) Handles ProjectsGridCustomValidator.ServerValidate
            Dim catArray As CategoriesCollection = CType(Session("catArray"), CategoriesCollection)
            If Not (catArray Is Nothing) Then
                If catArray.Count > 0 Then
                    args.IsValid = True
                    Return
                End If
            End If
            args.IsValid = False
        End Sub 'ValidateCategories

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

        Private Sub AbbrevCustomValidator_ServerValidate(ByVal [source] As Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs) Handles AbbrevCustomValidator.ServerValidate
            Dim catArray As CategoriesCollection = CType(Session("catArray"), CategoriesCollection)
            If catArray.Count <> 0 Then
                catArray.Sort(CategoriesCollection.CategoryFields.Abbreviation, True)
                Dim i As Integer
                For i = 0 To catArray.Count - 1
                    If CType(catArray(i), Category).Abbreviation = Abbrev.Text Then
                        args.IsValid = False
                        Return
                    End If
                    If i <> 0 Then
                        If CType(catArray(i), Category).Abbreviation = CType(catArray((i - 1)), Category).Abbreviation Then
                            args.IsValid = False
                            Return
                        End If
                    End If
                Next i
            End If
            args.IsValid = True
        End Sub 'AbbrevCustomValidator_ServerValidate

        Private Sub CategoriesGrid_ItemCreated(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataGridItemEventArgs) Handles CategoriesGrid.ItemCreated
            ' add confirmation on deletion of categories
            If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
                CType(e.Item.FindControl("CatDeleteButton"), ImageButton).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?')")
            End If
        End Sub 'CategoriesGrid_ItemCreated

        Property SortField() As String
            Get
                Dim o As Object = ViewState("SortField")
                If o Is Nothing Then
                    Return [String].Empty
                End If
                Return CStr(o)
            End Get

            Set(ByVal Value As String)
                If Value = SortField Then
                    ' same as current sort file, toggle sort direction
                    SortAscending = Not SortAscending
                End If
                ViewState("SortField") = Value
            End Set
        End Property

        '*********************************************************************
        '
        ' SortAscending property is tracked in ViewState
        '
        '*********************************************************************

        Property SortAscending() As Boolean
            Get
                Dim o As Object = ViewState("SortAscending")
                If o Is Nothing Then
                    Return True
                End If
                Return CBool(o)
            End Get

            Set(ByVal Value As Boolean)
                ViewState("SortAscending") = Value
            End Set
        End Property

        Private Sub InitializeComponent()

        End Sub
    End Class 'ProjectDetails
End Namespace 'ASPNET.StarterKit.TimeTracker.Web