Imports System
Imports System.Runtime.Serialization
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.Globalization
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Runtime.InteropServices
Imports System.Reflection
Imports System.Text.RegularExpressions
Imports System.Net
Imports System.Resources
Imports System.Xml
Imports System.Text

Imports Microsoft.Win32

Imports Stax.UI
Imports Stax.AppFramework

Public Delegate Sub ParamDelegate(Of T)(ByVal value As T)
Public Delegate Function FunctionDelegate(Of T)() As T
Public Delegate Function ParamFunctionDelegate(Of T)(ByVal value As T) As T
Public Delegate Sub SubDelegate()

Public Class CommonDirs
    Public Shared ReadOnly Property Startup() As String
        Get
            Return DirPath.AppendSeparator(Application.StartupPath)
        End Get
    End Property

    Public Shared ReadOnly Property Temp() As String
        Get
            Return DirPath.AppendSeparator(Path.GetTempPath)
        End Get
    End Property

    Public Shared ReadOnly Property System() As String
        Get
            Return DirPath.AppendSeparator(Environment.SystemDirectory)
        End Get
    End Property

    Public Shared ReadOnly Property Programs() As String
        Get
            Return DirPath.AppendSeparator(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles))
        End Get
    End Property

    Public Shared ReadOnly Property AllAppData() As String
        Get
            Return DirPath.AppendSeparator(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData))
        End Get
    End Property

    Public Shared ReadOnly Property UserAppData() As String
        Get
            Return DirPath.AppendSeparator(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData))
        End Get
    End Property

    Public Shared ReadOnly Property UserDesktop() As String
        Get
            Const CSIDL_DESKTOPDIRECTORY As Int32 = &H10 'users desktop dir
            Dim sb As New StringBuilder(260)
            SHGetFolderPath(IntPtr.Zero, CSIDL_DESKTOPDIRECTORY, IntPtr.Zero, 0, sb)
            Return DirPath.AppendSeparator(sb.ToString)
        End Get
    End Property

    Public Shared ReadOnly Property Windows() As String
        Get
            Const CSIDL_WINDOWS As Int32 = &H24
            Dim sb As New StringBuilder(260)
            SHGetFolderPath(IntPtr.Zero, CSIDL_WINDOWS, IntPtr.Zero, 0, sb)
            Return DirPath.AppendSeparator(sb.ToString)
        End Get
    End Property

    <DllImport("shfolder.dll", CharSet:=CharSet.Auto)> _
    Private Shared Function SHGetFolderPath(ByVal hwndOwner As IntPtr, ByVal nFolder As Integer, ByVal hToken As IntPtr, ByVal dwFlags As Integer, ByVal lpszPath As StringBuilder) As Integer
    End Function
End Class

