
Imports System.IO

Imports AForge
Imports AForge.Video
Imports AForge.Video.FFMPEG

Imports System.Drawing
Imports System.Drawing.Imaging

Public Class SmmProject
    Implements IVideoSource
    'Implements IProject

    '---------------------------------------------
    ' Alte Version eines StopMotionMaker Projekts
    ' Kann entfernt werden!
    '---------------------------------------------

    ' Verwendung von ImageList und ListView möglich
    ' Behält alle Bilder im Arbeitsspeicher


    Private path As String

    Private _imageList As New ImageList
    Private _frames As New List(Of SmmFrame)

    Public ReadOnly Property ImageList As ImageList
        Get
            Return _imageList
        End Get
    End Property

    Public ReadOnly Property Frames As List(Of SmmFrame)
        Get
            Return _frames
        End Get
    End Property

    Public Property Ratio As Double = 4 / 3

    Public Sub New(path As String, ratio As Double)
        Me.path = path
        Me.Ratio = ratio

        Me._imageList.ImageSize = New Size(200, 200 / Me.Ratio)
        Me._imageList.ColorDepth = ColorDepth.Depth32Bit

        'Bilder aus Projekt laden
        Dim dir As New DirectoryInfo(path)
        For Each File As FileInfo In dir.EnumerateFiles("*.jpg")
            Dim frame As New SmmFrame(File.FullName, Me._imageList.Images.Count)
            Me._imageList.Images.Add(frame.Frame)
            Me._frames.Add(frame)
        Next
    End Sub

    Public Function GetFrameCount() As Integer
        Return Me._frames.Count
    End Function

    Public Function GetFrame(index As Integer) As Image
        Return Me._frames(index).Frame
    End Function

    Public Function GetImageList() As ImageList
        Return Me._imageList
    End Function

    Public Function GetListViewItem(index As Integer) As ListViewItem
        Return New ListViewItem(Me._frames(index).Name, Me._frames(index).ImageIndex)
    End Function

    Public Sub SetFrameName(index As Integer, name As String)
        Me._frames(index).Name = name
    End Sub

    Public Sub AddFrame(frame As Image, name As String)
        Dim newFrame As New SmmFrame(frame, name, Me._imageList.Images.Count)
        Me._imageList.Images.Add(newFrame.Frame)
        Me._frames.Add(newFrame)
    End Sub

    Public Sub RemoveFrame(index As Integer)
        If index < Me._frames.Count And index >= 0 Then
            Dim f As SmmFrame = Me._frames.ElementAt(index)
            Me._frames.RemoveAt(index)
            f.Frame.Dispose()
        End If
    End Sub

    Public Sub CopyFrame(index As Integer, copyToIndex As Integer)
        If index < Me._frames.Count And index >= 0 Then
            Dim frame As SmmFrame = Me._frames(index)
            If copyToIndex >= Me._frames.Count Then
                Me._frames.Add(frame)
            Else
                Me._frames.Insert(Math.Max(copyToIndex, 0), frame)
            End If
        End If
    End Sub

    Public Sub MoveFrame(index As Integer, moveToIndex As Integer)
        If index < Me._frames.Count And index >= 0 Then
            Dim frame As SmmFrame = Me._frames(index)
            Me.RemoveFrame(index)
            If moveToIndex >= Me._frames.Count Then
                Me._frames.Add(frame)
            Else
                Me._frames.Insert(Math.Max(moveToIndex, 0), frame)
            End If
        End If
    End Sub

    Public Sub Save()
        Dim backup As New DirectoryInfo(path & IO.Path.DirectorySeparatorChar & ".backup")
        If backup.Exists Then backup.Delete(True)
        backup.Create()
        Dim dir As New DirectoryInfo(path)
        For Each File As FileInfo In dir.EnumerateFiles("*.jpg")
            File.MoveTo(backup.FullName & IO.Path.DirectorySeparatorChar & File.Name)
        Next
        Dim i As Integer = 0
        For Each f As SmmFrame In Me._frames
            Dim b As New Bitmap(f.Frame)
            IO.Path.GetInvalidPathChars()
            Dim text As String = IO.Path.GetInvalidFileNameChars().Aggregate(f.Name, Function(current, c) current.Replace(c.ToString(), "-")).Replace("_", "-")
            b.Save(path & IO.Path.DirectorySeparatorChar & i.ToString("#0000") & "_" & text & ".jpg", ImageFormat.Jpeg)
            b.Dispose()
            i += 1
        Next
    End Sub

    Public Sub Save(path As String)
        Me.path = path
        Me.Save()
    End Sub

    Public Sub SaveVideo(path As String, framesize As Size, framerate As Integer)
        Dim writer As New VideoFileWriter()
        writer.Open(path, framesize.Width, framesize.Height, framerate, VideoCodec.MPEG4)
        For Each i As SmmProject.SmmFrame In Me._frames
            writer.WriteVideoFrame(i.Frame)
        Next
        writer.Close()
    End Sub

    Function GetVideoSource(startIndex As Integer, framerate As Integer) As AForge.Video.IVideoSource
        Me.FrameStart = startIndex
        Me.Framerate = framerate
        Return Me
    End Function

    Public Class SmmFrame

        Private _frame As Image
        Private _imageIndex As Integer

        Public ReadOnly Property Frame As Image
            Get
                Return _frame
            End Get
        End Property

        Public ReadOnly Property ImageIndex As Integer
            Get
                Return _imageIndex
            End Get
        End Property

        Public Property Name As String

        Public Sub New(file As String, imageListIndex As Integer)
            Using imageStream As New FileStream(file, FileMode.Open, FileAccess.Read)
                Me._frame = Image.FromStream(imageStream)
            End Using
            Me._imageIndex = imageListIndex
            Me.Name = (IO.Path.GetFileNameWithoutExtension(file) & "_unnamed").Split("_")(1)
        End Sub

        Public Sub New(frame As Image, name As String, imageListIndex As Integer)
            Me._frame = frame
            Me._imageIndex = imageListIndex
            Me.Name = name
        End Sub
    End Class

