' *
' * The project site is at: http://sourceforge.jp/projects/fishbornas/
' *
' * First author tiritomato 2012.
' *
' * Distributed under the FishbornArchiveShelf License (See
' *  file "Licenses/License.txt" contained in a project, or the following link.
' *  http://sourceforge.jp/projects/fishbornas/scm/svn/blobs/head/trunk/Licenses/License.txt)
' *
' * 2012.06.07 Initial Revision (tiritomato)
' *

Public Class StretchEchoPanel

    Public Sub New()
        InitializeComponent()

        idxColTime = Log.Columns("TimeDataGridViewTextBoxColumn").Index
        idxColType = Log.Columns("TypeDataGridViewTextBoxColumn").Index
        idxColLog = Log.Columns("TextDataGridViewTextBoxColumn").Index

        LogTypeNameLength = 0
        For Each logtype_name As String In [Enum].GetNames(GetType(AppBase.AppendLogArgs.LogType))
            LogTypeNameLength = Math.Max(LogTypeNameLength, logtype_name.Length)
        Next

    End Sub

    ' public properties ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    Private m_AppBase As AppBase
    Public Property AppBase As AppBase
        Get
            Return m_AppBase
        End Get
        Set(ByVal value As AppBase)

            If m_AppBase IsNot Nothing Then
                m_AppBase.Echoing.Log.Remove(AddressOf AppendLog)
            End If

            ColumnLayoutChanged.Clear()
            StatusSplit_SplitterMovedHandler.Clear()

            m_AppBase = value
            If value Is Nothing Then Return

            LogFilter1 = New LogFilterMenu("TypeFilter", AppBase, BindingSource1)
            Log.ContextMenuStrip = New Logic.DataGridViewUtility.ColumnVisibleControlMenu({"TextDataGridViewTextBoxColumn"}, AppBase.Config.LogListColumnInf, LogContextMenu, {LogFilter1})

            AppBase.Config.LogListColumnInf.Apply(Log)

            If AppBase.Config.WindowSize.ContainsKey(Windows.Forms.FormWindowState.Normal) Then
                Dim inf As AppBase.WindowSizeConfig = AppBase.Config.WindowSize(Windows.Forms.FormWindowState.Normal)
                If 0 < inf.StatusSplitDistance Then StatusSplit.SplitterDistance = inf.StatusSplitDistance
            End If

            Dim FilterList As New Collections.Generic.List(Of String)
            Dim EnumValues As AppBase.AppendLogArgs.LogType() = [Enum].GetValues(GetType(AppBase.AppendLogArgs.LogType))
            For Each logtype As AppBase.AppendLogArgs.LogType In EnumValues
                Dim isDisplayEnable As Boolean = (AppBase.Config.LogFilter(logtype) Is Nothing OrElse AppBase.Config.LogFilter(logtype).Value = True)
                If isDisplayEnable Then FilterList.Add("Type = '" & logtype.ToString & "'")
            Next
            Dim Filter As String = Nothing
            If 0 < FilterList.Count And FilterList.Count <> EnumValues.Length Then Filter = String.Join(" OR ", FilterList)
            If Not String.Equals(BindingSource1.Filter, Filter) Then BindingSource1.Filter = Filter

            ColumnLayoutChanged.Add(AddressOf Log_ColumnWidthChangedImpl)
            StatusSplit_SplitterMovedHandler.Add(AddressOf StatusSplit_SplitterMovedImpl)
            AppBase.Echoing.Log.Add(AddressOf AppendLog)

        End Set
    End Property

    Public ReadOnly Property SingleLinePermitHeight As Integer
        Get
            Return StatusSplit.Height + SystemInformation.Border3DSize.Height * 2 + Log.RowTemplate.Height * 2
        End Get
    End Property

    ' private implements //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    Private Class LogFilterMenu
        Inherits ToolStripMenuItem

        Private Class SubItem
            Inherits ToolStripMenuItem

            Private AppBase As AppBase
            Private BindingSource As Windows.Forms.BindingSource

            Public Sub New(ByVal AppBase As AppBase, ByVal bd As Windows.Forms.BindingSource, ByVal v As AppBase.AppendLogArgs.LogType)
                Tag = v
                Text = v.ToString
                Name = v.ToString
                BindingSource = bd
                Me.AppBase = AppBase
                CheckOnClick = True
                Checked = True
                If Not AppBase.Config.LogFilter(v) Is Nothing Then Checked = AppBase.Config.LogFilter(v).Value
            End Sub

            Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
                MyBase.OnClick(e)
                If TypeOf Tag Is AppBase.AppendLogArgs.LogType Then
                    AppBase.Config.LogFilter(Tag) = Checked
                    Dim FilterList As New Collections.Generic.List(Of String)
                    Dim EnumValues As AppBase.AppendLogArgs.LogType() = [Enum].GetValues(GetType(AppBase.AppendLogArgs.LogType))
                    For Each logtype As AppBase.AppendLogArgs.LogType In EnumValues
                        Dim isDisplayEnable As Boolean = (AppBase.Config.LogFilter(logtype) Is Nothing OrElse AppBase.Config.LogFilter(logtype).Value = True)
                        If isDisplayEnable Then FilterList.Add("Type = '" & logtype.ToString & "'")
                    Next
                    Dim Filter As String = Nothing
                    If 0 < FilterList.Count And FilterList.Count <> EnumValues.Length Then Filter = String.Join(" OR ", FilterList)
                    If Not String.Equals(BindingSource.Filter, Filter) Then BindingSource.Filter = Filter
                End If
            End Sub

            Public Sub LoadItem()
                If TypeOf Tag Is AppBase.AppendLogArgs.LogType Then
                    Checked = True
                    If Not AppBase.Config.LogFilter(Tag) Is Nothing Then Checked = AppBase.Config.LogFilter(Tag).Value
                End If
            End Sub

        End Class ' SubItem

        Public Sub New(ByVal GrpName As String, ByVal AppBase As AppBase, ByVal bd As Windows.Forms.BindingSource)
            MyBase.New(GrpName)
            For Each logtype As AppBase.AppendLogArgs.LogType In [Enum].GetValues(GetType(AppBase.AppendLogArgs.LogType))
                DropDownItems.Add(New SubItem(AppBase, bd, logtype))
            Next
        End Sub

        Protected Overrides Sub OnDropDownOpened(ByVal e As System.EventArgs)
            MyBase.OnDropDownOpened(e)
            If Not Me.Owner Is Nothing Then
                For Each item As SubItem In DropDownItems
                    item.LoadItem()
                Next
            End If
        End Sub

    End Class

    Private ReadOnly idxColTime As Integer
    Private ReadOnly idxColType As Integer
    Private ReadOnly idxColLog As Integer
    Private ReadOnly ColumnLayoutChanged As New Logic.UniqueEvents(Of Windows.Forms.DataGridViewColumnEventArgs)
    Private ReadOnly LogTypeNameLength As Integer
    Private ReadOnly StatusSplit_SplitterMovedHandler As New Logic.UniqueEvents(Of SplitterEventArgs)
    Private LogFilter1 As LogFilterMenu

    Private Sub AppendLog(ByVal e As AppBase.AppendLogArgs)

        If InvokeRequired Then

            Invoke(New AppBase.AppendLogArgs.Echoes.Handler(AddressOf AppendLog), {e})

        Else

            ' [] send log to database []

            Dim isSendDB As Boolean = True
            Select Case e.t ' empty string check
                Case AppBase.AppendLogArgs.LogType.ProgressOutline, AppBase.AppendLogArgs.LogType.Status, AppBase.AppendLogArgs.LogType.StatusDescription
                    isSendDB = Not String.IsNullOrWhiteSpace(e.Log)
            End Select

            If isSendDB Then

                Dim IsLastRowSelected_SelectedColumnIndex As Integer = -1
                If Log IsNot Nothing Then
                    ' if no row is selected or, only last row is selected
                    If Log.SelectedRows Is Nothing OrElse ((Log.SelectedRows.Count = 0) OrElse (Log.SelectedRows.Count = 1 And Log.SelectedRows(0).Index = Log.RowCount - 1)) Then
                        IsLastRowSelected_SelectedColumnIndex = 0
                        If Log.CurrentCell IsNot Nothing Then IsLastRowSelected_SelectedColumnIndex = Log.CurrentCell.ColumnIndex
                    End If
                End If

                LogDataSet1.LogTable.AddLogTableRow(DateTime.Now, e.t.ToString, e.Log)
                If 0 <= IsLastRowSelected_SelectedColumnIndex Then
                    Log.CurrentCell = Log(IsLastRowSelected_SelectedColumnIndex, Log.Rows.Count - 1)
                    Logic.DataGridViewUtility.ScrollTo(Log.Rows(Log.Rows.Count - 1))
                End If

            End If

            ' [] send log to GUI []

            Select Case e.t
                Case AppBase.AppendLogArgs.LogType.Status
                    If Status IsNot Nothing Then Status.Text = e.Log
                Case AppBase.AppendLogArgs.LogType.StatusDescription
                    If StatusDescription IsNot Nothing Then StatusDescription.Text = e.Log
            End Select

        End If

    End Sub

    Private Sub Log_CellPainting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles Log.CellPainting

        Dim partPaint As DataGridViewPaintParts = e.PaintParts
        If 0 <= e.RowIndex Then
            If 1 < Log.SelectedRows.Count Then partPaint = partPaint And (Not DataGridViewPaintParts.Focus)
            e.Paint(e.CellBounds, partPaint)
            e.Handled = True
        End If

    End Sub

    Private Sub Log_ColumnWidthChanged(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewColumnEventArgs) Handles Log.ColumnWidthChanged
        ColumnLayoutChanged.Invoke(sender, e)
    End Sub

    Private Sub Log_ColumnWidthChangedImpl(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewColumnEventArgs)
        AppBase.Config.LogListColumnInf.Present(Log)
    End Sub

    Private Sub StretchEchoPanel_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
        If AppBase IsNot Nothing Then AppBase.Echoing.Log.Remove(AddressOf AppendLog)
        StatusSplit_SplitterMovedHandler.Clear()
        ColumnLayoutChanged.Clear()
    End Sub

    Private Sub LogClear_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LogClear.Click
        LogDataSet1.LogTable.Clear()
    End Sub

    Private Function LogDisplayedColumnIndex() As Collections.Generic.List(Of Integer)
        LogDisplayedColumnIndex = New Collections.Generic.List(Of Integer)
        If Log IsNot Nothing Then
            Dim Col As DataGridViewColumn = Log.Columns.GetFirstColumn(Windows.Forms.DataGridViewElementStates.Displayed)
            While Col IsNot Nothing
                LogDisplayedColumnIndex.Add(Col.Index)
                Col = Log.Columns.GetNextColumn(Col, Windows.Forms.DataGridViewElementStates.Displayed, Windows.Forms.DataGridViewElementStates.None)
            End While
        End If
    End Function

    Private Sub LogCopy_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LogCopy.Click

        Dim ColumnIndex As Collections.Generic.List(Of Integer) = LogDisplayedColumnIndex()
        If 0 < ColumnIndex.Count Then
            Dim sb As New System.Text.StringBuilder(LogTypeNameLength * Log.SelectedRows.Count)
            Dim JoinStrings As New Collections.Generic.List(Of String)
            Dim RowIdxList As New Collections.Generic.List(Of Integer)
            For Each Row As DataGridViewRow In Log.SelectedRows
                RowIdxList.Add(Row.Index)
            Next
            RowIdxList.Sort()
            For Each RowIdx As Integer In RowIdxList
                JoinStrings.Clear()
                Dim Row As DataGridViewRow = Log.Rows(RowIdx)
                For Each idx As Integer In ColumnIndex
                    If Row.Cells(idx).Value IsNot Nothing Then JoinStrings.Add(Row.Cells(idx).Value.ToString)
                Next
                sb.AppendLine(String.Join(" ", JoinStrings))
            Next
            If 0 < sb.Length Then Clipboard.SetText(sb.ToString())
        End If

    End Sub

    Private Sub LogSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles LogSave.Click

        Dim ColumnIndex As Collections.Generic.List(Of Integer) = LogDisplayedColumnIndex()
        If 0 < ColumnIndex.Count Then

            Dim ofd As New SaveFileDialog
            ofd.FileName = Now.ToString("yyyyMMdd_HHmmss") & ".txt"
            Dim DirList() As Logic.FileSystem.Path = { _
                New Logic.FileSystem.Path(AppBase.Config.Locations.LastLogPath).Parent,
                AppBase.Config.ExportableGroup.LastExportedPath.Parent,
                AppBase.Config.ExportableGroup.LastImportedPath.Parent,
                AppBase.Config.Path.Parent,
                IO.Directory.GetDirectoryRoot(Environment.GetFolderPath(Environment.SpecialFolder.System))}
            For Each Dir As Logic.FileSystem.Path In DirList
                If Dir IsNot Nothing AndAlso Dir.DirectoryExists Then ofd.InitialDirectory = Dir.ToString
            Next

            ofd.Filter = "テキストファイル (*.txt)|*.txt"
            ofd.FilterIndex = 0
            ofd.Title = "ログの保存"
            ofd.RestoreDirectory = True
            ofd.OverwritePrompt = True

            If ofd.ShowDialog() = DialogResult.OK Then
                Try
                    Using LogWriter As New System.IO.StreamWriter(ofd.FileName, False)
                        Dim JoinStrings As New Collections.Generic.List(Of String)
                        For Each Row As DataGridViewRow In Log.Rows
                            JoinStrings.Clear()
                            For Each idx As Integer In ColumnIndex
                                If Row.Cells(idx).Value IsNot Nothing Then JoinStrings.Add(Row.Cells(idx).Value.ToString)
                            Next
                            LogWriter.WriteLine(String.Join(" ", JoinStrings))
                        Next
                    End Using
                    AppBase.Config.Locations.LastLogPath = ofd.FileName
                Catch ex As Exception
                    MsgBox("File Writing Failed")
