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

    Private m_AppBase As AppBase = Nothing
    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.ArchiveItemCoverLoad.Remove(AddressOf ArchiveItemCoverLoad)
                m_AppBase.Echoing.ArchiveItemEntrySelected.Remove(AddressOf ArchiveItemEntrySelected)
                m_AppBase.Echoing.ArchiveItemSelected.Remove(AddressOf ArchiveItemSelected)
                m_AppBase.Echoing.ArchiveItemRemoved.Remove(AddressOf ArchiveItemRemoved)
            End If

            m_AppBase = value
            If value Is Nothing Then Return

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

        End Set
    End Property

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

    Private Class EntryImagePair

        Public Class Collections

            Public Class Queue

                Public Shared ReadOnly ItemCountCompactionMax As Integer = 16
                Public Shared ReadOnly ItemCountCompactionMin As Integer = Math.Max(1, ItemCountCompactionMax / 2)

                Public Sub Clear()
                    Payload.Clear()
                    System.GC.Collect()
                End Sub

                Public Function Enqueue(ByVal Src As EntryImagePair) As Boolean
                    If Src Is Nothing OrElse TryItem(Src.EntryPath) IsNot Nothing Then Return False
                    Payload.Enqueue(Src)
                    If ItemCountCompactionMax < Payload.Count Then
                        While ItemCountCompactionMin < Payload.Count
                            Payload.Dequeue()
                        End While
                        System.GC.Collect()
                    End If
                    Return True
                End Function

                Public ReadOnly Property TryItem(ByVal EntryPath As String) As EntryImagePair
                    Get
                        If EntryPath Is Nothing Then EntryPath = String.Empty
                        For Each EachItem As EntryImagePair In Payload
                            If EachItem IsNot Nothing AndAlso EachItem.EntryPath.Equals(EntryPath) Then Return EachItem
                        Next
                        Return Nothing
                    End Get
                End Property

                Default Public ReadOnly Property Item(ByVal EntryPath As String) As EntryImagePair
                    Get
                        If EntryPath Is Nothing Then EntryPath = String.Empty
                        Item = TryItem(EntryPath)
                        If Item Is Nothing Then
                            Item = New EntryImagePair(New String(EntryPath), Nothing)
                            Enqueue(Item)
                        End If
                    End Get
                End Property

                Private Payload As New System.Collections.Generic.Queue(Of EntryImagePair)

            End Class

            Public Class ArchivePathDictionary

                Public Sub Clear()
                    Payload.Clear()
                End Sub

                Public Function Contains(ByVal ArchivePath As String) As Boolean
                    If ArchivePath Is Nothing Then ArchivePath = String.Empty
                    Return Payload.ContainsKey(ArchivePath)
                End Function

                Default Public Property Item(ByVal ArchivePath As String) As EntryImagePair
                    Get
                        If ArchivePath Is Nothing Then ArchivePath = String.Empty
                        If Contains(ArchivePath) Then Return Payload(ArchivePath)
                        Item = New EntryImagePair
                        Payload(ArchivePath) = Item
                    End Get
                    Set(ByVal value As EntryImagePair)
                        If ArchivePath Is Nothing Then ArchivePath = String.Empty
                        If value Is Nothing Then value = New EntryImagePair
                        Payload(ArchivePath) = value
                    End Set
                End Property

                Public Sub Remove(ByVal ArchivePath As String)
                    If ArchivePath Is Nothing Then ArchivePath = String.Empty
                    Payload.Remove(ArchivePath)
                End Sub

                Private Payload As New System.Collections.Generic.Dictionary(Of String, EntryImagePair)

            End Class

        End Class

        Public Sub New(Optional ByVal EntryPath As String = Nothing, Optional ByVal StreamedImage As Logic.Graphics.StreamedImage = Nothing)
            Me.EntryPath = EntryPath
            Me.StreamedImage = StreamedImage
        End Sub

        Private m_EntryPath As String = String.Empty
        Public Property EntryPath As String
            Get
                Return m_EntryPath
            End Get
            Set(ByVal value As String)
                If value Is Nothing Then value = String.Empty
                m_EntryPath = value
            End Set
        End Property

        Public StreamedImage As Logic.Graphics.StreamedImage = Nothing

    End Class

    Private Class DrawItem
        Public Sub New(Optional ByVal src As Logic.Graphics.StreamedImage = Nothing)
            Payload = src
        End Sub
        Public Sub Clear()
            Payload = Nothing
        End Sub
        Public Shared Widening Operator CType(ByVal src As Logic.Graphics.StreamedImage) As DrawItem
            Return New DrawItem(src)
        End Operator
        Public ReadOnly Property Inner As Drawing.Image
            Get
                If Payload Is Nothing Then Return My.Resources.boot_image
                If Payload.Image Is Nothing Then Return My.Resources.no_image
                Return Payload.Image
            End Get
        End Property
        Private Payload As Logic.Graphics.StreamedImage = Nothing
    End Class

    Private CurrentPath As String
    Private CurrentBuffer As New EntryImagePair.Collections.Queue
    Private LastLoadImage As New EntryImagePair.Collections.ArchivePathDictionary
    Private DrawImage As New DrawItem

    Private Sub ArchiveItemEntrySelected(ByVal e As AppBase.ArchiveItemEntrySelectedArgs)

        If InvokeRequired Then

            Invoke(New AppBase.ArchiveItemEntrySelectedArgs.Echoes.Handler(AddressOf ArchiveItemEntrySelected), {e})

        Else
            Dim ImgExtentions As New Logic.FileSystem.Extention.Collection(Logic.Graphics.GDIPlusExtentions)
            Dim Ext As Logic.FileSystem.Extention = New Logic.FileSystem.Path(e.EntryPath).Extention
            If CurrentPath = e.ArchivePath And ImgExtentions.Contains(Ext) Then

                Dim EntryImagePair As EntryImagePair = CurrentBuffer.TryItem(e.EntryPath)
                If EntryImagePair Is Nothing Then
                    EntryImagePair = New EntryImagePair(New String(e.EntryPath), Nothing)
                    Dim MemBuf As IO.MemoryStream = Nothing
                    Dim p As AppBase.ArchiveProcess = Nothing
                    Dim ext_cfg As AppBase.ArchiveOptionConfig = AppBase.Config.ExportableGroup.DefaultCompressSetting(New Logic.FileSystem.Path(e.ArchivePath).Extention)
                    If ext_cfg IsNot Nothing Then p = ext_cfg.CreateProcess(AppBase)
                    If p IsNot Nothing AndAlso p.Extract(MemBuf, e.EntryPath, e.ArchivePath) = AppBase.RESULT.OK Then
                        EntryImagePair.StreamedImage = Logic.Graphics.StreamedImage.TryInstance(MemBuf)
                        If EntryImagePair.StreamedImage Is Nothing Then EntryImagePair.StreamedImage = New Logic.Graphics.StreamedImage
                        CurrentBuffer.Enqueue(EntryImagePair)
                    End If
                End If

                DrawImage = EntryImagePair.StreamedImage
                PictureBox1.Image = Nothing ' call paint event

            End If

        End If

    End Sub

    Private Sub ArchiveItemCoverLoad(ByVal e As AppBase.ArchiveItemImageLoadArgs)
        If InvokeRequired Then
            Invoke(New AppBase.ArchiveItemImageLoadArgs.Echoes.Handler(AddressOf ArchiveItemCoverLoad), {e})
        Else
            Dim load_item As EntryImagePair = LastLoadImage(e.ArchivePath)
            If CurrentPath = e.ArchivePath Then
                If e.StreamedImage IsNot Nothing Then CurrentBuffer(e.EntryPath).StreamedImage = e.StreamedImage ' queue last cover item
                DrawImage = e.StreamedImage
                PictureBox1.Image = Nothing ' call paint event
            End If
            load_item.EntryPath = e.EntryPath
            load_item.StreamedImage = e.StreamedImage
        End If
    End Sub

    Private Sub ArchiveItemRemoved(ByVal e As AppBase.ArchiveItemRemovedArgs)
        If e.Path IsNot Nothing AndAlso e.Path.Equals(CurrentPath) Then
            LastLoadImage.Remove(e.Path)
            CurrentBuffer.Clear()
            DrawImage.Clear()
            PictureBox1.Image = Nothing ' call paint event
        End If
    End Sub

    Private Sub ArchiveItemSelected(ByVal e As AppBase.ArchiveItemSelectedArgs)
        If InvokeRequired Then
            Invoke(New AppBase.ArchiveItemSelectedArgs.Echoes.Handler(AddressOf ArchiveItemSelected), {e})
        Else
            CurrentPath = Nothing
            CurrentBuffer.Clear()
            DrawImage = Nothing
            If e.Path IsNot Nothing Then
                CurrentPath = New String(e.Path)
                If LastLoadImage.Contains(CurrentPath) AndAlso LastLoadImage(CurrentPath).StreamedImage IsNot Nothing Then
                    DrawImage = LastLoadImage(CurrentPath).StreamedImage
                End If
            End If
            PictureBox1.Image = Nothing ' call paint event
        End If
    End Sub

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

    Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles PictureBox1.Paint

        Dim Box As Windows.Forms.PictureBox = TryCast(sender, Windows.Forms.PictureBox)
        If Box Is Nothing Then Return

        If DrawImage Is Nothing Or Box.Width <= 0 Or Box.Height <= 0 Then
            e.Graphics.FillRectangle(New Drawing.SolidBrush(Box.BackColor), 0, 0, Box.Width, Box.Height)
            Return
        End If

        Dim ItpMode As Drawing2D.InterpolationMode = e.Graphics.InterpolationMode
        Try
            Dim Margin(1) As Rectangle, HQDest As Rectangle, LQDestEdge(3) As Rectangle, LQSrcEdge(3) As Rectangle

            Dim Scale As New SizeF(Box.Width / DrawImage.Inner.Width, Box.Height / DrawImage.Inner.Height)
            If Scale.Height < Scale.Width Then
                Dim LogicMargin As Integer = Fix((Box.Width - DrawImage.Inner.Width * Scale.Height) / 2)
                Margin(0) = New Rectangle(0, 0, LogicMargin, Box.Height)
                Margin(1) = New Rectangle(Box.Width - LogicMargin, 0, LogicMargin, Box.Height)
                HQDest = New Rectangle(LogicMargin, 0, Box.Width - LogicMargin * 2, Box.Height)
            Else
                Dim LogicMargin As Integer = Fix((Box.Height - DrawImage.Inner.Height * Scale.Width) / 2)
                Margin(0) = New Rectangle(0, 0, Box.Width, LogicMargin)
                Margin(1) = New Rectangle(0, Box.Height - (LogicMargin), Box.Width, LogicMargin)
                HQDest = New Rectangle(0, LogicMargin, Box.Width, Box.Height - LogicMargin * 2)
            End If

            LQDestEdge(0) = New Rectangle(HQDest.X, HQDest.Y, HQDest.Width, 1) ' Top
            LQDestEdge(1) = New Rectangle(HQDest.X + HQDest.Width - 1, HQDest.Y, 1, HQDest.Height) ' Right
            LQDestEdge(2) = New Rectangle(HQDest.X, HQDest.Y + HQDest.Height - 1, HQDest.Width, 1) ' Bottom
            LQDestEdge(3) = New Rectangle(HQDest.X, HQDest.Y, 1, HQDest.Height) ' Left
            LQSrcEdge(0) = New Rectangle(0, 0, DrawImage.Inner.Width, 1) ' Top
            LQSrcEdge(1) = New Rectangle(DrawImage.Inner.Width - 1, 0, 1, DrawImage.Inner.Height) ' Right
            LQSrcEdge(2) = New Rectangle(0, DrawImage.Inner.Height - 1, DrawImage.Inner.Width, 1) ' Bottom
            LQSrcEdge(3) = New Rectangle(0, 0, 1, DrawImage.Inner.Height) ' Left

            e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.NearestNeighbor
            e.Graphics.FillRectangles(New Drawing.SolidBrush(Box.BackColor), Margin)
            For idx As Integer = 0 To 3
                e.Graphics.DrawImage(DrawImage.Inner, LQDestEdge(idx), LQSrcEdge(idx), Drawing.GraphicsUnit.Pixel)
            Next
            e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
            e.Graphics.DrawImage(DrawImage.Inner, HQDest)

        Finally
            ' restore
            e.Graphics.InterpolationMode = ItpMode
        End Try

    End Sub

End Class
