@using LANCommander.Data.Enums;
@using Microsoft.AspNetCore.SignalR.Client
@using NLog;
@using XtermBlazor
@inject ServerService ServerService
@inject ServerConsoleService ServerConsoleService
@inject ServerProcessService ServerProcessService
@inject NavigationManager NavigationManager
@inject IMessageService MessageService
@implements IAsyncDisposable

<Xterm @ref="Terminal" Options="TerminalOptions" AddonIds="TerminalAddons" />

@if (ServerConsole != null && ServerConsole.Type == ServerConsoleType.RCON)
{
    <Input @ref="CommandInput" @bind-Value="Command" BindOnInput OnPressEnter="OnPressEnter" @onkeyup="OnCommandKeyDown" />
}

@code {
    [Parameter] public Guid ServerId { get; set; }
    [Parameter] public Guid ServerConsoleId { get; set; }

    Xterm? Terminal;
    HubConnection? HubConnection;
    ServerConsole ServerConsole;

    Input<string> CommandInput;
    string Command;
    string[] History;
    int HistoryPosition;

    TerminalOptions TerminalOptions = new TerminalOptions
    {
        CursorBlink = true,
        CursorStyle = CursorStyle.Block
    };

    string[] TerminalAddons = new string[]
    {
        "xterm-addon-fit"
    };

    protected override async Task OnInitializedAsync()
    {
        if (ServerConsoleId != Guid.Empty)
        {
            ServerConsole = await ServerConsoleService.Get(ServerConsoleId);

            await Connect(ServerConsole);
        }
        else
        {
            await Connect();
        }
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await Task.Delay(100);

            if (Terminal != null)
                await Terminal.InvokeAddonFunctionVoidAsync("xterm-addon-fit", "fit");

            if (ServerConsoleId != Guid.Empty)
            {
                var log = await ServerConsoleService.ReadLog(ServerConsoleId);

                foreach (var line in log)
                {
                    await Terminal.WriteLine(line);
                }
            }
        }
    }

    async Task Connect()
    {
        HubConnection = new HubConnectionBuilder()
            .WithUrl(NavigationManager.ToAbsoluteUri("/hubs/gameserver"))
            .Build();

        HubConnection.On<Guid, string>("Log", (serverId, message) =>
        {
            if (serverId == ServerId)
                Terminal.WriteLine(message);
        });

        await HubConnection.StartAsync();
    }

    async Task Connect(ServerConsole serverConsole)
    {
        History = new string[50];
        HistoryPosition = 0;

        if (ServerConsole.Type == ServerConsoleType.LogFile)
        {
            HubConnection = new HubConnectionBuilder()
                .WithUrl(NavigationManager.ToAbsoluteUri("/hubs/gameserver"))
                .Build();

            HubConnection.On<Guid, Guid, string>("Log", (serverId, logId, message) =>
            {
                if (serverId == ServerId && logId == ServerConsoleId)
                    Terminal.WriteLine(message);
            });

            await HubConnection.StartAsync();
        }
        else if (ServerConsole.Type == ServerConsoleType.RCON)
        {
            try
            {
                ServerProcessService.RconConnect(ServerConsole);
            }
            catch (Exception ex)
            {
                await MessageService.Error($"Could not connect to RCON server: {ex.Message}");
            }
        }
    }

    public async Task OnPressEnter() {
        await ServerProcessService.RconSendCommandAsync(Command, ServerConsole);

        Array.Copy(History, 0, History, 1, History.Length - 1);
        History[0] = Command;
        HistoryPosition = -1;
        Command = "";
        CommandInput.Value = "";
        await Task.Yield();
        await CommandInput.ValueChanged.InvokeAsync();
        await CommandInput.Focus(FocusBehavior.FocusAndClear);

        StateHasChanged();
    }

    public async Task OnCommandKeyDown(KeyboardEventArgs args)
    {
        switch (args.Key)
        {
            case "ArrowUp":
                if (HistoryPosition < History.Length && History[HistoryPosition + 1] != null)
                    HistoryPosition++;

                Command = History[HistoryPosition];

                StateHasChanged();
                break;

            case "ArrowDown":
                if (HistoryPosition >= 0)
                {
                    HistoryPosition--;

                    if (HistoryPosition >= 0)
                        Command = History[HistoryPosition];
                    else
                    {
                        Command = "";
                    }
                }

                StateHasChanged();
                break;
        }
    }

    public async ValueTask DisposeAsync()
    {
        if (HubConnection is not null)
            await HubConnection.DisposeAsync();
    }
}
