Imports System.IO.Ports
Imports Microsoft.VisualBasic.Logging



Public Class Form1
	Private formIsLoaded As Boolean = False
	Private formStartingWidth As Integer
	Private textBoxesStartingWidth As Integer
	Private connectButtonsStartingLeft As Integer
	Private powerGroupStartingLeft As Integer

	Private ControlCodeMap As New Dictionary(Of Integer, String) From {
		{0, "NUL"}, {1, "SOH"}, {2, "STX"}, {3, "ETX"},
		{4, "EOT"}, {5, "ENQ"}, {6, "ACK"}, {7, "BEL"},
		{8, "BS"}, {9, "TAB"}, {10, "LF"}, {11, "VT"},
		{12, "FF"}, {13, "CR"}, {14, "SO"}, {15, "SI"},
		{16, "DLE"}, {17, "DC1"}, {18, "DC2"}, {19, "DC3"},
		{20, "DC4"}, {21, "NAK"}, {22, "SYN"}, {23, "ETB"},
		{24, "CAN"}, {25, "EM"}, {26, "SUB"}, {27, "ESC"},
		{28, "FS"}, {29, "GS"}, {30, "RS"}, {31, "US"},
		{127, "DEL"}
	}
	Private WithEvents SerialPort1 As New SerialPort()

	Private Sub Form1_SizeChanged(sender As Object, e As EventArgs) Handles Me.SizeChanged
		If (formIsLoaded) Then
			If (Me.Width < formStartingWidth) Then
				Me.Width = formStartingWidth
			End If
			Dim formCurrentWidth As Integer = Me.Width
			Dim xDelta As Integer = formCurrentWidth - formStartingWidth
			txtPartialInput.Width = textBoxesStartingWidth + xDelta
			ListBoxReceived.Width = textBoxesStartingWidth + xDelta
			ButtonConnect.Left = connectButtonsStartingLeft + xDelta
			ButtonDisconnect.Left = connectButtonsStartingLeft + xDelta
			GroupBoxPower.Left = powerGroupStartingLeft + xDelta
		End If
	End Sub

	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
		' Startup
		formStartingWidth = Width
		Dim fixedHeight As Integer = Height
		' Allow width to change, but lock height
		MinimumSize = New Size(formStartingWidth, fixedHeight) ' Minimum width 200
		MaximumSize = New Size(1200, fixedHeight) ' Unlimited width, fixed height

		textBoxesStartingWidth = txtPartialInput.Width
		connectButtonsStartingLeft = ButtonConnect.Left
		powerGroupStartingLeft = GroupBoxPower.Left

		FormBorderStyle = FormBorderStyle.Sizable
		WindowState = FormWindowState.Normal

		' Timer to check CTS status
		TimerCTS.Interval = 500
		TimerCTS.Start()

		' Populate COM ports
		ComboBoxPorts.Items.AddRange(SerialPort.GetPortNames())

		' Populate baud rates
		ComboBoxBaud.Items.AddRange(New Object() {9600, 19200, 38400, 57600, 115200})
		ComboBoxBaud.SelectedIndex = 0

		' Populate handshake options
		ComboBoxHandshake.Items.AddRange([Enum].GetNames(GetType(Handshake)))
		ComboBoxHandshake.SelectedItem = "RequestToSend" ' RTS/CTS default
		formIsLoaded = True
	End Sub

	' Poll CTS state
	Private Sub TimerCTS_Tick(sender As Object, e As EventArgs) Handles TimerCTS.Tick
		If SerialPort1.IsOpen Then
			CheckBoxCTS.CheckState = IIf(SerialPort1.CtsHolding = True, CheckState.Checked, CheckState.Unchecked)
		Else
			CheckBoxCTS.CheckState = CheckState.Indeterminate
		End If
	End Sub
	Private Sub ButtonConnect_Click(sender As Object, e As EventArgs) Handles ButtonConnect.Click
		Try
			If Not SerialPort1.IsOpen And ComboBoxPorts.SelectedItem <> Nothing Then
				SerialPort1.PortName = ComboBoxPorts.SelectedItem.ToString()
				SerialPort1.BaudRate = CInt(ComboBoxBaud.SelectedItem)
				SerialPort1.Handshake = CType([Enum].Parse(GetType(Handshake), ComboBoxHandshake.SelectedItem.ToString()), Handshake)
				SerialPort1.Parity = Parity.None
				SerialPort1.DataBits = 8
				SerialPort1.StopBits = StopBits.One
				SerialPort1.Open()
				LabelStatus.Text = "Connected"
				CheckBoxRTS.Enabled = IIf(ComboBoxHandshake.SelectedItem = "None", True, False)
				ButtonConnect.Enabled = False
				ButtonDisconnect.Enabled = True
			End If
		Catch ex As Exception
			MessageBox.Show("Error opening port: " & ex.Message)
		End Try
	End Sub

	Private Sub ButtonDisconnect_Click(sender As Object, e As EventArgs) Handles ButtonDisconnect.Click
		If SerialPort1.IsOpen Then
			SerialPort1.Close()
			LabelStatus.Text = "Disconnected"
			ButtonDisconnect.Enabled = False
			ButtonConnect.Enabled = True
		End If
	End Sub

	' Send buttons
	Private Sub ButtonSend1_Click(sender As Object, e As EventArgs) Handles btnSendReady.Click
		If SerialPort1.IsOpen Then
			Dim msg As String = Chr(17) & "000" & Chr(3)
			SerialPort1.WriteLine(msg)
			ListBoxReceived.Items.Add("> " & ExpandControlCodes(msg))
			ListBoxReceived.SelectedIndex = ListBoxReceived.Items.Count - 1
		End If
	End Sub


	' Handle incoming data
	Private Sub SerialPort1_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) _
		Handles SerialPort1.DataReceived
		Static Dim receivedMessage As String = ""
		Try
			receivedMessage &= SerialPort1.ReadExisting()
			Dim pos As Integer = InStr(receivedMessage, Chr(3), CompareMethod.Binary) ' ETX
			If (pos > 0) Then
				While (pos > 0)
					Dim oneMessage As String = ""

					Debug.Print("ETX at pos: " & pos.ToString() & " in '" & receivedMessage & "'")
					oneMessage = Mid(receivedMessage, 1, pos)
					Dim parsed As String = ExpandControlCodes(oneMessage)
					'
					' Analyze and deal with the message
					'
					ProcessAVRMessage(oneMessage)
					Me.Invoke(Sub() ListBoxReceived.Items.Add("< " & parsed))
					Me.Invoke(Sub() ListBoxReceived.SelectedIndex = ListBoxReceived.Items.Count - 1)
					receivedMessage = Mid(receivedMessage, pos + 1)
					Me.Invoke(Sub() txtPartialInput.Text = receivedMessage)
					pos = InStr(receivedMessage, Chr(3), CompareMethod.Binary)
				End While
			Else
				'Debug.Print("No ETX in '" & receivedMessage & "'")
				Me.Invoke(Sub() txtPartialInput.Text = receivedMessage)
			End If
		Catch ex As Exception
			' Handle errors
			Debug.Print("Exception caught and ignored?")
		End Try
	End Sub


	Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
		If SerialPort1.IsOpen Then SerialPort1.Close()
	End Sub

	Private Function ExpandControlCodes(input As String) As String
		Dim sb As New Text.StringBuilder()

		For Each ch As Char In input
			Dim code As Integer = Asc(ch)
			If ControlCodeMap.ContainsKey(code) Then
				sb.Append("[" & ControlCodeMap(code) & "]")
			Else
				sb.Append(ch)
			End If
		Next
		Return sb.ToString()
	End Function

	Private Sub CheckBoxRTS_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBoxRTS.CheckedChanged
		SerialPort1.RtsEnable = CheckBoxRTS.Checked
	End Sub


	Private Sub ButtonMasterVolUp_Click(sender As Object, e As EventArgs) Handles ButtonMasterVolUp.Click
		SendMessageToAVR(Chr(2) & "07A1A" & Chr(3))
	End Sub

	Private Sub ButtonMasterVolDown_Click(sender As Object, e As EventArgs) Handles ButtonMasterVolDown.Click
		SendMessageToAVR(Chr(2) & "07A1B" & Chr(3))
	End Sub

	Private Sub CheckBoxMasterMute_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBoxMasterMute.CheckedChanged
		SendMessageToAVR(IIf(CheckBoxMasterMute.Checked, Chr(2) & "07EA2" & Chr(3), "07EA3"))
	End Sub

	Private Sub ButtonZone2VolUp_Click(sender As Object, e As EventArgs) Handles ButtonZone2VolUp.Click
		SendMessageToAVR(Chr(2) & "07ADA" & Chr(3))
	End Sub

	Private Sub ButtonZone2VolDown_Click(sender As Object, e As EventArgs) Handles ButtonZone2VolDown.Click
		SendMessageToAVR(Chr(2) & "07ADB" & Chr(3))
	End Sub

	Private Sub CheckBoxZone2Mute_CheckedChanged(sender As Object, e As EventArgs) Handles CheckBoxZone2Mute.CheckedChanged
		SendMessageToAVR(IIf(CheckBoxZone2Mute.Checked, Chr(2) & "07EA0" & Chr(3), "07EA1"))
	End Sub

	Private Sub SendMessageToAVR(msg As String)
		If Not ButtonConnect.Enabled Then
			SerialPort1.WriteLine(msg)
			ListBoxReceived.Items.Add("> " & ExpandControlCodes(msg))
			ListBoxReceived.SelectedIndex = ListBoxReceived.Items.Count - 1
		End If
	End Sub

	'
	' Power - main and zone
	'
	Private Sub btnPowerOn_Click(sender As Object, e As EventArgs) Handles btnPowerOn.Click
		SendMessageToAVR(Chr(2) & "07A1D" & Chr(3))
	End Sub

	Private Sub btnPowerOff_Click(sender As Object, e As EventArgs) Handles btnPowerOff.Click
		SendMessageToAVR(Chr(2) & "07A1E" & Chr(3))
	End Sub

	Private Sub btnPowerZone1On_Click(sender As Object, e As EventArgs) Handles btnPowerZone1On.Click
		SendMessageToAVR(Chr(2) & "07E7E" & Chr(3))
	End Sub

	Private Sub btnPowerZone1Off_Click(sender As Object, e As EventArgs) Handles btnPowerZone1Off.Click
		SendMessageToAVR(Chr(2) & "07E7F" & Chr(3))
	End Sub

	Private Sub btnPowerZone2On_Click(sender As Object, e As EventArgs) Handles btnPowerZone2On.Click
		SendMessageToAVR(Chr(2) & "07EBa" & Chr(3))
	End Sub
	Private Sub btnPowerZone2Off_Click(sender As Object, e As EventArgs) Handles btnPowerZone2Off.Click
		SendMessageToAVR(Chr(2) & "07EBA" & Chr(3))
	End Sub



	'
	' Other stuff
	'
	Private Sub Form1_Resize(sender As Object, e As EventArgs) Handles Me.Resize
		Debug.Print("Resize")
	End Sub

	Private Sub ComboBoxPorts_SelectedIndexChanged(sender As Object, e As EventArgs) Handles ComboBoxPorts.SelectedIndexChanged
		If ComboBoxPorts.Text <> "" Then
			ButtonConnect.Enabled = True
		Else
			ButtonConnect.Enabled = False
		End If
	End Sub


	Private Sub ProcessAVRMessage(msg As String)
		' Process incoming messages from AVR here
		Debug.Print("Processing AVR message: " & ExpandControlCodes(msg))
		Dim response As String = Mid(msg, 2, Len(msg) - 2)
		Debug.Print("                      : " & response)
		If (Mid(response, 1, 4) = "0026") Then
			' Volume in the next byte
			Dim vol As Single = Convert.ToInt16(Mid(response, 5, 2), 16)  ' C7 is 0db
			If (vol > &HC7) Then
				Debug.Print((vol - &HC7) / 2)
			Else
				Debug.Print((vol - &HC7) / 2.5)
			End If
		End If
	End Sub

End Class
