
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Drawing.Imaging

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

Public Class frmMain

    Private project As IProject
    Private cam As Camera
    Private framerate As Integer = 25

    Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        'Shortcuts definieren (wird hier gemacht, weil der Designer kein Keys.Home hat [Bugfix])
        Me.cmiDelete.ShortcutKeys = Keys.Delete
        Me.cmiMoveToBegin.ShortcutKeys = Keys.Control Or Keys.Home
        Me.cmiMoveUp.ShortcutKeys = Keys.Control Or Keys.Up
        Me.cmiMoveDown.ShortcutKeys = Keys.Control Or Keys.Down
        Me.cmiMoveToEnd.ShortcutKeys = Keys.Control Or Keys.End
        Me.cmiCopyToBegin.ShortcutKeys = Keys.Alt Or Keys.Home
        Me.cmiCopyAfter.ShortcutKeys = Keys.Alt Or Keys.C
        Me.cmiCopyToEnd.ShortcutKeys = Keys.Alt Or Keys.End
        Me.cmiRename.ShortcutKeys = Keys.F2
        'Camera Menü initialisieren
        Dim tis As New List(Of ToolStripMenuItem)
        For Each i As KeyValuePair(Of String, String) In Camera.getDevices()
            Dim ti As New ToolStripMenuItem()
            Dim d As New VideoCaptureDevice(i.Key)
            Dim fs As Size = d.VideoCapabilities.First().FrameSize
            Dim fr As Integer = d.VideoCapabilities.First().FrameRate
            ti.Text = i.Value & " (" & fs.Width & "x" & fs.Height & "@" & fr & "fps)"
            ti.Tag = i.Key
            AddHandler ti.Click, Sub(tsender As System.Object, te As System.EventArgs)
                                     For Each tii As ToolStripMenuItem In Me.miCamera.DropDownItems
                                         tii.Checked = False
                                     Next
                                     Dim b As ToolStripMenuItem = CType(tsender, ToolStripMenuItem)
                                     b.Checked = True
                                     If Me.cam IsNot Nothing Then
                                         Me.cam.stop()
                                         Me.cam.Dispose()
                                     End If
                                     Me.cam = New Camera(CStr(b.Tag))
                                     If Me.project IsNot Nothing And playing = False Then
                                         Me.VideoPlayer.VideoSource = Me.cam.getDevice()
                                         Me.VideoPlayer.Start()
                                     End If
                                 End Sub
            Me.miCamera.DropDownItems.Add(ti)
            tis.Add(ti)
        Next
        'Projekt zurücksetzen
        Me.CloseProject()
        'Kamera einrichten
        If Camera.getDevices.Count() = 0 Then
            MessageBox.Show("Es wurde keine Kamera gefunden!", "StopMotionMaker", MessageBoxButtons.OK, MessageBoxIcon.Error)
            Me.Close()
            Return
        End If

        Dim camMoniker As String = My.Settings("LastCam")
        If camMoniker IsNot Nothing AndAlso Camera.getDevices.ContainsKey(camMoniker) Then
            For Each mi As ToolStripMenuItem In Me.miCamera.DropDownItems
                If CStr(mi.Tag) = camMoniker Then
                    Me.cam = New Camera(CStr(mi.Tag))
                    mi.Checked = True
                    Exit For
                End If
            Next
        End If
        If Me.cam Is Nothing Then
            Dim mi As ToolStripMenuItem = Me.miCamera.DropDownItems.Item(0)
            Me.cam = New Camera(CStr(mi.Tag))
            mi.Checked = True
        End If

        Dim path As String = My.Settings("LastProject")
        If path IsNot Nothing AndAlso path <> "" AndAlso IO.Directory.Exists(path) Then
            OpenProject(path)
        End If
    End Sub

    Private Sub frmMain2_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
        If Me.project IsNot Nothing Then
            My.Settings("LastProject") = Me.project.GetProjectPath()
        Else
            My.Settings("LastProject") = ""
        End If
        If Me.cam IsNot Nothing Then
            My.Settings("LastCam") = Me.cam.getDevice.Source
        Else
            My.Settings("LastCam") = ""
        End If
        Me.CloseProject()
    End Sub

    Private Sub OpenProject(path As String)
        Me.CloseProject()
        'Neues Projekt erstellen
        Me.project = New SmmProject2(path)
        'Bilderliste vorbereiten
        Me.ilvHistory.ImageSize = Me.cam.FrameSize
        Me.ilvHistory.ListSize = Me.project.GetFrameCount()
        'Videoplayer vorbereiten
        Me.VideoPlayer.Visible = True
        Me.VideoPanel_Resize(Nothing, Nothing)
        Me.VideoPlayer.VideoSource = Me.cam.getDevice()
        Me.VideoPlayer.Start()
        'Schaltflächen und Menüs aktivieren
        Me.btnPlay.Enabled = True
        Me.btnSnapshot.Enabled = True
        Me.miClose.Enabled = True
        Me.miSave.Enabled = True
        Me.miSaveAs.Enabled = True
        Me.miExportVideo.Enabled = True
        'Formular Beschriftung anpassen
        Me.Text = "StopMotionMaker - " & IO.Path.GetFileName(path)
    End Sub

    Private Sub CloseProject()
        'Kamera stoppen
        If Me.cam IsNot Nothing Then Me.cam.stop()
        'Projekt speichern
        If Me.project IsNot Nothing Then Me.project.Save()
        Me.project = Nothing
        'Videoplayer stoppen
        Me.playing = False
        Me.VideoPlayer.Stop()
        Me.VideoPlayer.Visible = False
        'Alle Objekte auf Standardwert setzen
        Me.selectedImageIndex = -1
        Me.selectedImageOverlay = Nothing
        Me.ilvHistory.ListSize = 0
        'Schaltflächen und Menüs deaktivieren
        Me.btnPlay.Enabled = False
        btnPlay.Image = My.Resources.play
        Me.btnSnapshot.Enabled = False
        Me.miClose.Enabled = False
        Me.miSave.Enabled = False
        Me.miSaveAs.Enabled = False
        Me.miExportVideo.Enabled = False
        'Formular Beschriftung anpassen
        Me.Text = "StopMotionMaker"
    End Sub

    Private Sub MarkUnsaved()
        If Not Me.Text.EndsWith("*") Then
            Me.Text = Me.Text & "*"
        End If
    End Sub

    Private Sub MarkSaved()
        If Me.Text.EndsWith("*") Then
            Me.Text = Me.Text.Substring(0, Me.Text.Length - 1)
        End If
    End Sub

    Private Sub tmAutoSave_Tick(sender As System.Object, e As System.EventArgs) Handles tmAutoSave.Tick
        If Me.project IsNot Nothing And Me.miAutosave.Checked Then
            Me.project.Save()
            Me.MarkSaved()
        End If
    End Sub

