@page "/"
@rendermode InteractiveServer
@implements IDisposable

@using IOModuleTestBlazor.Models
@using IOModuleTestBlazor.Services
@using Peak.Can.Basic

@inject ICanService CanService

<PageTitle>IO Module Dashboard</PageTitle>

<div class="container-fluid">
    <div class="row">
        <div class="col-12">
            <h1>IO Module Dashboard</h1>
            
            @* ── Connection Status ──────────────────────────────────────────────────── *@
            <div class="card mb-3">
                <div class="card-body py-2">
                    @if (CanService.IsConnected)
                    {
                        <div class="d-flex align-items-center gap-3">
                            <span class="badge bg-success">● Connected</span>
                            <span class="font-monospace fw-semibold">@CanService.ChannelName</span>
                            <span class="text-muted">@GetBitrateLabel(CanService.CurrentBitrate)</span>
                        </div>
                    }
                    else
                    {
                        <span class="badge bg-danger">● Disconnected</span>
                        <span class="text-muted ms-2">No CAN connection</span>
                    }
                </div>
            </div>

            @* ── Push Button Status ─────────────────────────────────────────────────── *@
            <div class="card mb-3">
                <div class="card-header">
                    <h5 class="mb-0">Push Button Status</h5>
                </div>
                <div class="card-body">
                    <div class="row text-center">
                        <div class="col-md-4">
                            <div class="mb-2">
                                <div class="status-light @(button1Active ? "status-light-on" : "status-light-off")"></div>
                            </div>
                            <label>User Button (PC13)</label>
                        </div>
                        <div class="col-md-4">
                            <div class="mb-2">
                                <div class="status-light @(button2Active ? "status-light-on" : "status-light-off")"></div>
                            </div>
                            <label>Button 1 (CN9-29)</label>
                        </div>
                        <div class="col-md-4">
                            <div class="mb-2">
                                <div class="status-light @(button3Active ? "status-light-on" : "status-light-off")"></div>
                            </div>
                            <label>Button 2 (CN9-30)</label>
                        </div>
                    </div>
                </div>
            </div>

            @* ── Termination Control ────────────────────────────────────────────────── *@
            <div class="card mb-3">
                <div class="card-header">
                    <h5 class="mb-0">CAN Hat Termination Control</h5>
                </div>
                <div class="card-body">
                    <div class="row text-center">
                        <div class="col-md-6">
                            <div class="mb-2">
                                <div class="status-light @(_termOn ? "status-light-on" : "status-light-off")"></div>
                            </div>
                            <label class="d-block mb-2">TERM_ON (PB10)</label>
                            <button class="btn btn-sm @(_termOn ? "btn-success" : "btn-outline-secondary")"
                                    @onclick="ToggleTermOn"
                                    disabled="@(!CanService.IsConnected)">
                                @(_termOn ? "ON" : "OFF")
                            </button>
                        </div>
                        <div class="col-md-6">
                            <div class="mb-2">
                                <div class="status-light @(_termOff ? "status-light-on" : "status-light-off")"></div>
                            </div>
                            <label class="d-block mb-2">TERM_OFF (PB11)</label>
                            <button class="btn btn-sm @(_termOff ? "btn-success" : "btn-outline-secondary")"
                                    @onclick="ToggleTermOff"
                                    disabled="@(!CanService.IsConnected)">
                                @(_termOff ? "ON" : "OFF")
                            </button>
                        </div>
                    </div>
                    @if (!CanService.IsConnected)
                    {
                        <p class="text-muted text-center small mt-2 mb-0">Connect to CAN to control termination</p>
                    }
                </div>
            </div>

            @* ── CAN Messages Table ──────────────────────────────────────────────────── *@
            <div class="card">
                <div class="card-header d-flex justify-content-between align-items-center">
                    <h5 class="mb-0">CAN Messages</h5>
                    <div class="d-flex gap-2">
                        <span class="badge bg-secondary">@latestMessages.Count message types</span>
                        <button class="btn btn-sm btn-outline-secondary" @onclick="ClearMessages">Clear</button>
                    </div>
                </div>
                <div class="card-body p-0">
                    @if (latestMessages.Count == 0)
                    {
                        <div class="p-3 text-center text-muted">
                            @if (CanService.IsConnected)
                            {
                                <p>Waiting for CAN messages...</p>
                            }
                            else
                            {
                                <p>Connect to CAN to see messages</p>
                            }
                        </div>
                    }
                    else
                    {
                        <div class="table-responsive" style="max-height: 400px; overflow-y: auto;">
                            <table class="table table-sm table-striped mb-0">
                                <thead class="table-dark sticky-top">
                                    <tr>
                                        <th>Timestamp</th>
                                        <th>ID</th>
                                        <th>DLC</th>
                                        <th>Data</th>
                                        <th>Signals</th>
                                    </tr>
                                </thead>
                                <tbody>
                                    @foreach (var msg in latestMessages.Values.OrderBy(m => m.Id))
                                    {
                                        <tr>
                                            <td class="font-monospace small">@FormatTimestamp(msg.TimestampUs)</td>
                                            <td class="font-monospace">0x@(msg.Id.ToString("X3"))</td>
                                            <td>@msg.Dlc</td>
                                            <td class="font-monospace">@FormatData(msg.Data)</td>
                                            <td class="small">
                                                @if (msg.Signals?.Count > 0)
                                                {
                                                    @foreach (var signal in msg.Signals)
                                                    {
                                                        <span class="badge bg-info me-1">@signal.Key: @signal.Value.ToString("F2")</span>
                                                    }
                                                }
                                            </td>
                                        </tr>
                                    }
                                </tbody>
                            </table>
                        </div>
                    }
                </div>
            </div>
        </div>
    </div>
</div>

