@page "/robots/model"
@attribute [Authorize]

@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using RobotNet.MapShares
@using RobotNet.RobotShares.Dtos
@using RobotNet.RobotShares.Enums
@using RobotNet.Shares
@using RobotNet.WebApp.Robots.Components
@using RobotNet.WebApp.Robots.Components.Robot
@using System.Net.Http.Headers

@inject IHttpClientFactory HttpFactory
@inject ISnackbar Snackbar
@inject IDialogService Dialog

<PageTitle>Robot Model</PageTitle>

<style>
    .selected {
    background-color: #3399ff !important;
    }

    .selected > td {
    color: white !important;
    }

    .selected > td .mud-input {
    color: white !important;
    }
</style>

<div class="d-flex flex-row w-100 h-100 p-2 overflow-hidden">
    <div class="d-flex h-100 flex-column flex-grow-1 pe-2">
        <div class="d-flex justify-content-between align-items-center mb-2">
            <MudTextField Value="txtSearch" T="string" Label="Search" Variant="Variant.Outlined" Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Filled.Search"
            AdornmentColor="Color.Secondary" ValueChanged="TextSearchChanged" Style="max-width: 550px" />
            <MudButton StartIcon="@Icons.Material.Filled.Add" Variant="Variant.Filled" Color="Color.Primary" Size="Size.Large" OnClick="OpenCreateRobotModel">
                NEW
            </MudButton>
        </div>
        <div class="d-flex" style="height: 92%">
            <MudTable Class="w-100" @ref="Table" Items="@RobotModelsShow" T="RobotModelDto" Dense=true Hover=true ReadOnly=true FixedHeader=true RowClass="cursor-pointer" Striped="true"
            ServerData="ReloadData" Loading=@IsLoading Outlined="true" RowClassFunc="@SelectedRowClassFunc" OnRowClick="RowClickEvent" Height="95%">
                <HeaderContent>
                    <MudTh>Nr</MudTh>
                    <MudTh>Name</MudTh>
                    <MudTh>Length (m)</MudTh>
                    <MudTh>Width (m)</MudTh>
                    <MudTh>NavigationPointX (m)</MudTh>
                    <MudTh>NavigationPointY (m)</MudTh>
                    <MudTh>NavigationType</MudTh>
                    <MudTh></MudTh>
                </HeaderContent>
                <RowTemplate>
                    <MudTd DataLabel="Nr">
                        @(Table?.CurrentPage * Table?.RowsPerPage + RobotModelsShow.IndexOf(context) + 1)
                    </MudTd>
                    <MudTd DataLabel="Name">
                        @context.ModelName
                    </MudTd>
                    <MudTd DataLabel="Length">
                        @context.Length
                    </MudTd>
                    <MudTd DataLabel="Width">
                        @context.Width
                    </MudTd>
                    <MudTd DataLabel="OriginX">
                        @context.OriginX
                    </MudTd>
                    <MudTd DataLabel="OriginY">
                        @context.OriginY
                    </MudTd>
                    <MudTd DataLabel="NavigationType">
                        @context.NavigationType
                    </MudTd>
                    <MudTd>
                        <div class="d-flex flex-row-reverse">
                            <MudMenu Icon="@Icons.Material.Filled.MoreVert" AnchorOrigin="Origin.BottomCenter">
                                <MudMenuItem OnClick="(() => OpenUpdateRobotModel(context))" Icon="@Icons.Material.Filled.Edit" IconColor="Color.Info">Edit</MudMenuItem>
                                <MudMenuItem OnClick="(() => DeleteRobotModel(context.Id))" Icon="@Icons.Material.Filled.Delete" IconColor="Color.Error">Delete</MudMenuItem>
                            </MudMenu>
                        </div>
                    </MudTd>
                </RowTemplate>
                <PagerContent>
                    <div class="d-flex w-100 flex-row-reverse">
                        <MudTablePager Style="width: 100%;" PageSizeOptions="new[] {25 , 100, 200}" />
                    </div>
                </PagerContent>
            </MudTable>
        </div>
    </div>
    <div class="robot-preview">
        <RobotModelPreview @ref="@RobotModelPreviewRef"/>
    </div>