#Region "Menü"
    Private Sub miExit_Click(sender As System.Object, e As System.EventArgs) Handles miExit.Click
        Me.Close()
    End Sub

    Private Sub miNew_Click(sender As System.Object, e As System.EventArgs) Handles miNew.Click
        Dim dlg As New FolderBrowserDialog With {
            .ShowNewFolderButton = True,
            .Description = "Bitte öffnen oder erstellen Sie einen leeren Ordner, in dem das neue Projekt gestartet wird."
        }
        If dlg.ShowDialog() = Windows.Forms.DialogResult.Cancel Then Return
        If IO.Directory.Exists(dlg.SelectedPath) And Not IO.Directory.EnumerateFileSystemEntries(dlg.SelectedPath).Any() Then
            Me.OpenProject(dlg.SelectedPath)
        Else
            MessageBox.Show("Ordner ist nicht vorhanden oder nicht leer!", "StopMotionMaker", MessageBoxButtons.OK, MessageBoxIcon.Warning)
        End If
    End Sub

    Private Sub miOpen_Click(sender As System.Object, e As System.EventArgs) Handles miOpen.Click
        Dim dlg As New FolderBrowserDialog With {
            .ShowNewFolderButton = False,
            .Description = "Bitte öffnen Sie einen bestehenden Ordner, aus dem das neue Projekt gestartet wird."
        }
        If dlg.ShowDialog() = Windows.Forms.DialogResult.Cancel Then Return
        If IO.Directory.Exists(dlg.SelectedPath) Then
            Me.OpenProject(dlg.SelectedPath)
        Else
            MessageBox.Show("Ordner ist nicht vorhanden!", "StopMotionMaker", MessageBoxButtons.OK, MessageBoxIcon.Warning)
        End If
    End Sub

    Private Sub miSave_Click(sender As System.Object, e As System.EventArgs) Handles miSave.Click
        Me.project.Save()
        Me.MarkSaved()
    End Sub

    Private Sub miSaveAs_Click(sender As System.Object, e As System.EventArgs) Handles miSaveAs.Click
        Dim dlg As New FolderBrowserDialog With {
            .ShowNewFolderButton = True,
            .Description = "Bitte öffnen oder erstellen Sie einen leeren Ordner, in dem das neue Projekt gestartet wird."
        }
        If dlg.ShowDialog() = Windows.Forms.DialogResult.Cancel Then Return
        If IO.Directory.Exists(dlg.SelectedPath) And Not IO.Directory.EnumerateFileSystemEntries(dlg.SelectedPath).Any() Then
            Me.project.Save(dlg.SelectedPath)
            Me.Text = "StopMotionMaker - " & IO.Path.GetFileName(dlg.SelectedPath)
        Else
            MessageBox.Show("Ordner ist nicht vorhanden oder nicht leer!", "StopMotionMaker", MessageBoxButtons.OK, MessageBoxIcon.Warning)
        End If
        Me.MarkSaved()
    End Sub

    Private Sub miExportVideo_Click(sender As System.Object, e As System.EventArgs) Handles miExportVideo.Click

        'Planet 13
        ' Hier wurde Vorspann/Abspann deaktiviert (Wird mit Moviemaker gemacht)

        'Dim dlg As New frmVideo(Me.project)
        Me.tmAutoSave.Stop()
        'dlg.FrameSize = Me.cam.FrameSize
        'dlg.FrameRate = Me.framerate
        'dlg.ShowDialog(Me)
        Dim dlg As New SaveFileDialog With {
            .Filter = "AVI-Video (*.avi)|*.avi|Alle Dateien (*.*)|*.*",
            .RestoreDirectory = True
        }
        If dlg.ShowDialog() = Windows.Forms.DialogResult.Cancel Then Return
        Me.project.SaveVideo(dlg.FileName, Me.cam.FrameSize, Me.framerate)
        Me.tmAutoSave.Start()
    End Sub

    Private Sub miClose_Click(sender As System.Object, e As System.EventArgs) Handles miClose.Click
        Me.CloseProject()
    End Sub

    Private Sub miFps_Click(sender As System.Object, e As System.EventArgs) Handles miFps30.Click, miFps25.Click, miFps20.Click, miFps15.Click, miFps10.Click, miFps7.Click, miFps5.Click, miFps4.Click, miFps3.Click, miFps2.Click, miFps1.Click
        Dim b As ToolStripMenuItem = CType(sender, ToolStripMenuItem)
        miFps30.Checked = False : miFps25.Checked = False : miFps20.Checked = False : miFps15.Checked = False : miFps10.Checked = False : miFps7.Checked = False : miFps5.Checked = False : miFps4.Checked = False : miFps3.Checked = False : miFps2.Checked = False : miFps1.Checked = False
        b.Checked = True
        framerate = Integer.Parse(b.Text)
    End Sub

    Private Sub miShowHelp_Click(sender As System.Object, e As System.EventArgs) Handles miShowHelp.Click
        Throw New NotSupportedException()
    End Sub

    Private Sub miCamera_Click(sender As System.Object, e As System.EventArgs) Handles miCameraInfo.Click
        If Me.cam IsNot Nothing Then
            MessageBox.Show("Kamera: " & Me.cam.getDevice.Source & vbCrLf & _
                        "Framesize: " & Me.cam.FrameSize.Width & "x" & Me.cam.FrameSize.Height & vbCrLf & Me.cam.GetInfo(), "StopMotionMaker", MessageBoxButtons.OK, MessageBoxIcon.Information)
        End If
    End Sub

    Private Sub miInfo_Click(sender As System.Object, e As System.EventArgs) Handles miInfo.Click
        Dim info As New frmSplash()
        info.ShowDialog(Me)
    End Sub
