' *
' * 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)
' *

Partial Public Class AppBase
    Partial Public Class Archive
        Partial Public Class Threading

            Public Class ArchiveTask
                Implements IAppBase
                Implements Logic.Threading.TaskQueueingThread.ITask

                Public Sub New(ByVal AppBase As AppBase, ByVal ArgsCollection As Collections.ObjectModel.ReadOnlyCollection(Of ArchiveItemScanArgs))
                    Me.AppBase = AppBase
                    Me.ArgsCollection = ArgsCollection
                End Sub

                Public Property AppBase As AppBase Implements IAppBase.AppBase
                Public Property ArgsCollection As Collections.ObjectModel.ReadOnlyCollection(Of ArchiveItemScanArgs)
                Public Sub Task(ByVal SenderIsStopRequested As Logic.Threading.SignalHandler) Implements Logic.Threading.TaskQueueingThread.ITask.Task

                    If (AppBase Is Nothing Or ArgsCollection Is Nothing) OrElse ArgsCollection.Count <= 0 Then Return

                    AppBase.Echoing.TaskStartEcho("ArchiveTask")

                    If AppBase.Config.Locations.WorkFolder.IsReady = False Then
                        AppBase.Echoing.TaskExitedEcho("Temporary Folder Error")
                        Return
                    End If

                    Dim ctProgress As Integer = 0

                    For Each Args As ArchiveItemScanArgs In ArgsCollection

                        ' user cancel check
                        If SenderIsStopRequested IsNot Nothing AndAlso SenderIsStopRequested() = True Then
                            AppBase.Echoing.TaskExitedEcho("User Cancel Exited")
                            Return
                        End If

                        ' do each task.
                        ' if critical error then, remain task is canceled
                        If DoEachTaskIsCriticalError(Args, SenderIsStopRequested) Then
                            AppBase.Echoing.TaskExitedEcho(String.Format("UserProcessCriticalFailed[{0}] {1}", Args.Data.PresetName, Args.Path))
                            Return
                        End If

                        ctProgress += 1
                        AppBase.Echoing.Progress.Echo(ctProgress, ArgsCollection.Count)

                    Next

                    AppBase.Echoing.TaskCompletedEcho()

                End Sub

                Public Function DoEachTaskIsCriticalError(ByVal Args As ArchiveItemScanArgs, ByVal SenderIsStopRequested As Logic.Threading.SignalHandler) As Boolean

                    DoEachTaskIsCriticalError = False ' init value by not critical error

                    ' resource list to release
                    Dim LocalCopiedArchivePath As Logic.FileSystem.Path = Nothing
                    Dim LocalExtractedDirectory As Logic.FileSystem.Path = Nothing

                    If Args.Path Is Nothing Or Args.Data Is Nothing Then Return False
                    If Args.Data.IsFilter Then Return False
                    If Args.Data.IsUserChecked = False Then Return False

                    Dim Preset As AppBase.ArchiveTaskPreset = AppBase.ArchiveTaskPresets(Args.Data.PresetName)
                    If Args.Data.IsUserCmd = False Or Preset Is Nothing Then Return False

                    Dim ArchiveConfig As AppBase.ArchiveOptionConfigCollection = Preset.CompressSetting
                    If ArchiveConfig Is Nothing Then ArchiveConfig = AppBase.DefaultCompressSetting

                    Dim TaskTargetArchivePath As Logic.FileSystem.Path = Args.Path.ToString
                    Dim ArchiveOption As ArchiveOptionConfig = ArchiveConfig(TaskTargetArchivePath.Extention)
                    If ArchiveOption Is Nothing Then Return False

                    Dim ArchiveProcess As ArchiveProcess = ArchiveOption.CreateProcess(AppBase, SenderIsStopRequested)
                    If ArchiveProcess Is Nothing Then Return False

                    AppBase.Echoing.Log.Echo(String.Format("[{0}] {1}", Args.Data.PresetName, Args.Path.Name), AppendLogArgs.LogType.ProgressOutline)
                    AppBase.Echoing.Log.Echo(String.Format("UserProcess[{0}] {1}", Args.Data.PresetName, Args.Path), AppendLogArgs.LogType.Status)

                    Try
                        ' [] Load To Temporary Archive []

                        If AppBase.ArchiveTaskConfig.LoadOption.IsLocalCopy Then

                            TaskTargetArchivePath.Parent = AppBase.Config.Locations.WorkFolder.TemporaryDir

                            ' // reserve release //
                            LocalCopiedArchivePath = TaskTargetArchivePath

                            Dim RetryCount As Integer = 0, IsError As Boolean = True
                            Do
                                ' // do file copy //
                                If BinaryFileCopy(AppBase, TaskTargetArchivePath, Args.Path, SenderIsStopRequested) <> RESULT.OK Then
                                    AppBase.Echoing.Log.Echo("FileCopyError")
                                    Exit Do
                                End If
                                If AppBase.ArchiveTaskConfig.LoadOption.IsCrcCheck = False Then
                                    IsError = False ' succeed pattern exit
                                    Exit Do
                                Else
                                    ' // do crc check //
                                    AppBase.Echoing.Log.Echo("CrcCheck " & TaskTargetArchivePath.ToString, AppendLogArgs.LogType.Status)
                                    Select Case ArchiveProcess.CrcCheck(TaskTargetArchivePath.ToString)
                                        Case AppBase.RESULT.OK
                                            IsError = False ' succeed pattern exit
                                            Exit Do
                                        Case AppBase.RESULT.ARCHIVE_PASSWORD_MISSMATCH
                                            AppBase.Echoing.Log.Echo("Skip(Password Protected) " & Args.Path.ToString)
                                            Return False
                                        Case Else
                                            AppBase.Echoing.Log.Echo(String.Format("CrcErr({0}) {1}", RetryCount, Args.Path.ToString))
                                    End Select
                                End If

                                RetryCount += 1

                            Loop While RetryCount < AppBase.ArchiveTaskConfig.LoadOption.CopyRetryCount

                            If IsError Then Return True ' fatal error

                        Else

                        End If

                        ' [] Extract Archive []

                        If Preset.IsExtract Then
                            AppBase.Echoing.Log.Echo("Extract " & TaskTargetArchivePath.ToString, AppendLogArgs.LogType.Status)
                            LocalExtractedDirectory = TaskTargetArchivePath.ToString
                            LocalExtractedDirectory.Extention = String.Empty

                            Select Case ArchiveProcess.Extract(LocalExtractedDirectory.ToString, TaskTargetArchivePath.ToString)
                                Case AppBase.RESULT.OK
                                    ' do nothing
                                Case AppBase.RESULT.ARCHIVE_PASSWORD_MISSMATCH
                                    AppBase.Echoing.Log.Echo("Skip(Password Protected) " & Args.Path.ToString)
                                    Return False
                                Case Else
                                    AppBase.Echoing.Log.Echo("Archive Extract Error " & Args.Path.ToString)
                                    Return True
                            End Select
                        End If

                        ArchiveOption = Nothing
                        ArchiveProcess = Nothing

                        ' [] User Process []
                        AppBase.Echoing.Log.Echo("ProcessCall", AppendLogArgs.LogType.Status)

                        Dim Builder As New System.Text.StringBuilder
                        If Not String.IsNullOrWhiteSpace(Preset.ScanCmd) Then
                            Dim Reader As New IO.StringReader(Preset.UserCmd)
                            Do
                                Dim LineOfCmd As String = Reader.ReadLine
                                If LineOfCmd Is Nothing Then Exit Do
                                If String.IsNullOrWhiteSpace(LineOfCmd) Then Continue Do

                                Dim CmdInf As New CommandInfo(AppBase)
                                CmdInf.ArcPath = Args.Path.ToString
                                CmdInf.ExtractDir = LocalExtractedDirectory.ToString

                                Dim ParseCmd As CommandInfo.ParseResult = CmdInf.Parse(LineOfCmd)
                                If Not String.IsNullOrWhiteSpace(ParseCmd.Result.Cmd) Then
                                    Dim RebuildLine As New Collections.Generic.List(Of String)
                                    RebuildLine.Add(ParseCmd.Result.Cmd)
                                    If ParseCmd.IsCreateWindow Then RebuildLine.Add("%CreateWindow%")
                                    If ParseCmd.IsIgnoreStdOut Then RebuildLine.Add("%IgnoreStdOut%")
                                    Builder.AppendLine(String.Join(" ", RebuildLine) & ChrW(0))
                                End If
                            Loop
                        End If
                        Dim UserCmdListBlock As String = Builder.ToString

                        If Not String.IsNullOrWhiteSpace(UserCmdListBlock) Then

                            Dim MMF As IO.MemoryMappedFiles.MemoryMappedFile = Nothing
                            Try
                                MMF = IO.MemoryMappedFiles.MemoryMappedFile.CreateNew(AppBase.PipeStarter.SharingMemoryMappedFile.CommandListMapName, System.Text.Encoding.Unicode.GetBytes(UserCmdListBlock).Length)
                                Using Stream As IO.MemoryMappedFiles.MemoryMappedViewStream = MMF.CreateViewStream()
                                    Using Writer As New IO.StreamWriter(Stream, System.Text.Encoding.Unicode)
                                        Writer.Write(UserCmdListBlock)
                                    End Using
                                End Using
                                Dim p As New Process
                                p.StartInfo.FileName = "Hameln.exe"
                                p.StartInfo.Arguments = String.Format("{0}{1}",
                                                                      Logic.CommandlineParser.Arguments.DASHCODE,
                                                                      AppBase.PipeStarter.ModeArgumentParameterSet(AppBase.PipeStarter.Mode.ExecCommandList).Code)
                                p.Start()

                                Dim CatchBuffer As New Collections.Generic.List(Of String)
                                Dim CatchFlag As System.Threading.Mutex = Nothing
                                Dim CompleteFlagRef As System.Threading.Mutex = Nothing
                                Try
                                    CompleteFlagRef = Logic.Threading.PallingWaitMutexExist(AppBase.PipeStarter.SharingMemoryMappedFile.CommandListConsoleBufferWriteCompleteFlagName, 10, , Function() p.HasExited)
                                    If CompleteFlagRef IsNot Nothing Then

                                        CatchBuffer.Clear()
                                        Using CatchMMF As IO.MemoryMappedFiles.MemoryMappedFile = IO.MemoryMappedFiles.MemoryMappedFile.OpenExisting(AppBase.PipeStarter.SharingMemoryMappedFile.CommandListConsoleBufferMapName)
                                            Using Stream As IO.MemoryMappedFiles.MemoryMappedViewStream = CatchMMF.CreateViewStream()
                                                Using CatchReader As New IO.StreamReader(Stream, System.Text.Encoding.Unicode)
                                                    Do
                                                        Dim Line As String = CatchReader.ReadLine
                                                        If Line Is Nothing Then Exit Do
                                                        Line = Line.Split(New Char() {ChrW(0)})(0)
                                                        If Not String.IsNullOrWhiteSpace(Line) Then CatchBuffer.Add(Line)
                                                    Loop
                                                End Using
                                            End Using
                                        End Using

                                        CatchFlag = Logic.Threading.CreateOrWaitMutex(AppBase.PipeStarter.SharingMemoryMappedFile.CommandListConsoleBufferWriteCompleteCatchFlagName)
                                        CompleteFlagRef.WaitOne()
                                        CompleteFlagRef.ReleaseMutex()
                                        CompleteFlagRef.Dispose()
                                        CompleteFlagRef = Nothing
                                        CatchFlag.ReleaseMutex()
                                        CatchFlag.Dispose()
                                        CatchFlag = Nothing

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

                                p.WaitForExit()

                                For Each OutputLine As String In CatchBuffer
                                    AppBase.Echoing.Log.Echo(OutputLine, AppendLogArgs.LogType.ConsoleOut)
                                Next

                            Catch ex As Exception