</div>

<MudDialog @bind-Visible="IsCreateRobotModelVisible">
    <TitleContent>
        <MudText Typo="Typo.h6">
            Create New Robot Model
        </MudText>
    </TitleContent>
    <DialogContent>
        <MudContainer Class="d-flex flex-column" Style="max-height: 700px; overflow-y: auto">
            <MudTextField Class="my-2" ShrinkLabel
                          @bind-Value="RobotModelCreateModel.ModelName" T="string"
                          Label="Name" Required="true" RequiredError="Name is required!"
                          Counter="18" Variant="Variant.Outlined"
                          Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.NameValidation))" />
            <div class="d-flex flex-row my-2">
                <MudNumericField Class="me-2" @bind-Value="RobotModelCreateModel.Length" Label="Length" Step="0.1" Min="0" Variant="Variant.Outlined" />
                <MudNumericField Class="ms-2" @bind-Value="RobotModelCreateModel.Width" Label="Width" Step="0.1" Min="0" Variant="Variant.Outlined" />
            </div>
            <div class="d-flex flex-row my-2">
                <MudNumericField Class="me-2" @bind-Value="RobotModelCreateModel.OriginX" Label="NavigationPointX" Step="0.1" Variant="Variant.Outlined" />
                <MudNumericField Class="ms-2" @bind-Value="RobotModelCreateModel.OriginY" Label="NavigationPointY" Step="0.1" Variant="Variant.Outlined" />
            </div>
            <MudSelect Class="my-2" T="NavigationType" Label="Navigation Type" Variant="Variant.Outlined" @bind-Value="RobotModelCreateModel.NavigationType">
                @foreach (NavigationType type in Enum.GetValues(typeof(NavigationType)))
                {
                    <MudSelectItem Value="@type">@type</MudSelectItem>
                }
            </MudSelect>
            <div class="d-flex flex-column">
                <div class="d-flex flex-row my-2">
                    <InputFile id="fileRobotModelImageCreate" OnChange="RobotModelCreateImageChanged" accept=".png" hidden />
                    <MudButton HtmlTag="label"
                               Variant="Variant.Filled"
                               Color="Color.Primary"
                               StartIcon="@Icons.Material.Filled.CloudUpload"
                               for="fileRobotModelImageCreate">
                        Robot Model Image
                    </MudButton>
                </div>
                <div class="d-flex mb-2">
                    <div class="robot-create-item">
                        <img alt="Robot Model Image" src="@ProjectionImageFilePreview" style="image-rendering: pixelated;" />
                    </div>
                </div>
            </div>

        </MudContainer>
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="@(() => IsCreateRobotModelVisible = false)" Variant="Variant.Filled">Cancel</MudButton>
        <MudButton Color="Color.Primary" OnClick="CreateNewRobotModel" Variant="Variant.Filled">Create</MudButton>
    </DialogActions>
</MudDialog>

<MudDialog @bind-Visible="IsUpdateRobotModelVisible">
    <TitleContent>
        <MudText Typo="Typo.h6">
            Update Robot Model
        </MudText>
    </TitleContent>
    <DialogContent>
        <MudContainer Class="d-flex flex-column" Style="max-height: 700px;">
            <MudTextField Class="my-2"
                          @bind-Value="RobotModelUpdateModel.ModelName" T="string"
                          Label="RobotModel's name" Required="true"
                          RequiredError="Robot Model name is required!" Counter="127" Variant="Variant.Outlined"
                          Validation="@(new Func<string, IEnumerable<string>>(MapEditorHelper.NameValidation))" />
            <div class="d-flex flex-row my-2">
                <MudNumericField Class="me-2" @bind-Value="RobotModelUpdateModel.Length" Label="Length" Step="0.1" Variant="Variant.Outlined" />
                <MudNumericField Class="ms-2" @bind-Value="RobotModelUpdateModel.Width" Label="Width" Step="0.1" Variant="Variant.Outlined" />
            </div>
            <div class="d-flex flex-row my-2">
                <MudNumericField Class="me-2" @bind-Value="RobotModelUpdateModel.OriginX" Label="NavigationPointX" Step="0.1" Variant="Variant.Outlined" />
                <MudNumericField Class="ms-2" @bind-Value="RobotModelUpdateModel.OriginY" Label="NavigationPointY" Step="0.1" Variant="Variant.Outlined" />
            </div>
        </MudContainer>
    </DialogContent>
    <DialogActions>
        <MudButton OnClick="@(() => IsUpdateRobotModelVisible = false)" Variant="Variant.Filled">Cancel</MudButton>
        <MudButton Color="Color.Primary" OnClick="UpdateRobotModel" Variant="Variant.Filled">Update</MudButton>
    </DialogActions>