#End Region

#Region "Panel"
    Private playing As Boolean

    Private Sub frmMain_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown
        If ActiveControl.GetType() Is GetType(TextBox) Then Return
        If e.KeyData = Keys.Enter Then
            'Snapshot
            Me.btnSnapshot.PerformClick()
            e.Handled = True
            e.SuppressKeyPress = True
        ElseIf e.KeyData = Keys.Space Then
            'Play/Pause
            Me.btnPlay.PerformClick()
            e.Handled = True
            e.SuppressKeyPress = True
        End If
    End Sub

    Private Sub btnPlay_Click(sender As System.Object, e As System.EventArgs) Handles btnPlay.Click
        If Me.playing = False Then
            'Play Video
            Me.playing = True
            btnSnapshot.Enabled = False
            btnPlay.Image = My.Resources.pause
            If Me.miPlayBegin.Checked = False Then
                Me.VideoPlayer.VideoSource = Me.project.GetVideoSource(selectedImageIndex, Me.framerate)
            Else
                Me.VideoPlayer.VideoSource = Me.project.GetVideoSource(0, Me.framerate)
            End If
            Me.VideoPlayer.Start()
        Else
            'Pause Video/Play Camera
            Me.playing = False
            btnPlay.Image = My.Resources.play
            btnSnapshot.Enabled = True
            Me.VideoPlayer.Stop()
            Me.VideoPlayer.VideoSource = Me.cam.getDevice()
            Me.VideoPlayer.Start()
            Me.ilvHistory.Focus()
        End If
    End Sub

    Private Sub btnSnapshot_Click(sender As System.Object, e As System.EventArgs) Handles btnSnapshot.Click
        Me.project.AddFrame(cam.getFrame(), DateTime.Now.ToString("hh:mm:ss"))
        Me.MarkUnsaved()
        Me.ilvHistory.ListSize = Me.project.GetFrameCount()
        Me.ilvHistory.SelectedIndex = Me.project.GetFrameCount() - 1
        Me.ilvHistory.Focus()
    End Sub
