' *
' * 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 ArchiveInfoPanel
    Implements IAppBase

    ' Constructor ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    Public Sub New()

        InitializeComponent()

        Dim render As New AppDlg.ToolStripCustomRenderer
        ToolStrip1.Renderer = render
        ToolStrip2.Renderer = render

        ArchiveName.Image = AppBase.ShellIcon.GetSingletonInstance(AppBase.ShellIcon.HANDLE.CANCEL)

        idxColName = Entries.Columns("ColName").Index
        idxColPath = Entries.Columns("ColPath").Index
        idxColExtractSize = Entries.Columns("ColExtractSize").Index
        idxColCompressedSize = Entries.Columns("ColCompressedSize").Index
        idxColCompressionRatio = Entries.Columns("ColCompressionRatio").Index
        idxColModifiedTime = Entries.Columns("ColModifiedTime").Index
        idxColCRC = Entries.Columns("ColCRC").Index

        DefaultOpenButton.Image = AppBase.ShellIcon.GetSingletonInstance(AppBase.ShellIcon.HANDLE.OPENAS_FILE)
        OpenAsImageButton.Image = AppBase.ShellIcon.GetSingletonInstance(AppBase.ShellIcon.HANDLE.OPENAS_IMG)
        OpenAsSoundButton.Image = AppBase.ShellIcon.GetSingletonInstance(AppBase.ShellIcon.HANDLE.OPENAS_SND)

    End Sub

    ' Properties //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

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

            If m_AppBase IsNot Nothing Then
                m_AppBase.Echoing.ArchiveItemSelected.Remove(AddressOf ArchiveItemSelected)
                m_AppBase.Echoing.ArchiveItemRemoved.Remove(AddressOf ArchiveItemRemoved)
            End If

            m_AppBase = value

            Entries.ContextMenuStrip = Nothing
            ColumnLayoutChanged.Clear()

            If m_AppBase Is Nothing Then Return

            m_AppBase.Config.EntryListColumnInf.Apply(Entries)

            m_AppBase.Echoing.ArchiveItemSelected.Add(AddressOf ArchiveItemSelected)
            m_AppBase.Echoing.ArchiveItemRemoved.Add(AddressOf ArchiveItemRemoved)

            Entries.ContextMenuStrip = New Logic.DataGridViewUtility.ColumnVisibleControlMenu(Nothing, m_AppBase.Config.EntryListColumnInf, New EntryContextMenu(Me))
            ColumnLayoutChanged.Add(AddressOf Entries_ColumnLayoutChangedImpl)

        End Set
    End Property

    Private m_CurrentPath As String
    Public ReadOnly Property CurrentPath As String
        Get
            Return m_CurrentPath
        End Get
    End Property

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

    Private ColumnLayoutChanged As New Logic.UniqueEvents(Of System.Windows.Forms.DataGridViewColumnEventArgs)
    Private ReadOnly idxColName As Integer
    Private ReadOnly idxColPath As Integer
    Private ReadOnly idxColExtractSize As Integer
    Private ReadOnly idxColCompressedSize As Integer
    Private ReadOnly idxColCompressionRatio As Integer
    Private ReadOnly idxColCreatedTime As Integer
    Private ReadOnly idxColModifiedTime As Integer
    Private ReadOnly idxColCRC As Integer

    Private Sub ArchiveItemRemoved(ByVal e As AppBase.ArchiveItemRemovedArgs)
        If e.Path.Equals(CurrentPath) Then Entries_Clear()
    End Sub

    Private Sub Entries_Clear()
        m_CurrentPath = Nothing
        ArchiveName.Text = Nothing
        ArchiveName.Image = AppBase.ShellIcon.GetSingletonInstance(AppBase.ShellIcon.HANDLE.CANCEL)
        Entries.Rows.Clear()
    End Sub

    Private Sub ArchiveItemSelected(ByVal e As AppBase.ArchiveItemSelectedArgs)
        If InvokeRequired Then
            Invoke(New AppBase.ArchiveItemSelectedArgs.Echoes.Handler(AddressOf ArchiveItemSelected), {e})
        Else
            Entries_Clear()
            m_CurrentPath = e.Path
            ArchiveName.Text = New Logic.FileSystem.Path(e.Path).Name.ToString
            ArchiveName.Image = Logic.FileSystem.GetSystemIcon(e.Path)
            If ArchiveName.Image Is Nothing Then ArchiveName.Image = AppBase.ShellIcon.GetSingletonInstance(AppBase.ShellIcon.HANDLE.CANCEL)
            If Not e.GetEntries Is Nothing AndAlso 0 < e.GetEntries.Length Then
                Dim CellValues(Entries.ColumnCount - 1) As Object
                Dim AddRows(e.GetEntries.Length - 1) As DataGridViewRow, idx As Integer = 0
                For Each entry As AppBase.Archive.EntryInfo In e.GetEntries
                    If IO.Path.HasExtension(entry.EntryPath) Then
                        AddRows(idx) = New DataGridViewRow
                        CellValues(idxColName) = New Logic.FileSystem.Path(entry.EntryPath).Name.ToString
                        CellValues(idxColPath) = entry.EntryPath
                        CellValues(idxColCompressedSize) = entry.CompressedSize
                        CellValues(idxColCompressionRatio) = entry.CompressionRatio
                        CellValues(idxColCRC) = entry.CrcHex8
                        CellValues(idxColExtractSize) = entry.ExtractSize
                        CellValues(idxColModifiedTime) = entry.ModifiedTime
                        AddRows(idx).CreateCells(Entries, CellValues)
                        AddRows(idx).Height = Entries.RowTemplate.Height
                        idx += 1
                    End If
                Next
                ReDim Preserve AddRows(idx - 1)
                Entries.Rows.AddRange(AddRows)
            End If
        End If
    End Sub

    Private Sub Entries_CellMouseDoubleClick(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles Entries.CellMouseDoubleClick
        If Entries.SelectedRows.Count = 1 AndAlso 0 <= Entries.SelectedRows(0).Index Then
            AppBase.Echoing.ArchiveItemEntrySelected.Echo(CurrentPath, Entries.SelectedRows(0).Cells(idxColPath).Value.ToString)
        End If
    End Sub

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

        Dim partPaint As DataGridViewPaintParts = e.PaintParts And (Not DataGridViewPaintParts.Focus)

        If 0 <= e.RowIndex Then

            Try
                Select Case e.ColumnIndex
                    Case idxColName
                        e.PaintBackground(e.ClipBounds, (partPaint And DataGridViewPaintParts.SelectionBackground) = DataGridViewPaintParts.SelectionBackground)
                        Dim path As String = Entries(idxColPath, e.RowIndex).Value.ToString
                        If Not String.IsNullOrWhiteSpace(path) Then
                            Dim img As Drawing.Bitmap = Logic.FileSystem.GetSystemIcon(path)
                            e.Graphics.DrawImage(img, e.CellBounds.X + 2, e.CellBounds.Y + CInt(Fix((e.CellBounds.Height - img.Height) / 2)), img.Width, img.Height)
                        End If
                        partPaint = partPaint And (Not DataGridViewPaintParts.Background)
                        e.Paint(e.CellBounds, partPaint)
                        e.Handled = True

                    Case Else
                        e.Paint(e.CellBounds, partPaint)
                        e.Handled = True

                End Select


            Catch ex As Exception
#If DEBUG Then
                Throw
#End If
            End Try

        End If

    End Sub

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

    Private Sub Entries_ColumnLayoutChangedImpl(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewColumnEventArgs)
        AppBase.Config.EntryListColumnInf.Present(Entries)
    End Sub

    Private Sub Entries_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Entries.Disposed
        ColumnLayoutChanged.Clear()
    End Sub

    Private Sub DefaultOpenButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles DefaultOpenButton.Click
        If IO.File.Exists(CurrentPath) Then
            Try
                Process.Start(CurrentPath)
            Catch ex As Exception
#If DEBUG Then
                Throw
#End If
            End Try
        End If
    End Sub

    Private Sub OpenAsImageButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenAsImageButton.Click
        If IO.File.Exists(CurrentPath) And AppBase IsNot Nothing Then
            Dim CmdInf As New CommandInfo(AppBase)
            CmdInf.ArcPath = CurrentPath
            If Entries.SelectedRows.Count = 1 Then CmdInf.EntryPath = Entries(idxColPath, Entries.SelectedRows(0).Index).ToString
            Dim Parse As CommandInfo.ParseResult = CmdInf.Parse(AppBase.Config.OpenCmdAsImage)
            If Not String.IsNullOrWhiteSpace(Parse.Result.Process) Then
                Try
                    Process.Start(Parse.Result.Process, Parse.Result.Arguments)
                Catch ex As Exception
                    MsgBox("Process Start Failed!")
                End Try
            End If
        End If
    End Sub

    Private Sub OpenAsSoundButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OpenAsSoundButton.Click
        If IO.File.Exists(CurrentPath) And AppBase IsNot Nothing Then
            Dim CmdInf As New CommandInfo(AppBase)
            CmdInf.ArcPath = CurrentPath
            If Entries.SelectedRows.Count = 1 Then CmdInf.EntryPath = Entries(idxColPath, Entries.SelectedRows(0).Index).ToString
            Dim Parse As CommandInfo.ParseResult = CmdInf.Parse(AppBase.Config.OpenCmdAsSound)
            If Not String.IsNullOrWhiteSpace(Parse.Result.Process) Then
                Try
                    Process.Start(Parse.Result.Process, Parse.Result.Arguments)
                Catch ex As Exception
                    MsgBox("Process Start Failed!")
                End Try
            End If
        End If
    End Sub

    Private Function GetSelectedEntryItems(ByVal PresetName As String) As Collections.ObjectModel.ReadOnlyCollection(Of String)

        If String.IsNullOrWhiteSpace(PresetName) Or AppBase Is Nothing Or Entries.SelectedRows.Count <= 0 Or IO.File.Exists(CurrentPath) = False Then Return Nothing

        Dim Preset As AppBase.EntryTaskPreset = AppBase.EntryTaskPresets(PresetName)
        If Preset Is Nothing Then Return Nothing

        Dim EntryItems As New Collections.Generic.List(Of String)
        For Each Row As DataGridViewRow In Entries.SelectedRows

            If Entries(idxColPath, Row.Index).Value Is Nothing Then Continue For

            Dim Ext As String = IO.Path.GetExtension(Entries(idxColPath, Row.Index).Value.ToString)
            If String.IsNullOrWhiteSpace(Ext) Then Continue For

            If Preset.PositiveExtentionList IsNot Nothing AndAlso (0 < Preset.PositiveExtentionList.Count And Preset.PositiveExtentionList.Contains(Ext) = False) Then Continue For
            If Preset.NegativeExtentionList IsNot Nothing AndAlso (0 < Preset.NegativeExtentionList.Count And Preset.NegativeExtentionList.Contains(Ext) = True) Then Continue For

            EntryItems.Add(Entries(idxColPath, Row.Index).Value.ToString)

        Next

        If 0 < EntryItems.Count Then EntryItems.Sort()
        Return EntryItems.AsReadOnly

    End Function

    Private Sub EntryContext_Click(ByVal PresetName As String)

        Dim Preset As AppBase.EntryTaskPreset = AppBase.EntryTaskPresets(PresetName)
        Dim EntryPath As Collections.ObjectModel.ReadOnlyCollection(Of String) = GetSelectedEntryItems(PresetName)
        If (AppBase Is Nothing) Or (Preset Is Nothing) Or (EntryPath Is Nothing) OrElse (EntryPath.Count <= 0) Then Return
        If Not (Preset.IsMultiSelect Or EntryPath.Count = 1) Then Return

        Dim p As New Process

        Dim CmdInf As New CommandInfo(AppBase)
        CmdInf.ArcPath = CurrentPath
        CmdInf.EntryPath = EntryPath(0)
        Dim ParseCmd As CommandInfo.ParseResult = CmdInf.Parse(Preset.EntryTaskCommand)

        p.StartInfo.UseShellExecute = True
        p.StartInfo.WorkingDirectory = IO.Path.GetDirectoryName(Reflection.Assembly.GetEntryAssembly.Location)

        Dim MMF As IO.MemoryMappedFiles.MemoryMappedFile = Nothing

        If Not Preset.IsPipeToStdIn Then

            p.StartInfo.CreateNoWindow = Not ParseCmd.IsCreateWindow
            p.StartInfo.WindowStyle = IIf(ParseCmd.IsCreateWindow, Diagnostics.ProcessWindowStyle.Normal, Diagnostics.ProcessWindowStyle.Hidden)
            p.StartInfo.FileName = ParseCmd.Result.Process
            p.StartInfo.Arguments = ParseCmd.Result.Arguments
            If String.IsNullOrWhiteSpace(p.StartInfo.FileName) Then
                AppBase.Echoing.Log.Echo("ArchiveEntry Command Error On Calling : process name is empty")
                Return
            End If
            p.Start()

        Else

            p.StartInfo.CreateNoWindow = False
            p.StartInfo.WindowStyle = IIf(ParseCmd.IsCreateWindow, Diagnostics.ProcessWindowStyle.Normal, Diagnostics.ProcessWindowStyle.Minimized)

            If String.IsNullOrWhiteSpace(Preset.EntryTaskCommand) Then
                AppBase.Echoing.Log.Echo("ArchiveEntry Command Error On Calling : process name is empty")
                Return
            End If

            Dim Mode As AppBase.PipeStarter.Mode
            Dim CatchFlag As System.Threading.Mutex = Nothing
            Dim CompleteFlagRef As System.Threading.Mutex = Nothing
            Try
                If Preset.IsMultiSelect = False And EntryPath.Count = 1 Then

                    Mode = AppBase.PipeStarter.Mode.SizedBinary

                    Dim Config As AppBase.ArchiveOptionConfig = AppBase.DefaultCompressSetting(Logic.FileSystem.GetPathExtention(CurrentPath))
                    If Not Config Is Nothing Then

                        Dim ExtProc As AppBase.ArchiveProcess = Config.CreateProcess(AppBase, Nothing)
                        Dim Buf As IO.MemoryStream = Nothing

                        If ExtProc.Extract(Buf, EntryPath(0), CurrentPath) <> AppBase.RESULT.OK Then
                            Throw New ApplicationException("Archive Extract Error")
                        Else
                            If Integer.MaxValue < Buf.Length Then Throw New ApplicationException(String.Format("Entry file size error: {0} byte", Buf.Length))
                            Buf.Position = 0
                        End If

                        Dim LenBuf() As Byte = BitConverter.GetBytes(CInt(Buf.Length))

                        MMF = IO.MemoryMappedFiles.MemoryMappedFile.CreateNew(AppBase.PipeStarter.SharingMemoryMappedFile.MapName, LenBuf.Length + Buf.Length)
                        Using Stream As IO.MemoryMappedFiles.MemoryMappedViewStream = MMF.CreateViewStream()
                            Stream.Write(LenBuf, 0, LenBuf.Length)
                            Buf.CopyTo(Stream)
                        End Using

                    End If

                Else

                    Mode = AppBase.PipeStarter.Mode.UnicodeTextList

                    Dim WriteBlock As String = Nothing
                    Dim sb As New System.Text.StringBuilder
                    For Each Path In EntryPath
                        sb.AppendLine(Path & ChrW(0))
                    Next
                    WriteBlock = sb.ToString
                    sb = Nothing

                    MMF = IO.MemoryMappedFiles.MemoryMappedFile.CreateNew(AppBase.PipeStarter.SharingMemoryMappedFile.MapName, System.Text.Encoding.Unicode.GetBytes(WriteBlock).Length)
                    Using Stream As IO.MemoryMappedFiles.MemoryMappedViewStream = MMF.CreateViewStream()
                        Using Writer As New IO.StreamWriter(Stream, System.Text.Encoding.Unicode)
                            Writer.Write(WriteBlock)
                        End Using
                    End Using

                End If
                p.StartInfo.FileName = "cmd"
                p.StartInfo.Arguments = String.Format("/U /{0} Hameln.exe {1}{2}|{3}",
                                                      IIf(ParseCmd.IsStayPrompt, "K", "C"),
                                                      Logic.CommandlineParser.Arguments.DASHCODE,
                                                      AppBase.PipeStarter.ModeArgumentParameterSet(Mode).Code,
                                                      ParseCmd.Result.Cmd)
                p.Start()
                CompleteFlagRef = Logic.Threading.PallingWaitMutexExist(AppBase.PipeStarter.SharingMemoryMappedFile.ReadCompleteFlagName, 10, , Function() p.HasExited)
                If CompleteFlagRef IsNot Nothing Then
                    CatchFlag = Logic.Threading.CreateOrWaitMutex(AppBase.PipeStarter.SharingMemoryMappedFile.ReadCompleteCatchFlagName)
                    CompleteFlagRef.WaitOne()
                    CompleteFlagRef.ReleaseMutex()
                    CompleteFlagRef.Dispose()
                    CompleteFlagRef = Nothing
                    CatchFlag.ReleaseMutex()
                    CatchFlag.Dispose()
                    CatchFlag = Nothing
                End If

            Catch ex As Exception
                Throw
            Finally
                If CompleteFlagRef IsNot Nothing Then CompleteFlagRef.Dispose()
                If CatchFlag IsNot Nothing Then CatchFlag.Dispose()
                If MMF IsNot Nothing Then MMF.Dispose()
            End Try

        End If

    End Sub

    Private Class EntryContextMenu
        Inherits ContextMenuStrip

        Public Sub New(ByVal ParentPanel As ArchiveInfoPanel)
            Me.ParentPanel = ParentPanel
        End Sub

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

        Private ParentPanel As ArchiveInfoPanel

        Protected Overrides Sub OnOpening(ByVal e As System.ComponentModel.CancelEventArgs)

            e.Cancel = False
            Items.Clear()
            Items.Add(New ArchiveReloadMenuItem("最新の情報に更新する"))
            Items.Add(New ToolStripSeparator)

            If (0 < ParentPanel.Entries.SelectedRows.Count And ParentPanel.AppBase IsNot Nothing) AndAlso 0 < ParentPanel.AppBase.EntryTaskPresets.Count Then
                For Each Preset As AppBase.EntryTaskPreset In ParentPanel.AppBase.EntryTaskPresets
                    If String.IsNullOrWhiteSpace(Preset.EntryTaskCommand) = False Then
                        Dim EntryItems As Collections.ObjectModel.ReadOnlyCollection(Of String) = ParentPanel.GetSelectedEntryItems(Preset.Name)
                        If 0 < EntryItems.Count AndAlso (Preset.IsMultiSelect Or EntryItems.Count = 1) Then Items.Add(New Item(Preset.Name))
                    End If
                Next
            End If

            If Items.Count <= 2 Then
                Dim AddItem As New ToolStripMenuItem("対応する有効なエントリー操作コマンドが登録されていません")
                Items.Add(AddItem)
                AddItem.Enabled = False
            End If

            MyBase.OnOpening(e)

        End Sub

        Private Class ArchiveReloadMenuItem
            Inherits ToolStripMenuItem

            Public Sub New(ByVal Name As String)
                MyBase.New(Name)
            End Sub

            Private ReadOnly Property ParentPanel As ArchiveInfoPanel
                Get
                    Dim Owner As EntryContextMenu = TryCast(Me.Owner, EntryContextMenu)
                    If Owner IsNot Nothing Then Return Owner.ParentPanel
                    Return Nothing
                End Get
            End Property

            Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
                MyBase.OnClick(e)
                Dim Owner As ArchiveInfoPanel = ParentPanel
                If Owner Is Nothing OrElse Owner.AppBase Is Nothing OrElse IO.File.Exists(Owner.CurrentPath) = False Then Return
                Owner.AppBase.MainTask.EnumEntry(Owner.CurrentPath)
            End Sub

        End Class

        Private Class Item
            Inherits ToolStripMenuItem

            Public Sub New(ByVal Name As String)
                MyBase.New(Name)
                Me.Name = Name
            End Sub

            Private ReadOnly Property ParentPanel As ArchiveInfoPanel
                Get
                    Dim Owner As EntryContextMenu = TryCast(Me.Owner, EntryContextMenu)
                    If Owner IsNot Nothing Then Return Owner.ParentPanel
                    Return Nothing
                End Get
            End Property

            Protected Overrides Sub OnClick(ByVal e As System.EventArgs)
                MyBase.OnClick(e)
                Dim Owner As ArchiveInfoPanel = ParentPanel
                If Owner IsNot Nothing Then Owner.EntryContext_Click(Name)
            End Sub

        End Class

    End Class

    Private Sub ArchiveInfoPanel_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
        AppBase = Nothing
    End Sub

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

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

End Class