Public Class PathBase
    Public Shared ReadOnly Property Separator() As Char
        Get
            Return Path.DirectorySeparatorChar
        End Get
    End Property

    Public Shared Function AppendSeparator(ByVal path As String) As String
        If path Is Nothing OrElse path = "" Then
            Return ""
        End If

        If path.EndsWith(Separator) Then
            Return path
        End If

        Return path + Separator
    End Function

    Public Shared Function IsValidFileSystemName(ByVal name As String) As Boolean
        For i As Integer = 0 To name.Length - 1
            Dim charValue As Char = name.Chars(i)

            Select Case charValue
                Case """"c, "*"c, "/"c, ":"c, "<"c, ">"c, "?"c, "\"c, "|"c
                    Return False
            End Select

            If AscW(charValue) < 32 Then
                Return False
            End If
        Next

        Return True
    End Function
End Class

Public Class DirPath
    Inherits PathBase

    Public Shared Function GetRoot(ByVal path As String) As String
        Return Regex.Match(path, "^.:\\").Value
    End Function

    Public Shared Function TrimSeparator(ByVal path As String) As String
        If path = "" OrElse path Is Nothing Then
            Return ""
        End If

        If path.EndsWith(Separator) AndAlso Not path.EndsWith(":\") Then
            Return path.TrimEnd(Separator)
        End If

        Return path
    End Function

    Public Shared Function FixSeperator(ByVal path As String) As String
        If path.Contains("\") AndAlso Separator <> "\" Then
            path = path.Replace("\", Separator)
        End If

        If path.Contains("/") AndAlso Separator <> "/" Then
            path = path.Replace("/", Separator)
        End If

        Return path
    End Function
End Class

Public Class Filepath
    Inherits PathBase

    Public Shared Function GetDir(ByVal path As String) As String
        If path Is Nothing OrElse path = "" Then
            Return ""
        End If

        If path.Contains(Separator) Then
            path = StringHelp.GetLeftLast(path, Separator) + Separator
        End If

        Return path
    End Function

    Public Shared Function GetDirAndNameNoExt(ByVal path As String) As String
        Return GetDir(path) + GetNameNoExt(path)
    End Function

    Public Shared Function GetNameWithExt(ByVal filepath As String) As String
        If Not filepath Is Nothing Then
            Dim index As Integer = filepath.LastIndexOf(Path.DirectorySeparatorChar)

            If index > -1 Then
                Return filepath.Substring(index + 1)
            End If
        End If

        Return filepath
    End Function

    Public Shared Function GetExt(ByVal path As String) As String
        Dim x As Integer = path.Length

        While x > 0
            x -= 1

            Dim c As Char = path(x)

            If c = Separator Then
                Return ""
            End If

            If c = "."c Then
                Return path.Substring(x).ToLower
            End If
        End While

        Return ""
    End Function

    Public Shared Function GetExtNoDot(ByVal path As String) As String
        If path = "" OrElse path Is Nothing Then
            Return ""
        End If

        If path.Contains(Separator) Then
            path = StringHelp.GetRightLast(path, Separator)
        End If

        If path.Contains(".") Then
            Return StringHelp.GetRightLast(path, ".").ToLower()
        Else
            Return ""
        End If
    End Function

    Public Shared Function GetChangeExt(ByVal path As String, ByVal ext As String) As String
        If path Is Nothing OrElse path = "" Then
            Return ""
        End If

        Return GetDirAndNameNoExt(path) + ext
    End Function

    Public Shared Function GetDirNoSep(ByVal path As String) As String
        path = GetDir(path)

        If path.EndsWith(Separator) Then
            path = TrimSeparator(path)
        End If

        Return path
    End Function

    Public Shared Function GetNameNoExt(ByVal path As String) As String
        If path = "" OrElse path Is Nothing Then
            Return ""
        End If

        Dim ret As String = path

        If ret.Contains(Separator) Then
            ret = StringHelp.GetRightLast(ret, Separator)
        End If

        If ret.Contains(".") Then
            ret = StringHelp.GetLeftLast(ret, ".")
        End If

        Return ret
    End Function

    Public Shared Function TrimSeparator(ByVal path As String) As String
        If path = "" OrElse path Is Nothing Then
            Return ""
        End If

        If path.EndsWith(Separator) AndAlso Not path.EndsWith(":" + Separator) Then
            Return path.TrimEnd(Separator)
        End If

        Return path
    End Function

    Public Shared Function GetDirNameOnly(ByVal path As String) As String
        Return StringHelp.GetRightLast(Filepath.GetDirNoSep(path), "\")
    End Function
End Class

Public Class SafeSerialization
    Public Shared Sub Serialize(ByVal o As Object, ByVal path As String)
        Dim list As New List(Of Object)

        For Each i As FieldInfo In o.GetType.GetFields(BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance)
            If Not i.IsNotSerialized Then
                Dim mc As New FieldContainer
                mc.Name = i.Name

                Dim value As Object = i.GetValue(o)

                If value Is Nothing Then
                    Throw New NullReferenceException(i.Name)
                End If

                If i.FieldType.IsPrimitive Then
                    mc.Value = value
                Else
                    mc.Value = GetObjectData(value)
                End If

                list.Add(mc)
            End If
        Next

        Dim bf As New BinaryFormatter

        Using fs As New FileStream(path, FileMode.Create)
            bf.Serialize(fs, list)
        End Using
    End Sub

    <DebuggerNonUserCode()> _
    Public Shared Function Deserialize(ByVal instance As Object, ByVal path As String) As Object
        Dim smartInstance As ISafeSerialization = DirectCast(instance, ISafeSerialization)

        If File.Exists(path) Then
            Dim list As List(Of Object) = Nothing
            Dim bf As New BinaryFormatter

            Using fs As New FileStream(path, FileMode.Open)
                Try
                    list = DirectCast(bf.Deserialize(fs), List(Of Object))
                Catch
                End Try
            End Using

            If Not list Is Nothing Then
                For Each i As FieldContainer In list
                    For Each iFieldInfo As FieldInfo In instance.GetType.GetFields(BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance)
                        If Not iFieldInfo.IsNotSerialized Then
                            If i.Name = iFieldInfo.Name Then
                                Try
                                    If iFieldInfo.FieldType.IsPrimitive Then
                                        iFieldInfo.SetValue(instance, i.Value)
                                    Else
                                        iFieldInfo.SetValue(instance, GetObjectInstance(CType(i.Value, Byte())))
                                    End If
                                Catch
                                    smartInstance.IsDirty = True
                                End Try
                            End If
                        End If
                    Next
                Next
            End If
        End If

        smartInstance.Init()

        If smartInstance.IsDirty Then
            smartInstance.IsDirty = False
            Serialize(instance, path)
        End If

        Return instance
    End Function

    <DebuggerNonUserCode()> _
    Private Shared Function GetObjectInstance(ByVal ba As Byte()) As Object
        Dim ms As New MemoryStream(ba)
        Dim bf As New BinaryFormatter
        Dim ret As Object = bf.Deserialize(ms)
        ms.Close()
        Return ret
    End Function

    Private Shared Function GetObjectData(ByVal o As Object) As Byte()
        Dim ms As New MemoryStream
        Dim bf As New BinaryFormatter
        bf.Serialize(ms, o)
        Dim ret As Byte() = ms.ToArray()
        ms.Close()
        Return ret
    End Function

    <Serializable()> _
    Public Class FieldContainer
        Public Value As Object
        Public Name As String
    End Class

    Public Shared Function Check(ByVal iface As ISafeSerialization, ByVal o As Object, ByVal key As String, ByVal minimal As Integer, ByVal current As Integer, ByVal settingName As String) As Boolean
        If settingName Is Nothing Then
            settingName = key
        End If

        If o Is Nothing Then
            iface.IsDirty = True

            If iface.Versions.ContainsKey(key) Then
                Msg.Show("A setting had to be reset: " + settingName, "Reset Setting", MsgIcon.Info)
            End If

            iface.Versions(key) = current

            Return True
        End If

        If Not iface.Versions.ContainsKey(key) Then
            iface.Versions(key) = 0
        End If

        If iface.Versions(key) < minimal Then
            iface.IsDirty = True
            iface.Versions(key) = current
            Msg.Show("A setting had to be reset: " + settingName, "Reset Setting", MsgIcon.Info)
            Return True
        ElseIf iface.Versions(key) < current Then
            iface.IsDirty = True
            iface.Versions(key) = current

            If Msg.Show("A setting has new default values: " + settingName + vbCrLf + vbCrLf + "Would you like to update?", "New Default Values", MsgIcon.Question, MessageBoxButtons.YesNo) = DialogResult.Yes Then
                Return True
            End If
        End If

        Return False
    End Function
End Class

Public Interface ISafeSerialization
    Property IsDirty() As Boolean
    ReadOnly Property Versions() As Dictionary(Of String, Integer)
    Sub Init()
End Interface

Public Class Sorter
    Private List As New List(Of Object)

    Public Sub Add(ByVal sortValue As IComparable, ByVal o As Object)
        List.Add(New SortObject(sortValue, o))
    End Sub

    Public Function GetSortedList() As List(Of Object)
        List.Sort()
        Dim ret As New List(Of Object)

        For Each i As SortObject In List
            ret.Add(i.Object)
        Next

        Return ret
    End Function

    Private Class SortObject
        Implements IComparable

        Public SortValue As IComparable
        Public [Object] As Object

        Public Sub New(ByVal sortValue As IComparable, ByVal o As Object)
            Me.SortValue = sortValue
            Me.Object = o
        End Sub

        Public Function CompareTo(ByVal obj As Object) As Integer Implements IComparable.CompareTo
            Return SortValue.CompareTo(CType(obj, SortObject).SortValue)
        End Function
    End Class
End Class

Public Class ExtendedResourceManager
    Inherits ResourceManager

    Public Sub New(ByVal baseName As String, ByVal [assembly] As [Assembly])
        MyBase.New(baseName, [assembly])
    End Sub

    <DebuggerHidden()> _
    Public Function GetImage(ByVal name As String) As Image
        Dim ret As Image = CType(GetObject(name + ".png"), Image)

        If ret Is Nothing Then
            Throw New Exception(name + " not found.")
        End If

        Return ret
    End Function
End Class

Public Class HelpDocument
    Private Path As String
    Private Title As String
    Private IsClosed As Boolean

    Public Sub New(ByVal path As String)
        Me.Path = path
    End Sub

    Private WriterValue As XmlTextWriter

    Public Property Writer() As XmlTextWriter
        Get
            Return WriterValue
        End Get
        Set(ByVal Value As XmlTextWriter)
            WriterValue = Value
        End Set
    End Property

    Public Sub WriteStart(ByVal title As String)
        Me.Title = title

        Writer = New XmlTextWriter(Path, Encoding.UTF8)

        Writer.Formatting = Formatting.Indented

        Writer.WriteStartElement("html")
        Writer.WriteStartElement("head")
        Writer.WriteElementString("title", title)
        Writer.WriteStartElement("link")
        Writer.WriteAttributeString("href", "Styles.css")
        Writer.WriteAttributeString("rel", "stylesheet")
        Writer.WriteAttributeString("type", "text/css")
        Writer.WriteEndElement() 'link
        Writer.WriteEndElement() 'head

        Writer.WriteStartElement("body")
        Writer.WriteElementString("h1", title)
    End Sub

    Public Sub WriteParagraph(ByVal rawText As String)
        WriteElement("p", rawText)
    End Sub

    Public Sub WriteElement(ByVal elementName As String, ByVal rawText As String)
        Writer.WriteStartElement(elementName)

        If MustConvert(rawText) Then
            Writer.WriteRaw(ConvertMarkup(rawText, False))
        Else
            Writer.WriteRaw(rawText)
        End If

        Writer.WriteEndElement()
    End Sub

    Public Shared Function ConvertMarkup(ByVal value As String, ByVal stripOnly As Boolean) As String
        Dim re As Regex

        If stripOnly Then
            re = New Regex("\[\[(.+?) ?\| ?(.+?)\]\]")

            If re.IsMatch(value) Then
                value = re.Replace(value, "$2")
            End If

            re = New Regex("\*\*(.+?)\*\*")

            If re.IsMatch(value) Then
                value = re.Replace(value, "$1")
            End If
        Else
            re = New Regex("\[\[(.+?) ?\| ?(.+?)\]\]")

            If re.IsMatch(value) Then
                value = re.Replace(value, "<a href=""$1"">$2</a>")
            End If

            re = New Regex("\*\*(.+?)\*\*")

            If re.IsMatch(value) Then
                value = re.Replace(value, "<b>$1</b>")
            End If
        End If

        Return value
    End Function

    Public Shared Function MustConvert(ByVal value As String) As Boolean
        If value.Contains("[[") OrElse value.Contains("**") Then
            Return True
        End If
    End Function

    Public Sub WriteTips(ByVal ParamArray tips As StringPairList())
        Dim l As New StringPairList

        For Each i As StringPairList In tips
            l.AddRange(i)
        Next

        l.Sort()

        For Each i As StringPair In l
            Writer.WriteElementString("h2", i.Name)

            If i.Value.Contains(vbCrLf + vbCrLf) Then
                For Each i2 As String In StringHelp.Split(i.Value, vbCrLf + vbCrLf)
                    WriteElement("p", i2)
                Next
            Else
                WriteElement("p", i.Value)
            End If
        Next
    End Sub

    Public Sub WriteList(ByVal ParamArray values As String())
        Writer.WriteStartElement("ul")

        For Each i As String In values
            Writer.WriteStartElement("li")
            Writer.WriteRaw(ConvertMarkup(i, False))
            Writer.WriteEndElement()
        Next

        Writer.WriteEndElement()
    End Sub

    Public Sub WriteTable(ByVal title As String, ByVal list As StringPairList)
        WriteTable(title, list, True)
    End Sub

    Public Sub WriteTable(ByVal title As String, ByVal list As StringPairList, ByVal sort As Boolean)
        If sort Then
            list.Sort()
        End If

        Writer.WriteElementString("h2", title)
        Writer.WriteElementString("p", "")

        Writer.WriteStartElement("table")
        Writer.WriteAttributeString("border", "1")
        Writer.WriteAttributeString("cellspacing", "0")
        Writer.WriteAttributeString("cellpadding", "3")
        Writer.WriteAttributeString("bordercolordark", "white")
        Writer.WriteAttributeString("bordercolorlight", "black")

        For Each i As StringPair In list
            Writer.WriteStartElement("tr")

            Writer.WriteStartElement("td")
            WriteElement("p", i.Name)
            Writer.WriteEndElement() 'td

            Writer.WriteStartElement("td")
            WriteElement("p", i.Value)
            Writer.WriteEndElement() 'td

            Writer.WriteEndElement() 'tr
        Next

        Writer.WriteEndElement() 'table
    End Sub

    Public Sub WriteDocument()
        If Not IsClosed Then
            IsClosed = True

            Writer.WriteRaw("<p>&nbsp;</p>" + vbCrLf)
            Writer.WriteRaw("<h5 align=""center"">Copyright &copy; 2005 by stax. All rights reserved.</h5>")
            Writer.WriteEndElement() 'body
            Writer.WriteEndElement() 'html
            Writer.Close()
        End If
    End Sub

    Public Sub WriteDocument(ByVal browser As WebBrowser)
        WriteDocument()
        browser.Navigate(Path)
    End Sub
End Class

<Serializable()> _
Public Class RegKey
    Private Path As String

    Public Sub New(ByVal path As String)
        If Not path.StartsWith("CU\") AndAlso Not path.StartsWith("LM\") Then
            Throw New Exception("path must start with CU\ or LM\")
        End If

        Me.Path = path
    End Sub

    Public Function GetValue(ByVal name As String) As Object
        Using k As RegistryKey = GetKey()
            Return k.GetValue(name)
        End Using
    End Function

    Public Function GetString(ByVal name As String) As String
        Dim test As Object = GetValue(name)

        If TypeOf test Is String Then
            Return test.ToString
        End If

        Return Nothing
    End Function

    Public Sub SetValue(ByVal name As String, ByVal value As Object)
        Using k As RegistryKey = GetKey()
            k.SetValue(name, value)
        End Using
    End Sub

    Public Function GetKey() As RegistryKey
        Dim rootKey As RegistryKey = Nothing
        Dim ret As RegistryKey = Nothing

        If Path.StartsWith("CU\") Then
            rootKey = Registry.CurrentUser
        ElseIf Path.StartsWith("LM\") Then
            rootKey = Registry.LocalMachine
        End If

        ret = rootKey.OpenSubKey(StringHelp.GetRight(Path, "\"), True)

        If ret Is Nothing Then
            ret = rootKey.CreateSubKey(StringHelp.GetRight(Path, "\"), RegistryKeyPermissionCheck.ReadWriteSubTree)
        End If

        Return ret
    End Function
End Class

<Serializable()> _
Public Class SettingBag(Of T)
    Public Sub New(ByVal value As T)
        Me.Value = value
    End Sub

    Private ValueValue As T

    Public Overridable Property Value() As T
        Get
            Return ValueValue
        End Get
        Set(ByVal Value As T)
            ValueValue = Value
        End Set
    End Property
End Class

Public Class BindingSettingBag(Of T)
    Inherits SettingBag(Of T)

    Private Obj As Object
    Private PropName As String

    Public Sub New(ByVal obj As Object, ByVal propName As String)
        MyBase.New(Nothing)
        Me.Obj = obj
        Me.PropName = propName
    End Sub

    Public Overrides Property Value() As T
        Get
            Return DirectCast(Obj.GetType.GetProperty(PropName).GetValue(Obj, Nothing), T)
        End Get
        Set(ByVal value As T)
            If Not Obj Is Nothing Then
                Obj.GetType.GetProperty(PropName).SetValue(Obj, value, Nothing)
            End If
        End Set
    End Property
End Class

Public Class StringPair
    Implements IComparable(Of StringPair)

    Public Sub New()
    End Sub

    Public Sub New(ByVal name As String, ByVal text As String)
        Me.Name = name
        Me.Value = text
    End Sub

    Private NameValue As String

    Public Property Name() As String
        Get
            Return NameValue
        End Get
        Set(ByVal Value As String)
            NameValue = Value
        End Set
    End Property

    Private ValueValue As String

    Public Property Value() As String
        Get
            Return ValueValue
        End Get
        Set(ByVal Value As String)
            ValueValue = Value
        End Set
    End Property

    Public Function CompareTo(ByVal other As StringPair) As Integer Implements System.IComparable(Of StringPair).CompareTo
        Return Name.CompareTo(other.Name)
    End Function
End Class

Public Class Misc
    Public Shared Sub UpdateMostRecentList(ByVal list As List(Of String), ByVal value As String, ByVal max As Integer)
        If value Is Nothing OrElse value = "" Then
            Exit Sub
        End If

        If list.Count > 0 AndAlso value = list(0) Then
            Exit Sub
        End If

        list.Remove(value)
        list.Insert(0, value)

        While list.Count > max
            list.RemoveAt(list.Count - 1)
        End While
    End Sub
End Class

Public Class AbortException
    Inherits ApplicationException
End Class

Public Class CLIArg
    Public Sub New(ByVal value As String)
        Me.Value = value
    End Sub

    Private ValueValue As String

    Public Property Value() As String
        Get
            Return ValueValue
        End Get
        Set(ByVal Value As String)
            ValueValue = Value
        End Set
    End Property

    Public Shared Function GetArgs() As List(Of CLIArg)
        Dim ret As New List(Of CLIArg)
        Dim args As New List(Of String)(Environment.GetCommandLineArgs)
        args.RemoveAt(0)

        For Each i As String In args
            ret.Add(New CLIArg(i))
        Next

        Return ret
    End Function

    Public Function IsMatch(ByVal ParamArray values As String()) As Boolean
        For Each i As String In values
            i = i.ToUpper
            Dim val As String = Value.ToUpper

            If "-" + i = val OrElse "/" + i = val OrElse _
                val.ToUpper.StartsWith("-" + i + ":") OrElse _
                val.ToUpper.StartsWith("/" + i + ":") Then

                Return True
            End If
        Next
    End Function

    Public Function GetInt() As Integer
        Return CInt(StringHelp.GetRight(Value, ":"))
    End Function

    Public Function GetString() As String
        Return StringHelp.GetRight(Value, ":").Trim(""""c)
    End Function

    Public Function IsFile() As Boolean
        Return File.Exists(Value)
    End Function
End Class

Public Class StringPairList
    Inherits List(Of StringPair)

    Public Overloads Sub Add(ByVal name As String, ByVal text As String)
        Add(New StringPair(name, text))
    End Sub
End Class