Option Strict Off
Imports System.Collections
Imports Microsoft.Win32
Imports System.Text.RegularExpressions
Imports System.IO
Imports System.IO.MemoryMappedFiles
Imports System.Runtime.InteropServices
Imports System.Threading
Imports System.Security
Imports System.ComponentModel
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.ConstrainedExecution
Imports System.Security.Permissions

Public Class Class1
    Implements GOverlayPlugin.Interfaces.IPlugin

    Public thread As New System.Threading.Thread(AddressOf grabdatathread)
    Private objHost As GOverlayPlugin.Interfaces.IHost
    Public PluginName As String = "APC UPS"

    Public Cache As String
    Public LastCache As New System.Collections.Hashtable

    Public APCStats As APCStructure
    Public csParams = New UIControl.MGD_CurrStatus_Params
    Public ups As New UIControl.CUIControl

    Public Sub Initialize(ByVal Host As GOverlayPlugin.Interfaces.IHost) Implements GOverlayPlugin.Interfaces.IPlugin.Initialize
        objHost = Host

        thread.Start()
    End Sub

    Public ReadOnly Property Description() As String Implements GOverlayPlugin.Interfaces.IPlugin.Description
        'Return your plugin name
        Get
            Return "This plugin allows to read the APC UPS status (required APC ups connected with cable and with a working PowerChute application)." & vbNewLine & vbNewLine & _
                vbNewLine & _
                "Developed by TheLaGmAn"
        End Get
    End Property

    Public ReadOnly Property Name() As String Implements GOverlayPlugin.Interfaces.IPlugin.Name
        'Return your plugin name
        Get
            Return PluginName
        End Get        
    End Property

    Public ReadOnly Property Display() As String Implements GOverlayPlugin.Interfaces.IPlugin.Display
        'Return the display this plugin belongs to
        Get
            Return "lcdsys"
        End Get
    End Property

    Function CallBack(method As String) As Hashtable Implements GOverlayPlugin.Interfaces.IPlugin.CallBacks
        'Send the request to upload all the files
        'objHost.DebugMessage("ETS2 Plugin - Callback Requested")
        'objHost.DebugMessage("ETS2 Plugin - " & method)

        Dim returnHT As Hashtable = New Hashtable

        Dim value = 0

        If method = "willrequestvalues" Then
            'Comes here once per run (only if sensors are used thru here)
            GrabData()
        Else
            
            'Find sensor in our structure
            Dim i As Integer
            Dim myType As Type = APCStats.GetType
            Dim myField() As System.Reflection.FieldInfo = myType.GetFields() 'Get the fields from my() 'type'
            For i = 0 To myField.Length - 1
                'objHost.DebugMessage(myField(i).Name)
                If ("APCUPS." & myField(i).Name = method) Then
                    value = myField(i).GetValue(APCStats)
                    'objHost.DebugMessage("g")
                    'value = APCStats.(i)
                End If
            Next i
        End If

        returnHT("value") = value

        Return returnHT

    End Function

    Function AvailableSensors(pluginOptions As Hashtable) As System.Collections.Generic.Dictionary(Of String, String) Implements GOverlayPlugin.Interfaces.IPlugin.AvailableSensors
        'Create the list of the sensors/elements this plugin has
        'You can access your pluginOptions here as pluginOptions(your_option)        
        Try
            Try
                GrabData()
            Catch ex As Exception
            End Try


            Dim sensors As New System.Collections.Generic.Dictionary(Of String, String)
            sensors.Add("APCUPS.InputVoltage", "Input Voltage: " & APCStats.InputVoltage)
            sensors.Add("APCUPS.ACPresent", "AC Present: " & APCStats.ACPresent)
            sensors.Add("APCUPS.BatteryPresent", "Battery Present: " & APCStats.BatteryPresent)
            sensors.Add("APCUPS.ChargeFailure", "Charge Failure: " & APCStats.ChargeFailure)
            sensors.Add("APCUPS.ConfigActivePower", "Maximum Wattage Available: " & APCStats.ConfigActivePower)
            sensors.Add("APCUPS.InputFrequency", "Input Frequency: " & APCStats.InputFrequency)
            sensors.Add("APCUPS.InternalCommsFault", "Input Voltage: " & APCStats.InternalCommsFault)
            sensors.Add("APCUPS.OutputFreq", "Output Frequency: " & APCStats.OutputFreq)
            sensors.Add("APCUPS.OutputVoltage", "Output Voltage: " & APCStats.OutputVoltage)
            sensors.Add("APCUPS.Overload", "Overload: " & APCStats.Overload)
            sensors.Add("APCUPS.PercentLoad", "Load Percent: " & APCStats.PercentLoad)
            sensors.Add("APCUPS.WattsUsage", "Watts Usage: " & APCStats.WattsUsage)
            sensors.Add("APCUPS.RaminingCapacity", "Battery Percent Available: " & APCStats.RemainingCapacity)
            sensors.Add("APCUPS.BatteryRemainingTime", "Battery Remaining Time (seconds): " & APCStats.BatteryRemainingTime)
            sensors.Add("APCUPS.BatteryRemainingTimeM", "Battery Remaining Time (minutes): " & APCStats.BatteryRemainingTimeM)
            sensors.Add("APCUPS.isOverloaded", "is the UPS Overloaded?: " & APCStats.IsOverloaded)


            Return sensors
        Catch ex As Exception
            objHost.DebugMessage("Error loading sensors:" & ex.Message & ": " & ex.StackTrace)
            'MsgBox(ex.Message & ":: " & ex.StackTrace)
        End Try
    End Function


    Function ComboBoxes() As Hashtable Implements GOverlayPlugin.Interfaces.IPlugin.ComboBoxes
        'Create custom ComboBox for your configuration to use
        Dim boxes As New Hashtable
        Dim myboxOptions As New Hashtable
        'Set each one of the Combobox options as value, Display Name
        ' myboxOptions.Add(0, "Kmh")
        'myboxOptions.Add(1, "Mph")
        'myboxOptions.Add("DriveLetter", "Drive Letter")
        'myboxOptions.Add("Model", "Model")
        'myboxOptions.Add("Temperature", "Temperature")
        'myboxOptions.Add("TemperatureClass", "Temperature Class")

        'Add the combobox options as the combobox "PLUG.optioncustom1"
        'boxes.Add("ETS2.ComboMetric", myboxOptions)
        Return boxes
    End Function

    Public Function SetDefaultOptions(sensorId As String, elementData As Hashtable) As Hashtable Implements GOverlayPlugin.Interfaces.IPlugin.SetDefaultOptions
        'Set the default values you want to have on your sensor when its created, if the user doesnt change any option, he will have this settings


        elementData("width") = 50
        elementData("height") = 20
        elementData("font_style") = 0
        elementData("basic_color") = 0
        elementData("font_size") = 1
        elementData("font_color") = 0
        elementData("font_transform") = 0
        'elementData("CM.DiskData") = "DiskSize"

        'elementData("font_style") = 0
        'elementData("font_size") = 10
        'elementData("max_digits") = 2

        Return elementData
    End Function

    Public Function CreateOptions(sensorId As String, elementData As Hashtable) As Hashtable Implements GOverlayPlugin.Interfaces.IPlugin.CreateOptions
        'Set the options the user will have when clicking on the element
        'The available option_types are:
        'Text: Input text
        'Combo3: Combo with options (Enabled=0, Disabled=1)
        'Sensors: Combo with available sensors
        'ColorBasic: Combo with basic colors for the regular text font
        'ColorRGB: Combo with common RGB colors = 16bitinteger
        'ComboOnOff: Combo with options (Disabled=0, Enabled=1)
        'ComboYesNo: Combo with options (Yes=Yes, No=No)
        'TextSize: Combo with options (Extra Small=4, Very Small=5, Small=10, Normal=15, Big=20, Very Big=30, Extreme=40)
        'FontStyle2: Combo with options (Regular Font=0, Square Font=1, Numbers Font 1=2
        'Orientation: Combo with options (Horizontal=0, Vertical=1)
        'Here you can also load your custom comboboxes

        Dim options As New Hashtable
        Dim x = 0

        options.Add(x, New ArrayList({"FontStyle2", "Font Style", "font_style", "Font style"})) : x = x + 1

        If elementData("font_style") = 0 Then
            'Regular font
            options.Add(x, New ArrayList({"ColorBasic", "Text Color", "basic_color", "Select the basic color"})) : x = x + 1
        ElseIf elementData("font_style") = 1 Then
            'Square Font
            options.Add(x, New ArrayList({"FontSize", "Font Size", "font_size", "Select the font size"})) : x = x + 1
            options.Add(x, New ArrayList({"ColorRGB", "Font Color", "font_color", "Select the font color"})) : x = x + 1
            options.Add(x, New ArrayList({"FontTransform", "Font Transform", "font_transform", "Select font-transform"})) : x = x + 1
        Else
            'Image-font
            options.Add(x, New ArrayList({"Text", "Maximum Digits", "maxdigits", "Write the maximum digits to show"})) : x = x + 1
        End If

        options.Add(x, New ArrayList({"Text", "Width", "width", "Write a width in pixels"})) : x = x + 1
        options.Add(x, New ArrayList({"Text", "Height", "height", "Write a height in pixels"})) : x = x + 1

        Return options



    End Function

    Public Function PluginOptionsDefault() As Hashtable Implements GOverlayPlugin.Interfaces.IPlugin.PluginOptionsDefault
        'Set the default values you want to have on plugin, if the user doesnt change any option, he will have this settings
        Dim options As New Hashtable
        'options("reqimages") = "*.*.plugin_ets_*"
        Return options
    End Function
    Public Function PluginOptions(pluginCurrentOptions As Hashtable) As Hashtable Implements GOverlayPlugin.Interfaces.IPlugin.PluginOptions
        'Set the options the user will have when going to the plugins tab and clicking on your plugin
        'The availalbe option_type are teh same as CreateOptions function
        Dim options As New Hashtable
        'Option: option_index as integer, option_data as ArrayList
        'Option_Data: option_type as string, option_label as string, option_name as string (no spaces, no _)

        'options.Add(0, New ArrayList({"TextMulti", "Images Match finder", "reqimages"}))
        'options.Add(1, New ArrayList({"Button", "Upload Images", "reqimagesbutton"}))
        Return options
    End Function

    Structure APCStructure
        Dim InputVoltage As Integer
        Dim ACPresent As Integer
        Dim BatteryPresent As Integer
        Dim ChargeFailure As Integer
        Dim ConfigActivePower As Integer
        Dim InputFrequency As Integer
        Dim InternalCommsFault As Integer
        Dim OutputFreq As Integer
        Dim OutputVoltage As Integer
        Dim Overload As Integer
        Dim PercentLoad As Integer
        Dim WattsUsage As Integer
        Dim RemainingCapacity As Integer
        Dim BatteryRemainingTime As Decimal
        Dim BatteryRemainingTimeM As Decimal
        Dim IsOverloaded As Boolean
    End Structure

    Public Function GrabData()
        Try
            'we have a thread for this
            Return True
        Catch ex As Exception
            objHost.DebugMessage(ex.Message & ": " & ex.StackTrace)
            Return False
        End Try
    End Function
    Public Function DisplayOnLCD(sensorId As String, elementData As Hashtable, pluginOptions As Hashtable, cacheRuns As Integer) As ArrayList Implements GOverlayPlugin.Interfaces.IPlugin.DisplayOnLCD

        Dim x = CInt(elementData("x"))    'grab X position of the element
        Dim y = CInt(elementData("y"))    'grab Y position of the element

        Dim commandList2 As New ArrayList()

        Try
            Dim value = 0

            'If it fails, dont output anything
            If GrabData() = False Then
                'Return commandList2
            End If

            Try

                'Find sensor in our structure
                Dim i As Integer
                Dim myType As Type = APCStats.GetType
                Dim myField() As System.Reflection.FieldInfo = myType.GetFields() 'Get the fields from my() 'type'
                For i = 0 To myField.Length - 1
                    'objHost.DebugMessage(myField(i).Name)
                    If ("APCUPS." & myField(i).Name = sensorId) Then
                        value = myField(i).GetValue(APCStats)
                        'objHost.DebugMessage("g")
                        'value = APCStats.(i)
                    End If
                Next i



                If Not LastCache.ContainsKey(sensorId) OrElse value.ToString.ToLower <> LastCache(sensorId).ToString.ToLower Then

                    If elementData("font_style") = 0 Then
                        commandList2.Add(New ArrayList({"text", x, y, value.ToString, 0, False, False, 0, elementData("basic_color")}))
                    ElseIf elementData("font_style") = 1 Then
                        If elementData("font_transform") = 1 Then value = value.ToString.ToUpper
                        If elementData("font_transform") = 2 Then value = value.ToString.ToLower
                        'clear previous value
                        If LastCache.ContainsKey(sensorId) Then
                            commandList2.Add(New ArrayList({"text_square", LastCache(sensorId).ToString, x, y, elementData("font_size"), CInt(elementData("font_size")) * 2, 3, 0}))
                            'objHost.DebugMessage("clear: " & LastCache(sensorId).ToString)
                        End If
                        'write value
                        commandList2.Add(New ArrayList({"text_square", value.ToString, x, y, elementData("font_size"), CInt(elementData("font_size")) * 2, 3, elementData("font_color")}))
                    Else
                        commandList2.Add(New ArrayList({"text_image", x, y, value.ToString, elementData("font_style")}))
                    End If

                    LastCache(sensorId) = value
                Else

                End If

                Return commandList2
                

                'objHost.DebugMessage("v: " & data(55).ToString)


            Catch ex As Exception
                objHost.DebugMessage(ex.Message & ": " & ex.StackTrace)
            End Try

        Catch ex As Exception
            objHost.DebugMessage("The process throws the error:" & ex.Message & " : " & ex.StackTrace)
            'Console.ReadLine()
        End Try


        Return commandList2
        'When the cache is refreshed, lets redraw even if the value stays the same, we could avoid this if we want, this is done just in case there were some errors and the value wasnt draw for some reason before.
        If cacheRuns = 0 Then
            Cache = Nothing
        End If

        Return New ArrayList()
    End Function

    Public Function grabdatathread()
        While True
            ups.Initialize()
            ups.GetCurrentStatus(csParams)

            'Dim ups2 As New UIControl.CEventMgr

            APCStats.InputVoltage = csParams.m_inputvoltage
            APCStats.ACPresent = csParams.m_acPresent
            APCStats.BatteryPresent = csParams.m_batteryPresent
            APCStats.ChargeFailure = csParams.m_chargeFailure
            APCStats.ConfigActivePower = csParams.m_config_active_power
            APCStats.InputFrequency = csParams.m_input_frequency
            APCStats.InternalCommsFault = csParams.m_internalCommsFault
            APCStats.OutputFreq = csParams.m_output_frequency
            APCStats.OutputVoltage = csParams.m_output_voltage
            APCStats.Overload = csParams.m_overload
            APCStats.PercentLoad = csParams.m_percentLoad
            APCStats.WattsUsage = csParams.m_percentLoad * csParams.m_config_active_power / 100
            APCStats.RemainingCapacity = csParams.m_remainingCapacity
            'APCStats.BatteryRemainingTime = ups2.GetRemainingBatteryTime()
            'APCStats.BatteryRemainingTimeM = Math.Round(ups2.GetRemainingBatteryTime() / 60, 0)
            'APCStats.IsOverloaded = ups2.IsOverloaded

            'Dim t As New UIControl.CEventMgr

            Dim ar As New ArrayList
            ups.MGDGetEvents(ar)


            'TextBox1.Text = vbNewLine & TextBox1.Text & "events: " & ar.Count()
            'For Each v In ar
            'TextBox1.Text = TextBox1.Text & vbNewLine & v
            'MsgBox("a")
            'Next

            thread.Sleep(1000)
        End While

    End Function

    Public Function convertcolorto16bit(cr, cg, cb)

        Dim R As Double
        Dim G As Double
        Dim B As Double

        R = cr / 256 * 32
        G = cg / 256 * 64
        B = cb / 256 * 32

        R = Math.Floor(R)
        G = Math.Floor(G)
        B = Math.Floor(B)

        Dim final As Int32 = (CInt(R) << 11)
        final = final + Int(G << 5)
        final = final + Int(B)

        Return final

    End Function