#If DEBUG Then
                                Throw
#End If
                            Finally
                                ' // Clear MemoryMappedFile //
                                If MMF IsNot Nothing Then
                                    MMF.Dispose()
                                    MMF = Nothing
                                End If
                            End Try
                        End If ' Not String.IsNullOrWhiteSpace(UserCmdListBlock)

                        ' [] Compress []
                        AppBase.Echoing.Log.Echo("Compress", AppendLogArgs.LogType.Status)
                        If LocalExtractedDirectory.DirectoryExists And Preset.IsCompress Then

                            Dim CompressPath As Logic.FileSystem.Path = Args.Data.MirrorArchivePath
                            Dim MirrorParent As Logic.FileSystem.Path = CompressPath.Parent

                            If AppBase.ArchiveTaskConfig.CreateOption.IsCrcCheck Then
                                CompressPath.Parent = AppBase.Config.Locations.WorkFolder.TemporaryDir
                                ' // reserve release //
                                If LocalCopiedArchivePath IsNot Nothing Then LocalCopiedArchivePath.DeleteFile()
                                LocalCopiedArchivePath = CompressPath
                            End If

                            If MirrorParent.DirectoryExists = False AndAlso AppBase.ArchiveTaskConfig.CreateOption.IsCreateDirectory Then
                                Try
                                    IO.Directory.CreateDirectory(MirrorParent.ToString)
                                Catch
                                    AppBase.Echoing.Log.Echo(String.Format("Dir Create Error {0}", Args.Data.MirrorArchivePath))
                                    Return False
                                End Try
                            End If

                            If MirrorParent.DirectoryExists = False Then
                                AppBase.Echoing.Log.Echo(String.Format("Dir Not Existed {0}", Args.Data.MirrorArchivePath))
                                Return False
                            End If

                            ArchiveOption = ArchiveConfig.SelectedExt
                            ArchiveProcess = ArchiveOption.CreateProcess(AppBase, SenderIsStopRequested)

                            If ArchiveProcess.CompressDir(CompressPath.ToString, LocalExtractedDirectory.ToString) <> RESULT.OK Then
                                AppBase.Echoing.Log.Echo(String.Format("Compress Error {0}", Args.Data.MirrorArchivePath))
                                Return False
                            End If

                            If AppBase.ArchiveTaskConfig.CreateOption.IsCrcCheck Then
                                ' // copy local completed archive //
                                Dim RetryCount As Integer = 0, IsError As Boolean = True
                                Do
                                    ' // do file copy //
                                    If BinaryFileCopy(AppBase, Args.Data.MirrorArchivePath, CompressPath, SenderIsStopRequested) <> RESULT.OK Then
                                        AppBase.Echoing.Log.Echo("FileCopyError")
                                        Exit Do ' critical failed pattern
                                    End If

                                    ' // do crc check //
                                    AppBase.Echoing.Log.Echo("CrcCheck", AppendLogArgs.LogType.ProgressOutline)
                                    AppBase.Echoing.Log.Echo(Args.Data.MirrorArchivePath)
                                    Select Case ArchiveProcess.CrcCheck(Args.Data.MirrorArchivePath)
                                        Case AppBase.RESULT.OK
                                            IsError = False ' succeed pattern exit
                                            Exit Do
                                        Case AppBase.RESULT.ARCHIVE_PASSWORD_MISSMATCH
                                            AppBase.Echoing.Log.Echo("CrcErr(Password Protected) " & Args.Data.MirrorArchivePath)
                                            Exit Do ' critical failed pattern
                                        Case Else
                                            AppBase.Echoing.Log.Echo(String.Format("CrcErr({0}) {1}", RetryCount, Args.Data.MirrorArchivePath))
                                    End Select

                                    RetryCount += 1

                                Loop While RetryCount < AppBase.ArchiveTaskConfig.LoadOption.CopyRetryCount

                                If IsError Then Return True ' critical error

                            End If

                        End If

                    Catch ex As Exception
                        Throw

                    Finally
                        If LocalCopiedArchivePath IsNot Nothing Then LocalCopiedArchivePath.DeleteFile()
                        If LocalExtractedDirectory IsNot Nothing Then LocalExtractedDirectory.DeleteDirectory()
                    End Try

                End Function

            End Class ' AppBase.Archive.Threading.ArchiveTask

        End Class ' AppBase.Archive.Threading
    End Class ' AppBase.Archive
End Class ' AppBase

