Option Explicit On
Option Strict On

Imports System.IO
Imports System.IO.Ports
Imports System.Management
Imports System.Text

Public Class Form1

    Public WithEvents Serial As New SerialPort
    Public WithEvents Button As New Button
    Private Const RAW_REPL As Char = Chr(1)
    Private Const USER_FRIENDLY As Char = Chr(2)
    Private Const BREAK As Char = Chr(3)
    Private Const SOFT_RESET As Char = Chr(4)
    Private Const PASTE_MODE As Char = Chr(5)
    Private Const RASPBERRY_PI_PICO_DEVICE_NAME As String = "USB シリアル デバイス"
    Private Const SPLITSIZE As Integer = 2048

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Button.Text = "送信"
        Me.Controls.Add(Button)
        InitSerial()
    End Sub

    Private Sub InitSerial()
        'シリアルデバイスの列挙・選択
        Dim serialPorts As New ManagementClass("Win32_SerialPort")
        For Each port As ManagementObject In serialPorts.GetInstances()
            Dim deviceName As String = port.GetPropertyValue("Caption").ToString
            If deviceName.StartsWith(RASPBERRY_PI_PICO_DEVICE_NAME) Then
                Serial.PortName = port.GetPropertyValue("DeviceID").ToString
            End If
        Next
        'シリアルポートの初期設定
        With Serial
            .BaudRate = 115200
            .DataBits = 8
            .Parity = Parity.None
            .StopBits = StopBits.One
            .Handshake = Handshake.None
            .DtrEnable = True 'これがないとPICOが反応しない
            .NewLine = vbCrLf
        End With
        If Serial.IsOpen Then
            Serial.Close()
        End If
        Serial.Open()
    End Sub

    Private Sub Button_Click(sender As Object, e As EventArgs) Handles Button.Click
        SendFileToPICO("./test.wav")
        MessageBox.Show("転送終了")
    End Sub

    Private Sub SendFileToPICO(filename As String)
        PicoOpen(filename)
        Dim bytes As Byte() = My.Computer.FileSystem.ReadAllBytes(filename)
        For i As Integer = 0 To bytes.Length - 1 Step SPLITSIZE
            Dim code As New StringBuilder
            For j As Integer = i To i + SPLITSIZE - 1
                If j > bytes.Length - 1 Then
                    Exit For
                End If
                code.Append(bytes(j).ToString("x2"))
            Next
            SendToPico(code.ToString)
        Next
        PicoClose()
    End Sub

    Private Sub PicoOpen(filename As String)
        With Serial
            .Write(BREAK) '実行中のコード停止
            .Write(RAW_REPL)
            .Write(SOFT_RESET) 'リブート
            .Write(RAW_REPL)
            .WriteLine("filename = '" & Path.GetFileName(filename) & "'")
            .WriteLine("fp = open(filename, 'wb')")
            .Write(SOFT_RESET) 'コード実行
            .Write(RAW_REPL)
            .WriteLine("from binascii import unhexlify")
            .WriteLine("def w(x):")
            .WriteLine(vbTab & "fp.write(unhexlify(x))")
            .WriteLine(vbTab & "fp.flush()")
            .Write(SOFT_RESET) 'コード実行
        End With
    End Sub

    Private Sub SendToPico(code As String)
        With Serial
            .Write(RAW_REPL)
            .WriteLine("w(b'" & code & "')")
            .Write(SOFT_RESET) 'コード実行
        End With
    End Sub

    Private Sub PicoClose()
        With Serial
            .Write(RAW_REPL)
            .WriteLine("fp.close()")
            .WriteLine("del w")
            .WriteLine("del filename")
            .WriteLine("del fp")
            .Write(SOFT_RESET)
            .WriteLine("import main.py") 'PICO内のmain.py実行
            .Write(SOFT_RESET)
        End With
    End Sub

    Private Sub Serial_DataReceived(sender As Object, e As SerialDataReceivedEventArgs) Handles Serial.DataReceived
        Dim data As String = Serial.ReadExisting
        Debug.Print(data)
    End Sub

End Class