</MudDialog>

@code {
    private string txtSearch = "";
    private bool IsLoading = false;

    private RobotModelPreview RobotModelPreviewRef = default!;

    private List<RobotModelDto> RobotModels = [];
    private List<RobotModelDto> RobotModelsShow = [];
    private MudTable<RobotModelDto>? Table;

    private int selectedRowNumber = -1;
    private RobotModelDto RobotModelSelected = new();

    private bool IsCreateRobotModelVisible { get; set; }
    private readonly RobotModelCreateModel RobotModelCreateModel = new();
    private IBrowserFile? RobotModelCreateImage { get; set; }
    private string? ProjectionImageFilePreview { get; set; }

    private bool IsUpdateRobotModelVisible { get; set; }
    private readonly RobotModelUpdateModel RobotModelUpdateModel = new();


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

        await LoadRobotModels();
    }

    private async Task LoadRobotModels()
    {
        try
        {
            IsLoading = true;
            RobotModels.Clear();
            StateHasChanged();

            using var Http = HttpFactory.CreateClient("RobotManagerAPI");
            var robotmodels = await Http.GetFromJsonAsync<IEnumerable<RobotModelDto>>($"api/RobotModels?txtSearch={txtSearch}");
            RobotModels.AddRange(robotmodels ?? []);

            Table?.ReloadServerData();
            IsLoading = false;
            StateHasChanged();
        }
        catch (AccessTokenNotAvailableException ex)
        {
            ex.Redirect();
            return;
        }
    }

    private void TextSearchChanged(string text)
    {
        txtSearch = text;
        Table?.ReloadServerData();
    }

    private bool FilterFunc(RobotModelDto robotmodel)
    {
        if (string.IsNullOrWhiteSpace(txtSearch))
            return true;
        if (robotmodel.ModelName is not null && robotmodel.ModelName.Contains(txtSearch, StringComparison.OrdinalIgnoreCase))
            return true;
        if ($"{robotmodel.ModelName}".Contains(txtSearch))
            return true;
        return false;
    }

    private Task<TableData<RobotModelDto>> ReloadData(TableState state, CancellationToken _)
    {
        RobotModelsShow.Clear();
        var tasks = new List<RobotModelDto>();
        RobotModels.ForEach(task =>
        {
            if (FilterFunc(task)) tasks.Add(task);
        });
        RobotModelsShow = tasks.Skip(state.Page * state.PageSize).Take(state.PageSize).ToList();
        return Task.FromResult(new TableData<RobotModelDto>() { TotalItems = tasks.Count, Items = RobotModelsShow });
    }

    private void RowClickEvent(TableRowClickEventArgs<RobotModelDto> tableRowClickEventArgs) { }

    private string SelectedRowClassFunc(RobotModelDto element, int rowNumber)
    {
        if (selectedRowNumber == rowNumber && Table?.SelectedItem != null && !Table.SelectedItem.Equals(element))
        {
            return string.Empty;
        }
        else if (selectedRowNumber == rowNumber && Table?.SelectedItem != null && Table.SelectedItem.Equals(element))
        {
            return "selected";
        }
        else if (Table?.SelectedItem != null && Table.SelectedItem.Equals(element))
        {
            selectedRowNumber = rowNumber;
            RobotModelSelected = element;
            RobotModelPreviewRef.SetRobotPreview(RobotModelSelected);
            return "selected";
        }
        else
        {
            return string.Empty;
        }
    }

    private void OpenCreateRobotModel()
    {
        RobotModelCreateModel.ModelName = string.Empty;
        RobotModelCreateModel.OriginX = 0;
        RobotModelCreateModel.OriginY = 0;
        RobotModelCreateModel.Length = 0;
        RobotModelCreateModel.Width = 0;
        RobotModelCreateModel.NavigationType = NavigationType.Differential;
        ProjectionImageFilePreview = "/images/Image-not-found.png";
        RobotModelCreateImage = null;
        IsCreateRobotModelVisible = true;
        StateHasChanged();
    }

    private async Task RobotModelCreateImageChanged(InputFileChangeEventArgs e)
    {
        RobotModelCreateImage = e.File;
        if (RobotModelCreateImage is not null)
        {
            var buffers = new byte[RobotModelCreateImage.Size];
            var path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
            await using var fs = new FileStream(path, FileMode.Create);
            await RobotModelCreateImage.OpenReadStream(RobotModelCreateImage.Size).CopyToAsync(fs);
            fs.Position = 0;
            await fs.ReadAsync(buffers);
            fs.Close();
            File.Delete(path);

            ProjectionImageFilePreview = $"data:{RobotModelCreateImage.ContentType};base64,{Convert.ToBase64String(buffers)}";
            StateHasChanged();
        }
    }

    private async Task CreateNewRobotModel()
    {
        try
        {
            if (RobotModelCreateImage is null || string.IsNullOrEmpty(ProjectionImageFilePreview))
            {
                Snackbar.Add("Ảnh robot chưa được chọn.", Severity.Warning);
                return;
            }

            if (string.IsNullOrEmpty(RobotModelCreateModel.ModelName))
            {
                Snackbar.Add("Map's name không được để trống.", Severity.Warning);
                return;
            }

            var nameInvalid = MapEditorHelper.NameChecking(RobotModelCreateModel.ModelName);
            if (!nameInvalid.IsSuccess)
            {
                Snackbar.Add(nameInvalid.returnStr, Severity.Warning);
                return;
            }

            using var content = new MultipartFormDataContent
            {
                { new StringContent(RobotModelCreateModel.ModelName ?? String.Empty), nameof(RobotModelCreateModel.ModelName) },
                { new StringContent(RobotModelCreateModel.OriginX.ToString()), nameof(RobotModelCreateModel.OriginX) },
                { new StringContent(RobotModelCreateModel.OriginY.ToString()), nameof(RobotModelCreateModel.OriginY) },
                { new StringContent(RobotModelCreateModel.Length.ToString()), nameof(RobotModelCreateModel.Length) },
                { new StringContent(RobotModelCreateModel.Width.ToString()), nameof(RobotModelCreateModel.Width) },
                { new StringContent(RobotModelCreateModel.NavigationType.ToString()), nameof(RobotModelCreateModel.NavigationType) }
            };

            var fileContent = new StreamContent(RobotModelCreateImage.OpenReadStream(maxAllowedSize: 2048000));
            fileContent.Headers.ContentType = new MediaTypeHeaderValue(RobotModelCreateImage.ContentType);
            content.Add(fileContent, "Image", RobotModelCreateImage.Name);

            using var Http = HttpFactory.CreateClient("RobotManagerAPI");
            var response = await (await Http.PostAsync("api/RobotModels", content)).Content.ReadFromJsonAsync<MessageResult<RobotModelDto>>();
            if (response is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
            else if (!response.IsSuccess) Snackbar.Add(response.Message ?? "Lỗi chưa xác định.", Severity.Error);
            else if (response.Data is null) Snackbar.Add("Hệ thống không thể tạo robot model này", Severity.Error);
            else
            {
                RobotModels.Add(response.Data);
                Table?.ReloadServerData();
                Snackbar.Add($"Tạo robot model {RobotModelCreateModel.ModelName} thành công.", Severity.Success);
            }

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

    private void OpenUpdateRobotModel(RobotModelDto robotmodel)
    {
        RobotModelUpdateModel.Id = robotmodel.Id;
        RobotModelUpdateModel.ModelName = robotmodel.ModelName;
        RobotModelUpdateModel.OriginX = robotmodel.OriginX;
        RobotModelUpdateModel.OriginY = robotmodel.OriginY;
        RobotModelUpdateModel.Length = robotmodel.Length;
        RobotModelUpdateModel.Width = robotmodel.Width;
        IsUpdateRobotModelVisible = true;
        StateHasChanged();
    }

    private async Task UpdateRobotModel()
    {
        var nameErrors = MapEditorHelper.NameChecking(RobotModelUpdateModel.ModelName);
        if (!nameErrors.IsSuccess)
        {
            Snackbar.Add(nameErrors.returnStr, Severity.Warning);
            return;
        }

        using var Http = HttpFactory.CreateClient("RobotManagerAPI");
        var result = await (await Http.PutAsJsonAsync($"api/RobotModels", RobotModelUpdateModel)).Content.ReadFromJsonAsync<MessageResult<RobotModelDto>>();
        if (result == null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
        else if (!result.IsSuccess) Snackbar.Add(result.Message, Severity.Error);
        else if (result.Data is null)
        {
            Snackbar.Add("Hệ thống không thể cập nhật robot model này", Severity.Error);
        }
        else
        {
            var robotmodel = RobotModels.FirstOrDefault(rm => rm.Id == RobotModelUpdateModel.Id);
            if (robotmodel is not null)
            {
                robotmodel.ModelName = result.Data.ModelName;
                robotmodel.OriginX = result.Data.OriginX;
                robotmodel.OriginY = result.Data.OriginY;
                robotmodel.Width = result.Data.Width;
                robotmodel.Length = result.Data.Length;
                Snackbar.Add("Cập nhật thành công", Severity.Success);
            }
            else Snackbar.Add("Robot Model không tồn tại", Severity.Error);
        }

        IsUpdateRobotModelVisible = false;
        StateHasChanged();
    }

    private async Task DeleteRobotModel(Guid robotId)
    {
        var parameters = new DialogParameters<ConfirmDialog>
        {
            { x => x.Content, "Bạn có chắc chắn muốn xóa robot model đi không?" },
            { x => x.ConfirmText, "Delete" },
            { x => x.Color, Color.Secondary }
        };
        var ConfirmDelete = await Dialog.ShowAsync<ConfirmDialog>("Xoá Robot Model", parameters);
        var result = await ConfirmDelete.Result;
        if (result is not null && result.Data is not null && bool.TryParse(result.Data.ToString(), out bool data) && data)
        {
            using var Http = HttpFactory.CreateClient("RobotManagerAPI");
            var response = await Http.DeleteFromJsonAsync<MessageResult>($"api/RobotModels/{robotId}");
            if (response is null) Snackbar.Add("Lỗi giao tiếp với hệ thống", Severity.Error);
            else if (!response.IsSuccess) Snackbar.Add(response.Message ?? "Lỗi chưa xác định.", Severity.Error);
            else
            {
                var robotmodel = RobotModels.FirstOrDefault(m => m.Id == robotId);
                if (robotmodel is not null)
                {
                    if (robotmodel.Id == RobotModelSelected.Id)
                    {
                        RobotModelPreviewRef.SetRobotPreview(null);
                        Table?.SetSelectedItem(null);
                        selectedRowNumber = -1;
                    }
                    RobotModels.Remove(robotmodel);
                    Table?.ReloadServerData();
                    Snackbar.Add($"Xóa robot model thành công.", Severity.Success);
                }
                else Snackbar.Add($"Robot Model không tồn tại.", Severity.Warning);
            }
            StateHasChanged();
        }
    }
}
