Imports System.IO
Imports System.Reflection
Imports Newtonsoft.Json.Linq

Public NotInheritable Class SettingsManager(Of T As SettingsBase)
    Implements ISettingsManager

    Public Event AutoSavingSettings As EventHandler
    Public Event SavingSettings As EventHandler
    Public Event SavedSettings As EventHandler

    Private defaultInstance As T = Nothing
    Private enableAutoSave As Boolean = False
    Private addedHandler As Boolean = False

    Public Property ConfigFilePath As String Implements ISettingsManager.ConfigFilePath

    Public Property AutoSaveOnExit As Boolean
        Get
            Return enableAutoSave
        End Get
        Set
            If enableAutoSave <> Value Then
                enableAutoSave = Value
                Select Case enableAutoSave
                    Case True
                        If Not addedHandler Then
                            AddAutoSaveHandler()
                        End If
                    Case False
                        If addedHandler Then
                            RemoveAutoSaveHandler()
                        End If
                End Select
            End If
        End Set
    End Property

    Private Sub AddAutoSaveHandler()
        AddHandler AppDomain.CurrentDomain.ProcessExit, AddressOf AutoSaveSettingsOnExit
        addedHandler = True
    End Sub
    Private Sub RemoveAutoSaveHandler()
        RemoveHandler AppDomain.CurrentDomain.ProcessExit, AddressOf AutoSaveSettingsOnExit
        addedHandler = False
    End Sub

    Public ReadOnly Property Instance As T
        Get
            If defaultInstance Is Nothing Then
                Load()
            End If
            Return defaultInstance
        End Get
    End Property

    Public Sub New()
        ConfigFilePath = ""
        AutoSaveOnExit = False
    End Sub
    Public Sub New(fileName As String, autoSaveOnExit As Boolean)
        ConfigFilePath = fileName
        Me.AutoSaveOnExit = autoSaveOnExit
    End Sub

    Private Sub AutoSaveSettingsOnExit(sender As Object, e As EventArgs)
        RaiseEvent AutoSavingSettings(Me, New EventArgs)
        Save()
    End Sub

    Public Sub Save() Implements ISettingsManager.Save
        If Not String.IsNullOrEmpty(ConfigFilePath) AndAlso defaultInstance IsNot Nothing Then
            SavePrivate()
        End If
    End Sub

    Public Sub Load() Implements ISettingsManager.Load
        If Not String.IsNullOrEmpty(ConfigFilePath) AndAlso File.Exists(ConfigFilePath) Then
            LoadPrivate()
        Else
            CreateNewInstance()
        End If

        If defaultInstance IsNot Nothing Then
            defaultInstance._settingsManager = Me
        End If
    End Sub

    Public Sub Reset() Implements ISettingsManager.Reset
        Instance.ResetValues()
    End Sub

    Private Sub CreateNewInstance()
        Dim ctor As ConstructorInfo = GetType(T).GetConstructor({})
        If ctor IsNot Nothing Then
            defaultInstance = ctor.Invoke({})

            defaultInstance.ResetValues()
        Else
            Throw New Exception("The base type has no constructor with no parameters.")
        End If
    End Sub

    Private Sub LoadPrivate()
        defaultInstance = JObject.Parse(File.ReadAllText(ConfigFilePath)).ToObject(Of T)
    End Sub

    Private Sub SavePrivate()
        RaiseEvent SavingSettings(Me, New EventArgs)

        Dim obj As JObject = JObject.FromObject(defaultInstance)
        If obj IsNot Nothing Then
            File.WriteAllText(ConfigFilePath, obj.ToString)
        End If

        RaiseEvent SavedSettings(Me, New EventArgs)
    End Sub

End Class