#Region "Native API Signatures and Types"

    ''' <summary>
    ''' Access rights for file mapping objects
    ''' http://msdn.microsoft.com/en-us/library/aa366559.aspx
    ''' </summary>
    ''' <remarks></remarks>
    Public Enum FileMapAccess
        FILE_MAP_COPY = 1
        FILE_MAP_WRITE = 2
        FILE_MAP_READ = 4
        FILE_MAP_ALL_ACCESS = &HF001F
    End Enum


    ''' <summary>
    ''' Represents a wrapper class for a file mapping handle. 
    ''' </summary>
    ''' <remarks></remarks>
    <SuppressUnmanagedCodeSecurity(), _
    HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort:=True)> _
    Friend NotInheritable Class SafeFileMappingHandle
        Inherits SafeHandleZeroOrMinusOneIsInvalid

        <SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode:=True)> _
        Private Sub New()
            MyBase.New(True)
        End Sub

        <SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode:=True)> _
        Public Sub New(ByVal handle As IntPtr, ByVal ownsHandle As Boolean)
            MyBase.New(ownsHandle)
            MyBase.SetHandle(handle)
        End Sub

        <ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), _
        DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
        Private Shared Function CloseHandle(ByVal handle As IntPtr) _
        As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function

        Protected Overrides Function ReleaseHandle() As Boolean
            Return SafeFileMappingHandle.CloseHandle(MyBase.handle)
        End Function

    End Class


    Friend ReadOnly INVALID_HANDLE_VALUE As New IntPtr(-1)


    ''' <summary>
    ''' The class exposes Windows APIs used in this code sample.
    ''' </summary>
    ''' <remarks></remarks>
    <SuppressUnmanagedCodeSecurity()> _
    Friend Class NativeMethod

        ''' <summary>
        ''' Opens a named file mapping object.
        ''' </summary>
        ''' <param name="dwDesiredAccess">
        ''' The access to the file mapping object. This access is checked against 
        ''' any security descriptor on the target file mapping object.
        ''' </param>
        ''' <param name="bInheritHandle">
        ''' If this parameter is TRUE, a process created by the CreateProcess 
        ''' function can inherit the handle; otherwise, the handle cannot be 
        ''' inherited.
        ''' </param>
        ''' <param name="lpName">
        ''' The name of the file mapping object to be opened.
        ''' </param>
        ''' <returns>
        ''' If the function succeeds, the return value is an open handle to the 
        ''' specified file mapping object.
        ''' </returns>
        <DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
        Public Shared Function OpenFileMapping( _
            ByVal dwDesiredAccess As FileMapAccess, _
            ByVal bInheritHandle As Boolean, _
            ByVal lpName As String) _
            As SafeFileMappingHandle
        End Function


        ''' <summary>
        ''' Maps a view of a file mapping into the address space of a calling 
        ''' process.
        ''' </summary>
        ''' <param name="hFileMappingObject">
        ''' A handle to a file mapping object. The CreateFileMapping and 
        ''' OpenFileMapping functions return this handle.
        ''' </param>
        ''' <param name="dwDesiredAccess">
        ''' The type of access to a file mapping object, which determines the 
        ''' protection of the pages.
        ''' </param>
        ''' <param name="dwFileOffsetHigh">
        ''' A high-order DWORD of the file offset where the view begins.
        ''' </param>
        ''' <param name="dwFileOffsetLow">
        ''' A low-order DWORD of the file offset where the view is to begin.
        ''' </param>
        ''' <param name="dwNumberOfBytesToMap">
        ''' The number of bytes of a file mapping to map to the view. All bytes 
        ''' must be within the maximum size specified by CreateFileMapping.
        ''' </param>
        ''' <returns>
        ''' If the function succeeds, the return value is the starting address of
        ''' the mapped view.
        ''' </returns>
        <DllImport("Kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
        Public Shared Function MapViewOfFile( _
            ByVal hFileMappingObject As SafeFileMappingHandle, _
            ByVal dwDesiredAccess As FileMapAccess, _
            ByVal dwFileOffsetHigh As UInt32, _
            ByVal dwFileOffsetLow As UInt32, _
            ByVal dwNumberOfBytesToMap As UInt32) _
            As IntPtr
        End Function


        ''' <summary>
        ''' Unmaps a mapped view of a file from the calling process's address 
        ''' space.
        ''' </summary>
        ''' <param name="lpBaseAddress">
        ''' A pointer to the base address of the mapped view of a file that is to 
        ''' be unmapped.
        ''' </param>
        ''' <returns></returns>
        <DllImport("Kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
        Public Shared Function UnmapViewOfFile( _
            ByVal lpBaseAddress As IntPtr) _
            As <MarshalAs(UnmanagedType.Bool)> Boolean
        End Function

    End Class

#End Region
End Class