#End Region

#Region "History ImageList"
    Private selectedImageIndex As Integer
    Private selectedImageOverlay As Image

    Private Sub ilvHistory_AfterLabelEdit(sender As Object, e As ImageListView.LabelEditEventArgs) Handles ilvHistory.AfterLabelEdit
        If e.Label <> "" Then
            Me.project.SetFrameName(e.Index, e.Label)
            Me.MarkUnsaved()
        End If
    End Sub

    Private Sub ilvHistory_RetrieveListItem(sender As Object, e As ImageListView.RetrieveListItemEventArgs) Handles ilvHistory.RetrieveListItem
        e.FileName = Me.project.GetFrameFileName(e.Index)
        e.Label = Me.project.GetFrameName(e.Index)
    End Sub

    Private Sub ilvHistory_SelectedIndexChanged(sender As Object, e As ImageListView.SelectedIndexChangedEventArgs) Handles ilvHistory.SelectedIndexChanged
        Me.selectedImageIndex = e.Index
        If e.Index > -1 Then
            If Me.selectedImageOverlay IsNot Nothing Then Me.selectedImageOverlay.Dispose()
            Me.selectedImageOverlay = Me.SetOpacity(Me.project.GetFrame(e.Index), 0.3)
        Else
            Me.selectedImageOverlay = Nothing
        End If
    End Sub
#End Region