#If DEBUG Then
                    Throw
#End If
                End Try
            End If

        End If

    End Sub

    Private Sub StatusSplit_SplitterMovedImpl(ByVal sender As System.Object, ByVal e As System.Windows.Forms.SplitterEventArgs)
        If ParentForm.WindowState = FormWindowState.Normal Or ParentForm.WindowState = FormWindowState.Maximized Then
            AppBase.Config.WindowSize(ParentForm.WindowState).StatusSplitDistance = StatusSplit.SplitterDistance
        End If
    End Sub

    Private Sub StatusSplit_SplitterMoved(ByVal sender As System.Object, ByVal e As System.Windows.Forms.SplitterEventArgs) Handles StatusSplit.SplitterMoved
        StatusSplit_SplitterMovedHandler.Invoke(sender, e)
    End Sub

    Private Sub LogContextMenu_Opening(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) Handles LogContextMenu.Opening
        If LogContextMenu.Items.Contains(LogFilter1) = False Then LogContextMenu.Items.Add(LogFilter1)
    End Sub

    Private Sub StretchEchoPanel_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Resize
        Log.ColumnHeadersVisible = (Height >= MinimumSize.Height + Log.ColumnHeadersHeight)
        If Height <= MinimumSize.Height + Log.ColumnHeadersHeight + SystemInformation.HorizontalScrollBarHeight Then
            Log.ScrollBars = Windows.Forms.ScrollBars.Vertical
        Else
            Log.ScrollBars = Windows.Forms.ScrollBars.Both
        End If
        Log.Refresh()
        Logic.DataGridViewUtility.ScrollTo(Log.CurrentCell)
    End Sub

    Private Sub Log_Enter(sender As Object, e As System.EventArgs) Handles Log.Enter
        Log.DefaultCellStyle.SelectionForeColor = SystemColors.HighlightText
        Log.DefaultCellStyle.SelectionBackColor = SystemColors.Highlight
    End Sub

    Private Sub Log_Leave(sender As Object, e As System.EventArgs) Handles Log.Leave
        Log.DefaultCellStyle.SelectionForeColor = SystemColors.ControlText
        Log.DefaultCellStyle.SelectionBackColor = SystemColors.Control
    End Sub

    Private Sub Log_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles Log.Resize
        MinimumSize = New Size(Me.MinimumSize.Width, StatusSplit.Height + SystemInformation.Border3DSize.Height * 2 + Log.RowTemplate.Height)
    End Sub

End Class
