@page "/robots/monitor"
@attribute [Authorize]
@implements IAsyncDisposable

@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using RobotNet.MapShares.Dtos
@using RobotNet.RobotShares.Dtos
@using RobotNet.RobotShares.Enums
@using RobotNet.Shares
@using RobotNet.WebApp.Clients
@using RobotNet.WebApp.Robots.Components.Monitoring

@inject ISnackbar Snackbar
@inject IHttpClientFactory HttpFactory
@inject RobotHubClient RobotHub

<PageTitle>Robot Monitoring</PageTitle>

<div class="d-flex flex-row w-100 h-100 position-relative">
    <div class="d-flex h-100 flex-grow-1 flex-column">
        <MonitorToolbar @ref="MonitorToolbarRef" Model="@ToolBarModel" ExpandChanged="ExpandChanged" ButtonEventClick="ButtonEventClick"
        CheckedEventClick="ToolbarCheckedClick" MapChanged="MapChanged" RobotChanged="RobotChanged" />
        <div class="flex-grow-1 w-100">
            <MonitorMap @ref="MonitorMapRef" ShowGrid="@ToolBarModel.ShowGrid" ShowName="@ToolBarModel.ShowName" ShowPath="@ToolBarModel.ShowPath" ShowLaser="@ToolBarModel.ShowLaser" ShowElement="@ToolBarModel.ShowElement"/>
        </div>
    </div>
    <div class="h-100 d-flex flex-column align-items-center" style="width: fit-content; border-left: 2px solid black;">
        <MonitorInfomation @ref="@MonitorInfomationRef" />
    </div>

    <MudOverlay @bind-Visible="IsDeactive" DarkBackground="true" Absolute="true">
        <MudText Typo="Typo.h1" Color="@Color.Error" Style="font-weight: bold;">@IsDeactiveText</MudText>
    </MudOverlay>
</div>