<style>
    .status-light {
        width: 40px;
        height: 40px;
        border-radius: 50%;
        margin: 0 auto;
        border: 2px solid #ccc;
        transition: all 0.3s ease;
    }
    
    .status-light-on {
        background-color: #28a745;
        border-color: #28a745;
        box-shadow: 0 0 15px rgba(40, 167, 69, 0.7);
    }
    
    .status-light-off {
        background-color: #6c757d;
        border-color: #6c757d;
    }
</style>

@code {
    private Dictionary<uint, CanMessageDto> latestMessages = new();
    private bool button1Active = false;
    private bool button2Active = false;
    private bool button3Active = false;
    private bool _termOn  = false;
    private bool _termOff = false;
    private Timer? _updateTimer;

    protected override void OnInitialized()
    {
        // Poll for updates every 50ms for responsive UI (20 FPS)
        _updateTimer = new Timer(UpdateUI, null, TimeSpan.Zero, TimeSpan.FromMilliseconds(50));
    }

    private void UpdateUI(object? state)
    {
        InvokeAsync(() =>
        {
            // Get latest messages from CAN service
            var newMessages = CanService.GetLatestMessages();
            
            // Check if any messages have changed
            bool hasChanges = false;
            
            foreach (var (id, message) in newMessages)
            {
                if (!latestMessages.TryGetValue(id, out var existing) || 
                    !existing.Data.SequenceEqual(message.Data) ||
                    existing.TimestampUs != message.TimestampUs)
                {
                    latestMessages[id] = message;
                    UpdateButtonStates(message);
                    hasChanges = true;
                }
            }
            
            // Only trigger UI update if something actually changed
            if (hasChanges)
            {
                StateHasChanged();
            }
        });
    }

    private void UpdateButtonStates(CanMessageDto message)
    {
        // STM32H7 Nucleo CAN Protocol Implementation
        // Based on CAN message documentation v1.2
        
        switch (message.Id)
        {
            case 0x210: // User Button State (onboard blue button)
                if (message.Data.Length > 0)
                {
                    // Use onboard button for Button 1 indicator
                    button1Active = message.Data[0] > 0;
                }
                break;
                
            case 0x220: // CN9-29 Button State (Button 1 - PG0)
                if (message.Data.Length >= 2)
                {
                    // Byte 0: Toggle state (persistent)
                    // Byte 1: GPIO state (real-time press state)
                    // Use GPIO state for immediate visual feedback
                    button2Active = message.Data[1] > 0;
                }
                break;
                
            case 0x230: // CN9-30 Button State (Button 2 - PG1)
                if (message.Data.Length >= 2)
                {
                    // Byte 0: Toggle state (persistent)
                    // Byte 1: GPIO state (real-time press state)
                    // Use GPIO state for immediate visual feedback
                    button3Active = message.Data[1] > 0;
                }
                break;

            case 0x250: // Termination pin status — bit0=TERM_ON (PB10), bit1=TERM_OFF (PB11)
                if (message.Data.Length > 0)
                {
                    _termOn  = (message.Data[0] & 0x01) != 0;
                    _termOff = (message.Data[0] & 0x02) != 0;
                }
                break;
        }
        
        // Alternative: Use toggle states instead of GPIO states for persistent indication
        // Uncomment these lines if you want the lights to stay on after button release:
        
        // case 0x220: // Button 1 Toggle State
        //     if (message.Data.Length > 0)
        //         button2Active = message.Data[0] > 0;
        //     break;
        //     
        // case 0x230: // Button 2 Toggle State  
        //     if (message.Data.Length > 0)
        //         button3Active = message.Data[0] > 0;
        //     break;
    }

    // ── Termination control ───────────────────────────────────────────────────

    private void ToggleTermOn()  { _termOn  = !_termOn;  SendTermination(); }
    private void ToggleTermOff() { _termOff = !_termOff; SendTermination(); }

    private void SendTermination()
    {
        var data = new byte[8];
        data[0] = (byte)((_termOn ? 0x01 : 0x00) | (_termOff ? 0x02 : 0x00));  // bit0=TERM_ON, bit1=TERM_OFF
        var msg = new PcanMessage(0x240, MessageType.Standard, 8, data, false);
        CanService.Write(msg);
    }

    private void ClearMessages()
    {
        latestMessages.Clear();
        StateHasChanged();
    }

    private string FormatTimestamp(ulong timestampUs)
    {
        var dt = DateTime.Now;
        var ms = (timestampUs / 1000) % 1000;
        return $"{dt:HH:mm:ss}.{ms:D3}";
    }

    private string FormatData(byte[] data)
    {
        return string.Join(" ", data.Select(b => b.ToString("X2")));
    }

    private string GetBitrateLabel(Peak.Can.Basic.Bitrate bitrate)
    {
        return bitrate switch
        {
            Peak.Can.Basic.Bitrate.Pcan1000 => "1000 kbps",
            Peak.Can.Basic.Bitrate.Pcan800 => "800 kbps",
            Peak.Can.Basic.Bitrate.Pcan500 => "500 kbps",
            Peak.Can.Basic.Bitrate.Pcan250 => "250 kbps",
            Peak.Can.Basic.Bitrate.Pcan125 => "125 kbps",
            Peak.Can.Basic.Bitrate.Pcan100 => "100 kbps",
            Peak.Can.Basic.Bitrate.Pcan50 => "50 kbps",
            Peak.Can.Basic.Bitrate.Pcan20 => "20 kbps",
            Peak.Can.Basic.Bitrate.Pcan10 => "10 kbps",
            Peak.Can.Basic.Bitrate.Pcan5 => "5 kbps",
            _ => bitrate.ToString()
        };
    }

    public void Dispose()
    {
        _updateTimer?.Dispose();
    }
}
