
Imports System.IO

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

Imports System.Drawing
Imports System.Drawing.Imaging

Imports System.Xml

Public Class SmmProject2
    Implements IProject

    Public Property VideoTitle As String Implements IProject.VideoTitle
    Public Property VideoCredits As String Implements IProject.VideoCredits
    Public Property VideoPath As String Implements IProject.VideoPath

    Private path As String

    Private frames As New List(Of SmmFrame)

    Public Sub New(path As String)
        Me.VideoTitle = ""
        Me.VideoCredits = ""
        Me.VideoPath = ""

        'Projekt laden
        Dim dir As New DirectoryInfo(path)
        Me.path = dir.FullName
        Dim files As FileInfo() = dir.GetFiles(".SMMProject")
        If files.Count() > 0 Then
            Dim settings As New XmlReaderSettings()
            settings.ConformanceLevel = ConformanceLevel.Document
            settings.IgnoreWhitespace = True
            settings.IgnoreComments = True

            Dim rd As XmlReader = XmlReader.Create(files.First().FullName, settings)
            rd.Read()
            rd.ReadStartElement("stopmotionmaker")
            If rd.IsStartElement() And rd.Name = "title" Then
                rd.Read()
                Me.VideoTitle = rd.Value
                rd.Read()
            End If
            rd.Read()
            If rd.IsStartElement() And rd.Name = "credits" Then
                rd.Read()
                Me.VideoCredits = rd.Value.Replace(vbLf, vbCrLf)
                rd.Read()
            End If
            rd.Read()
            While rd.Read()
                If rd.NodeType = XmlNodeType.Element And rd.Name = "frame" Then
                    Me.frames.Add(New SmmFrame(rd.Item("file"), rd.Item("name")))
                End If
            End While
            rd.Close()
        End If
    End Sub

    Public Function GetProjectPath() As String Implements IProject.GetProjectPath
        Return Me.path
    End Function

    Public Function GetFrameFileName(index As Integer) As String Implements IProject.GetFrameFileName
        Return Me.path & "\" & Me.frames(index).File
    End Function

    Public Function GetFrameName(index As Integer) As String Implements IProject.GetFrameName
        Return Me.frames(index).Name
    End Function

    Public Function GetFrame(index As Integer) As System.Drawing.Image Implements IProject.GetFrame
        Return Bitmap.FromFile(Me.path & "\" & Me.frames(index).File)
    End Function

    Public Function GetFrameCount() As Integer Implements IProject.GetFrameCount
        Return Me.frames.Count
    End Function

    Public Function GetImageList() As System.Windows.Forms.ImageList Implements IProject.GetImageList
        Throw New NotSupportedException
    End Function

    Public Function GetListViewItem(index As Integer) As System.Windows.Forms.ListViewItem Implements IProject.GetListViewItem
        Throw New NotSupportedException
    End Function



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

    Public Sub AddFrame(frame As System.Drawing.Image, name As String) Implements IProject.AddFrame
        Dim f As New SmmFrame(System.Guid.NewGuid.ToString() & ".jpg", name)
        frame.Save(Me.path & "\" & f.File, ImageFormat.Jpeg)
        Me.frames.Add(f)
    End Sub

    Public Sub CopyFrame(index As Integer, copyToIndex As Integer) Implements IProject.CopyFrame
        If index < Me.frames.Count And index >= 0 Then
            Dim frame As SmmFrame = New 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) Implements IProject.MoveFrame
        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 RemoveFrame(index As Integer) Implements IProject.RemoveFrame
        If index < Me.frames.Count And index >= 0 Then
            Me.frames.RemoveAt(index)
        End If
    End Sub




    Public Sub Save() Implements IProject.Save
        Me.Save(Me.path)
    End Sub

    Public Sub Save(path As String) Implements IProject.Save
        Dim settings As New XmlWriterSettings()
        settings.ConformanceLevel = ConformanceLevel.Document
        settings.Encoding = New System.Text.UTF8Encoding(False)
        settings.Indent = True
        settings.OmitXmlDeclaration = False
        Dim wr As XmlWriter = XmlWriter.Create(path & "\.SMMProject", settings)
        wr.WriteStartDocument()
        wr.WriteStartElement("stopmotionmaker")
        'Title
        wr.WriteStartElement("title")
        wr.WriteCData(Me.VideoTitle)
        wr.WriteEndElement()
        'Credits
        wr.WriteStartElement("credits")
        wr.WriteCData(Me.VideoCredits)
        wr.WriteEndElement()
        'Frames
        wr.WriteStartElement("frames")
        For Each f As SmmFrame In Me.frames
            wr.WriteStartElement("frame")
            wr.WriteAttributeString("file", f.File)
            wr.WriteAttributeString("name", f.Name)
            wr.WriteEndElement()
        Next
        wr.WriteEndDocument()
        wr.Close()
        If path <> Me.path Then
            For Each f As SmmFrame In Me.frames
                IO.File.Copy(Me.path & "\" & f.File, path & "\" & f.File)
            Next
            Me.path = path
        End If
    End Sub

    Public Sub SaveVideo(path As String, framesize As System.Drawing.Size, framerate As Integer) Implements IProject.SaveVideo
        'Planet 13
        'Möglichkeit für Vorspann/Abspann wurde entfernt (wird mit Moviemaker gemacht)

        'Dim background As Image = New Bitmap(framesize.Width, framesize.Height)
        'Using g As Graphics = Graphics.FromImage(background)
        '    If IO.File.Exists(Application.StartupPath & "\background.png") Then
        '        g.DrawImage(Bitmap.FromFile(Application.StartupPath & "\background.png"), 0, 0, framesize.Width, framesize.Height)
        '    Else
        '        g.FillRectangle(Brushes.Black, 0, 0, framesize.Width, framesize.Height)
        '    End If
        'End Using
        ''Title
        'Dim title As Image = background.Clone
        'Using g As Graphics = Graphics.FromImage(title)
        '    Dim f As New Font("MS Trebuchet", (framesize.Height / 10), FontStyle.Bold, GraphicsUnit.Pixel)
        '    g.DrawString(Me.VideoTitle, f, Brushes.LightGray,
        '                 (framesize.Width - g.MeasureString(Me.VideoTitle, f).Width) / 2,
        '                 (framesize.Height - g.MeasureString(Me.VideoTitle, f).Height) / 2)
        'End Using
        ''Credits
        'Dim credits As New List(Of Image)
        'Dim lines As String() = (Me.VideoCredits.Trim() & vbCrLf).Replace(vbLf, "").Split(vbCr)
        'Dim linesCount As Integer = lines.Count() - 1
        'Dim linesPerPage As Integer = 10

        'Dim spacing As Integer = framesize.Height / 6
        'Dim lineheight As Integer = (framesize.Height - (spacing * 2)) / 10
        'Dim font As New Font("MS Trebuchet", lineheight - 10, FontStyle.Bold, GraphicsUnit.Pixel)

        'For i As Integer = 0 To linesCount - 1 Step linesPerPage
        '    Dim image As Image = background.Clone()
        '    Using g As Graphics = Graphics.FromImage(image)
        '        For j As Integer = 0 To linesPerPage - 1
        '            If i + j < lines.Count() Then
        '                g.DrawString(lines(i + j), font, Brushes.LightGray,
        '                             spacing,
        '                             spacing + (lineheight * j))
        '            End If
        '        Next
        '    End Using
        '    credits.Add(image)
        'Next

        'Video
        Dim writer As New VideoFileWriter()
        writer.Open(path, framesize.Width, framesize.Height, framerate, VideoCodec.MPEG4)
        'For j As Integer = 1 To framerate * 3
        '    writer.WriteVideoFrame(title)
        'Next
        For Each i As SmmFrame In Me.frames
            Dim b As Image = Bitmap.FromFile(Me.path & "\" & i.File)
            writer.WriteVideoFrame(b)
            b.Dispose()
        Next
        'For Each i As Image In credits
        '    For j As Integer = 1 To framerate * 2
        '        writer.WriteVideoFrame(i)
        '    Next
        'Next
        writer.Close()
        'title.Dispose()
        'For Each i As Image In credits
        '    i.Dispose()
        'Next
        'background.Dispose()
    End Sub

    Public Function GetVideoSource(startIndex As Integer, framerate As Integer) As AForge.Video.IVideoSource Implements IProject.GetVideoSource
        Return New SmmVideo(Me.path, Me.frames, Math.Max(0, startIndex), framerate)
    End Function

    Private Class SmmFrame
        Public Property File As String
        Public Property Name As String

        Public Sub New(file As String, name As String)
            Me.File = file
            Me.Name = name
        End Sub

        Public Sub New(frame As SmmFrame)
            Me.File = frame.File
            Me.Name = frame.Name
        End Sub
    End Class

    Private Class SmmVideo
        Implements IVideoSource

        Private path As String
        Private frames As List(Of SmmFrame)
        Private framerate As Integer
        Private frameindex As Integer
        Private counter As Integer

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

        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

        Public Sub New(path As String, frames As List(Of SmmFrame), frameindex As Integer, framerate As Integer)
            Me.path = path
            Me.frames = frames
            Me.frameindex = frameindex
            Me.framerate = framerate
            Me.counter = frameindex
        End Sub

        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 Me.counter > Me.frameindex
            End Get
        End Property

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

        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(Bitmap.FromFile(Me.path & "\" & Me.frames(counter).File)))
            counter += 1
        End Sub

        Public Sub Start() Implements AForge.Video.IVideoSource.Start
            If Me.frames.Count > 0 Then
                counter = Me.frameindex
                timer.Interval = 1000 / Me.framerate
                timer.Start()
                RaiseEvent NewFrame(Me, New NewFrameEventArgs(Bitmap.FromFile(Me.path & "\" & Me.frames(counter).File)))
                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 Class

End Class