@code {

    private MonitorMap MonitorMapRef = null!;
    private MonitorToolbar MonitorToolbarRef = null!;
    private MonitorInfomation MonitorInfomationRef = null!;
    private MonitorToolBarModel ToolBarModel = new()
        {
            ShowGrid = false,
            ShowName = true,
            ShowPath = true,
            FocusRobot = false,
            ShowLaser = false,
            ShowElement = true,
        };

    private List<MapInfoDto> Maps = [];
    private Guid MapIdSelected = new();

    private bool IsDeactive = false;
    private string IsDeactiveText = "Deactive";
    private RobotInfomationDto RobotSelected = new();

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await base.OnAfterRenderAsync(firstRender);
        if (!firstRender) return;

        RobotHub.MapDeactive += OnDeactive;
        RobotHub.UpdateChanged += OnUpdateChanged;
        RobotHub.ElementsStateChanged += ElementsStateChanged;
        await RobotHub.StartAsync();

        await LoadMaps();
    }

    private async Task LoadMaps()
    {
        try
        {
            IsDeactive = true;
            IsDeactiveText = "IsLoading...";
            Maps.Clear();
            StateHasChanged();

            using var Http = HttpFactory.CreateClient("MapManagerAPI");
            var maps = await Http.GetFromJsonAsync<IEnumerable<MapInfoDto>>($"api/MapsManager?txtSearch=");

            if (maps is null || maps.Count() == 0)
            {
                IsDeactiveText = "Map Empty";
                StateHasChanged();
                return;
            }

            var mapsActive = maps.Where(m => m.Active).ToList();
            if (mapsActive.Count == 0)
            {
                IsDeactiveText = "Map Empty";
                StateHasChanged();
                return;
            }

            Maps.AddRange(mapsActive ?? []);

            if (Maps.Count > 0)
            {
                MonitorToolbarRef.LoadMaps(Maps, Maps[0]);
                await LoadMapData(Maps[0].Id);
            }

            IsDeactive = false;
            StateHasChanged();
        }
        catch (AccessTokenNotAvailableException ex)
        {
            ex.Redirect();
            return;
        }
    }

    private async Task LoadMapData(Guid Id)
    {
        try
        {
            IsDeactive = true;
            IsDeactiveText = "IsLoading...";
            StateHasChanged();

            using var Http = HttpFactory.CreateClient("MapManagerAPI");
            var mapDataResult = await Http.GetFromJsonAsync<MessageResult<MapDataDto>>($"api/MapsData/{Id}");
            if (mapDataResult is not null && mapDataResult.Data is not null)
            {
                await MonitorMapRef.LoadMap(null);
                await MonitorMapRef.LoadMap(mapDataResult.Data);
                MapIdSelected = mapDataResult.Data.Id;
                IsDeactive = false;
                if (RobotHub.IsConnected) await RobotHub.MapActive(MapIdSelected);
            }
            else IsDeactiveText = "Map Not Existed";

            IsDeactive = false;
            StateHasChanged();
        }
        catch
        {
            IsDeactiveText = "Map Not Existed";
            StateHasChanged();
            return;
        }
    }

    private async Task ToolbarCheckedClick(MonitorToolbarCheckedType type, bool value)
    {
        switch (type)
        {
            case MonitorToolbarCheckedType.ShowGrid:
                ToolBarModel.ShowGrid = value;
                break;
            case MonitorToolbarCheckedType.ShowName:
                ToolBarModel.ShowName = value;
                break;
            case MonitorToolbarCheckedType.ShowPath:
                ToolBarModel.ShowPath = value;
                break;
            case MonitorToolbarCheckedType.ShowLaser:
                ToolBarModel.ShowLaser = value;
                break;
            case MonitorToolbarCheckedType.ShowElement:
                ToolBarModel.ShowElement = value;
                break;
            case MonitorToolbarCheckedType.FocusRobot:
                ToolBarModel.FocusRobot = value;
                MonitorMapRef.FocusRobot(value);
                if (value) await MonitorMapRef.FocusRobotAsync();
                break;
        }
        StateHasChanged();
    }

    private void ExpandChanged(bool value) => MonitorInfomationRef.ExpandedClick(value);

    private async Task ButtonEventClick(MonitorToolbarButtonType type)
    {
        switch (type)
        {
            case MonitorToolbarButtonType.Fit:
                await MonitorMapRef.ScaleFitContentAsync();
                break;
            case MonitorToolbarButtonType.Focus:
                await MonitorMapRef.FocusRobotAsync();
                break;
            case MonitorToolbarButtonType.ZoomIn:
                await MonitorMapRef.ScaleZoom(0.5);
                break;
            case MonitorToolbarButtonType.ZoomOut:
                await MonitorMapRef.ScaleZoom(-0.5);
                break;
        }
    }

    private async Task MapChanged(MapInfoDto map)
    {
        if (map.Id != MapIdSelected) await LoadMapData(map.Id);
    }

    private void RobotChanged(RobotInfomationDto robot)
    {
        if(robot.RobotId != RobotSelected.RobotId)
        {
            RobotSelected = robot;
            MonitorMapRef.RobotSelectedChange(RobotSelected.RobotId);
        }
    }

    private bool IsUpdateing = false;
    private void OnUpdateChanged(IEnumerable<RobotInfomationDto> robotInfos)
    {
        if (IsUpdateing) return;
        IsUpdateing = true;
        InvokeAsync(async () =>
        {
            var robots = robotInfos.Where(r => r.MapId == MapIdSelected).ToList();
            await MonitorMapRef.SetRobotPosition([.. robots]);
            MonitorToolbarRef.LoadRobots([.. robotInfos]);
            var robotSelect = robots.FirstOrDefault(r => r.RobotId == RobotSelected.RobotId);
            if (robotSelect is not null) MonitorInfomationRef.UpdateState(robotSelect);
            IsUpdateing = false;
        });
    }

    private void ElementsStateChanged(IEnumerable<ElementDto> elementsState)
    {
        _ = InvokeAsync(() =>
        {
            var elements = elementsState.Where(e => e.MapId == MapIdSelected).ToList();
            MonitorMapRef.ElementStateUpdated([.. elements]);
        });
    }

    private void OnDeactive()
    {
        IsDeactiveText = "Deactive";
        IsDeactive = true;
        StateHasChanged();
        RobotHub.UpdateChanged -= OnUpdateChanged;
        RobotHub.MapDeactive -= OnDeactive;
        _ = RobotHub.StopAsync();
    }

    public async ValueTask DisposeAsync()
    {
        RobotHub.MapDeactive -= OnDeactive;
        RobotHub.UpdateChanged -= OnUpdateChanged;
        await RobotHub.StopAsync();
    }
}