#Region "History Context Menü"
    Private Sub HistoryContextMenu_Opening(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles HistoryContextMenu.Opening
        If Me.selectedImageIndex < 0 Then
            e.Cancel = True
        End If
    End Sub

    Private Sub cmiRename_Click(sender As System.Object, e As System.EventArgs) Handles cmiRename.Click
        If Me.selectedImageIndex >= 0 Then ilvHistory.BeginEdit()
    End Sub

    Private Sub cmiMoveToBegin_Click(sender As System.Object, e As System.EventArgs) Handles cmiMoveToBegin.Click
        If Me.selectedImageIndex >= 0 Then
            Me.project.MoveFrame(Me.selectedImageIndex, 0)
            Me.ilvHistory.SelectedIndex = 0
            Me.MarkUnsaved()
        End If
    End Sub

    Private Sub cmiMoveUp_Click(sender As System.Object, e As System.EventArgs) Handles cmiMoveUp.Click
        If Me.selectedImageIndex >= 0 Then
            Me.project.MoveFrame(Me.selectedImageIndex, Me.selectedImageIndex - 1)
            Me.ilvHistory.SelectedIndex = Math.Max(Me.selectedImageIndex - 1, 0)
            Me.MarkUnsaved()
        End If
    End Sub

    Private Sub cmiMoveDown_Click(sender As System.Object, e As System.EventArgs) Handles cmiMoveDown.Click
        If Me.selectedImageIndex >= 0 Then
            Me.project.MoveFrame(Me.selectedImageIndex, Me.selectedImageIndex + 1)
            Me.ilvHistory.SelectedIndex = Me.selectedImageIndex + 1
            Me.MarkUnsaved()
        End If
    End Sub

    Private Sub cmiMoveToEnd_Click(sender As System.Object, e As System.EventArgs) Handles cmiMoveToEnd.Click
        If Me.selectedImageIndex >= 0 Then
            Me.project.MoveFrame(Me.selectedImageIndex, Me.project.GetFrameCount())
            Me.ilvHistory.SelectedIndex = Me.project.GetFrameCount() - 1
            Me.MarkUnsaved()
        End If
    End Sub

    Private Sub cmiCopyToBegin_Click(sender As System.Object, e As System.EventArgs) Handles cmiCopyToBegin.Click
        If Me.selectedImageIndex >= 0 Then
            Me.project.CopyFrame(Me.selectedImageIndex, 0)
            Me.ilvHistory.ListSize = Me.project.GetFrameCount()
            Me.ilvHistory.SelectedIndex = Me.selectedImageIndex + 1
            Me.MarkUnsaved()
        End If
    End Sub

    Private Sub cmiCopyAfter_Click(sender As System.Object, e As System.EventArgs) Handles cmiCopyAfter.Click
        If Me.selectedImageIndex >= 0 Then
            Me.project.CopyFrame(Me.selectedImageIndex, Me.selectedImageIndex + 1)
            Me.ilvHistory.ListSize = Me.project.GetFrameCount()
            Me.MarkUnsaved()
        End If
    End Sub

    Private Sub cmiCopyToEnd_Click(sender As System.Object, e As System.EventArgs) Handles cmiCopyToEnd.Click
        If Me.selectedImageIndex >= 0 Then
            Me.project.CopyFrame(Me.selectedImageIndex, Me.project.GetFrameCount())
            Me.ilvHistory.ListSize = Me.project.GetFrameCount()
            Me.MarkUnsaved()
        End If
    End Sub

    Private Sub cmiDelete_Click(sender As Object, e As System.EventArgs) Handles cmiDelete.Click
        If Me.selectedImageIndex >= 0 Then
            Me.project.RemoveFrame(Me.selectedImageIndex)
            Me.ilvHistory.ListSize = Me.project.GetFrameCount()
            Me.ilvHistory.SelectedIndex = Me.ilvHistory.ListSize - 1
            Me.MarkUnsaved()
        End If
    End Sub
#End Region

#Region "VideoPlayer"
    Private Sub VideoPanel_Resize(sender As System.Object, e As System.EventArgs) Handles VideoPanel.Resize
        If Me.project IsNot Nothing Then
            If VideoPanel.Width > Me.cam.FrameSize.Width And VideoPanel.Height > Me.cam.FrameSize.Height Then
                VideoPlayer.AutoSizeControl = True
                VideoPlayer.Left = (VideoPanel.Width - VideoPlayer.Width) / 2
                VideoPlayer.Top = (VideoPanel.Height - VideoPlayer.Height) / 2
            Else
                VideoPlayer.AutoSizeControl = False
                Dim ratio As Double = Me.cam.FrameSize.Width / Me.cam.FrameSize.Height
                Dim r As Double = VideoPanel.Width / VideoPanel.Height
                If r < ratio Then
                    'Skaliere nach Breite
                    VideoPlayer.Width = VideoPanel.Width
                    VideoPlayer.Height = VideoPanel.Width / ratio
                    VideoPlayer.Left = 0
                    VideoPlayer.Top = (VideoPanel.Height - VideoPlayer.Height) / 2
                Else
                    'Skaliere nach Höhe
                    VideoPlayer.Width = VideoPanel.Height * ratio
                    VideoPlayer.Height = VideoPanel.Height
                    VideoPlayer.Left = (VideoPanel.Width - VideoPlayer.Width) / 2
                    VideoPlayer.Top = 0
                End If
            End If
        End If
    End Sub

    Private Function SetOpacity(image As Image, opacity As Single) As Image
        Dim colorMatrix = New ColorMatrix()
        colorMatrix.Matrix33 = opacity
        Dim imageAttributes = New ImageAttributes()
        imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.[Default], ColorAdjustType.Bitmap)
        Dim output = New Bitmap(image.Width, image.Height)
        Using gfx = Graphics.FromImage(output)
            gfx.SmoothingMode = SmoothingMode.AntiAlias
            gfx.DrawImage(image, New Rectangle(0, 0, image.Width, image.Height), 0, 0, image.Width, image.Height, _
             GraphicsUnit.Pixel, imageAttributes)
        End Using
        Return output
    End Function

    Private Sub VideoPlayer_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles VideoPlayer.Paint
        Dim g As Graphics = e.Graphics
        'Zeichne Overlay
        If selectedImageOverlay IsNot Nothing And Me.miOverlay.Checked And Me.playing = False Then
            g.DrawImage(selectedImageOverlay, 0, 0, Me.VideoPlayer.Width - 2, Me.VideoPlayer.Height - 2)
        End If
    End Sub

    'Private oldsSelectedImageIndex As Integer = -1
    'Private selectedImageOverlay As Image

    'Private Sub VideoPlayer_NewFrame(sender As Object, ByRef image As System.Drawing.Bitmap) Handles VideoPlayer.NewFrame
    '    Dim i As Integer = Me.selectedImageIndex
    '    If Me.oldsSelectedImageIndex <> i And i >= 0 Then
    '        'Erzeuge Overlay
    '        Me.oldsSelectedImageIndex = i
    '        If Me.selectedImageOverlay IsNot Nothing Then Me.selectedImageOverlay.Dispose()
    '        Me.selectedImageOverlay = Me.SetOpacity(Me.project.GetFrame(Me.oldsSelectedImageIndex), 0.3)
    '    End If

    '    Using g As Graphics = Graphics.FromImage(image)
    '        'Zeichne Overlay
    '        If i >= 0 And Me.miOverlay.Checked And Me.playing = False Then
    '            g.DrawImage(selectedImageOverlay, 0, 0)
    '        End If
    '    End Using
    'End Sub

    Private Sub VideoPlayer_PlayingFinished(sender As Object, reason As AForge.Video.ReasonToFinishPlaying) Handles VideoPlayer.PlayingFinished
        If Me.playing Then
            'Hier aufpassen wegen Threadsicherheit!
            'Das funktioniert nur weil in SmmProject der Forms Timer verwendet wurde.
            'Camera ruft diese methode jedoch in einem anderen Thread auf.
            Me.btnPlay.PerformClick()
        End If
    End Sub
#End Region

End Class