#Region "IVideoSource"
    Public Property Framerate As Integer = 25
    Public Property FrameStart As Integer = 0

    Private WithEvents timer As New Windows.Forms.Timer()
    Private counter As Integer

    Public ReadOnly Property BytesReceived As Long Implements AForge.Video.IVideoSource.BytesReceived
        Get
            Return 1
        End Get
    End Property

    Public ReadOnly Property FramesReceived As Integer Implements AForge.Video.IVideoSource.FramesReceived
        Get
            Return Me._frames.Count
        End Get
    End Property

    Public ReadOnly Property IsRunning As Boolean Implements AForge.Video.IVideoSource.IsRunning
        Get
            Return counter > FrameStart
        End Get
    End Property

    Public ReadOnly Property Source As String Implements AForge.Video.IVideoSource.Source
        Get
            Return "SmmProject"
        End Get
    End Property

    Public Event NewFrame(sender As Object, eventArgs As AForge.Video.NewFrameEventArgs) Implements AForge.Video.IVideoSource.NewFrame
    Public Event PlayingFinished(sender As Object, reason As AForge.Video.ReasonToFinishPlaying) Implements AForge.Video.IVideoSource.PlayingFinished
    Public Event VideoSourceError(sender As Object, eventArgs As AForge.Video.VideoSourceErrorEventArgs) Implements AForge.Video.IVideoSource.VideoSourceError

    Private Sub timer_Tick(sender As Object, e As System.EventArgs) Handles timer.Tick
        If counter = Me._frames.Count Then
            timer.Stop()
            counter = 0
            RaiseEvent PlayingFinished(Me, ReasonToFinishPlaying.EndOfStreamReached)
            Return
        End If
        RaiseEvent NewFrame(Me, New NewFrameEventArgs(Me._frames(counter).Frame))
        counter += 1
    End Sub

    Public Sub Start() Implements AForge.Video.IVideoSource.Start
        If Me._frames.Count > 0 Then
            counter = FrameStart
            timer.Interval = 1000 / Me.Framerate
            timer.Start()
            RaiseEvent NewFrame(Me, New NewFrameEventArgs(Me._frames(counter).Frame))
            counter += 1
        End If
    End Sub

    Public Sub [Stop]() Implements AForge.Video.IVideoSource.Stop
        timer.Stop()
        counter = 0
        RaiseEvent PlayingFinished(Me, ReasonToFinishPlaying.StoppedByUser)
    End Sub

    Public Sub SignalToStop() Implements AForge.Video.IVideoSource.SignalToStop
        Me.Stop()
    End Sub

    Public Sub WaitForStop() Implements AForge.Video.IVideoSource.WaitForStop

    End Sub
#End Region
End Class
