GameHud.razor
@using Sandbox;
@using Sandbox.UI;
@using System;
@using System.Linq;
@inherits PanelComponent

<root>
    @if ( Manager.IsValid() )
    {
        <!-- ВІНЬЄТКА З КОЛЬОРОМ КОМАНДИ (ІНТУЇТИВНА ПІДКАЗКА) -->
        @if ( LocalPlayerTeam.IsValid() )
        {
            <div class="team-vignette" style="box-shadow: inset 0 0 150px @(GetCssColor(LocalPlayerTeam.TeamColor.WithAlpha(0.25f)));"></div>
        }
        <!-- ІНФОРМАЦІЯ ПРО ГРАВЦЯ (ЗЛІВА ЗВЕРХУ) -->
        @if ( LocalPlayerTeam.IsValid() )
        {
            <div class="player-info-top-left" style="border-left: 5px solid @GetCssColor(LocalPlayerTeam.TeamColor);">
                <div class="player-icon-container" @ref="PortraitContainer" style="border-color: @GetCssColor(LocalPlayerTeam.TeamColor);"></div>
                <div class="player-hp-bar">
                    @{
                        int hpSegments = (int)MathF.Ceiling(LocalPlayerTeam.Health / 20f);
                    }
                    @for(int i = 0; i < 5; i++)
                    {
                        <div class="hp-segment @(i < hpSegments ? "filled" : "empty")"></div>
                    }
                </div>
            </div>
        }

        <!-- АМУНІЦІЯ (СПРАВА ЗНИЗУ) -->
        @if ( LocalPlayerGun.IsValid() && !LocalPlayerTeam.IsDead )
        {
            <div class="ammo-bottom-right">
                <div class="ammo-icon">
                    <svg viewBox="0 0 24 24" class="ammo-svg" style="color: @GetCssColor(LocalPlayerTeam.TeamColor);">
                        <path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"/>
                    </svg>
                </div>
                <div class="ammo-text">
                    <span class="ammo-current">@LocalPlayerGun.AmmoInMagazine</span>
                    <span class="ammo-divider">/</span>
                    <span class="ammo-reserve">@LocalPlayerGun.AmmoReserve</span>
                </div>
                @if ( LocalPlayerGun.IsReloading )
                {
                    <div class="reloading-text">RELOADING...</div>
                }
            </div>
        }

        <!-- ВЕРХНЯ ЦЕНТРАЛЬНА СМУГА ПРОГРЕСУ -->
        <div class="hud-top-center">
            <div class="hud-bar-container">
                <!-- Ліва смуга: СИНЯ команда -->
                <div class="team-bar-left">
                    <div class="team-fill-left" style="width: @(BluePercent)%;"></div>
                    <div class="team-label-left">
                        <span class="team-score">@(MathF.Round(BluePercent))%</span>
                    </div>
                </div>

                <!-- Центральний розділювач -->
                <div class="hud-divider"></div>

                <!-- Права смуга: ЧЕРВОНА команда -->
                <div class="team-bar-right">
                    <div class="team-fill-right" style="width: @(RedPercent)%;"></div>
                    <div class="team-label-right">
                        <span class="team-score">@(MathF.Round(RedPercent))%</span>
                    </div>
                </div>
            </div>
        </div>

        <!-- ОКРЕМИЙ ТАЙМЕР СПРАВА ЗВЕРХУ -->
        <div class="hud-top-right-timer">
            <div class="timer-title">TIME</div>
            <div class="timer-value">@FormattedTime</div>
        </div>

        <!-- CONTROLS HINT / PANEL (BOTTOM LEFT) -->
        <div class="controls-bottom-left">
            @if ( ShowControls )
            {
                <div class="controls-panel">
                    <div class="controls-title">CONTROLS</div>
                    <div class="control-item"><span class="key-bind">[CTRL]</span> Dive / Swim in Paint</div>
                    <div class="control-item"><span class="key-bind">[SHIFT]</span> Sprint</div>
                    <div class="control-item"><span class="key-bind">[R]</span> Reload Weapon</div>
                </div>
            }
            else
            {
                <div class="press-tab-hint">Hold [TAB] for Controls</div>
            }
        </div>

        <!-- ОВЕРЛЕЙ КІНЦЯ ГРИ -->
        @if ( Manager.WinnerTeamId != -1 )
        {
            <div class="end-game-overlay">
                <div class="end-game-card">
                    <div class="time-up-banner">MATCH FINISHED</div>
                    
                    @if ( Manager.WinnerTeamId == 0 )
                    {
                        <div class="winner-title blue">BLUE TEAM WINS!</div>
                    }
                    else if ( Manager.WinnerTeamId == 1 )
                    {
                        <div class="winner-title red">RED TEAM WINS!</div>
                    }
                    else
                    {
                        <div class="winner-title draw">DRAW!</div>
                    }

                    <div class="results-visualizer">
                        <div class="team-result blue-result @(Manager.WinnerTeamId == 0 ? "winner" : "")">
                            <div class="team-name">BLUE</div>
                            <div class="percentage">@(BluePercent.ToString("F1"))%</div>
                            <div class="blobs-count">@Manager.BluePaintCount blobs</div>
                        </div>

                        <div class="vs-divider">
                            <div class="vs-circle">VS</div>
                        </div>

                        <div class="team-result red-result @(Manager.WinnerTeamId == 1 ? "winner" : "")">
                            <div class="team-name">RED</div>
                            <div class="percentage">@(RedPercent.ToString("F1"))%</div>
                            <div class="blobs-count">@Manager.RedPaintCount blobs</div>
                        </div>
                    </div>

                    @{
                        float totalPercent = BluePercent + RedPercent;
                        float blueRatio = totalPercent > 0 ? (BluePercent / totalPercent) * 100f : 50f;
                        float redRatio = totalPercent > 0 ? (RedPercent / totalPercent) * 100f : 50f;
                    }
                    <div class="comparison-bar">
                        <div class="comp-fill blue-fill" style="width: @(blueRatio)%;"></div>
                        <div class="comp-fill red-fill" style="width: @(redRatio)%;"></div>
                    </div>

                    <div class="restart-timer">Next round starting soon...</div>
                </div>
            </div>
        }

        <!-- ОВЕРЛЕЙ СМЕРТІ ЛОКАЛЬНОГО ГРАВЦЯ -->
        @if ( LocalPlayerTeam.IsValid() && LocalPlayerTeam.IsDead )
        {
            <div class="death-overlay">
                <div class="death-card">
                    <div class="splatted-text">YOU WERE SPLATTED!</div>
                    <div class="respawn-timer-text">RESPAWNING IN @(MathF.Ceiling(MathF.Max(0f, 5f - LocalPlayerTeam.TimeSinceDeath)))s</div>
                </div>
            </div>
        }
    }
</root>

@code {
    private string GetCssColor( Color c )
    {
        return $"rgba({(int)(c.r * 255)}, {(int)(c.g * 255)}, {(int)(c.b * 255)}, {c.a.ToString(System.Globalization.CultureInfo.InvariantCulture)})";
    }

    private string GetHexColor( Color c )
    {
        return $"#{(int)(c.r * 255):X2}{(int)(c.g * 255):X2}{(int)(c.b * 255):X2}";
    }

    /// <summary>
	/// Кількість плям слизу для 100% заповнення смуги прогресу
	/// </summary>
	[Property, Category( "Settings" )] 
    public int TargetPaintCapacity { get; set; } = 300;

    public GameManager Manager { get; private set; }
    public PlayerTeam LocalPlayerTeam { get; private set; }
    public PaintGun LocalPlayerGun { get; private set; }

    public bool ShowControls { get; set; }
    public Panel PortraitContainer { get; set; }

    protected override void OnStart()
    {
        // Не показуємо інтерфейс для віддалених гравців у мережевій грі
        if ( IsProxy )
        {
            Enabled = false;
            return;
        }

        // Знаходимо або створюємо GameManager
        FindOrCreateManager();
        FindLocalPlayer();
    }

    protected override void OnUpdate()
    {
        if ( !Manager.IsValid() )
        {
            FindOrCreateManager();
        }

        if ( !LocalPlayerTeam.IsValid() )
        {
            FindLocalPlayer();
        }
        else
        {
            if ( PortraitContainer != null )
            {
                var playerRoot = (LocalPlayerTeam.GameObject.Components.Get<PlayerController>() ?? LocalPlayerTeam.GameObject.Components.GetInAncestors<PlayerController>())?.GameObject ?? LocalPlayerTeam.GameObject;
                var portraitRenderer = playerRoot.Components.GetAll<PortraitRenderer>( FindMode.EverythingInSelfAndDescendants ).FirstOrDefault();
                
                if ( portraitRenderer != null && portraitRenderer.RenderedTexture != null )
                {
                    PortraitContainer.Style.SetBackgroundImage( portraitRenderer.RenderedTexture );
                    PortraitContainer.Style.Dirty();
                    PortraitContainer.StateHasChanged();
                }
            }
        }

        ShowControls = Input.Down( "Score" );
    }

    protected override void OnDestroy()
    {
    }

    private void FindOrCreateManager()
    {
        Manager = Scene.GetAllComponents<GameManager>().FirstOrDefault();
        
        if ( !Manager.IsValid() && Scene.IsValid() )
        {
            Log.Info( "HUD: GameManager not found in scene. Automatically creating fallback GameManager..." );
            var go = Scene.CreateObject();
            go.Name = "GameManager (Auto)";
            Manager = go.Components.Create<GameManager>();
        }
    }

    private void FindLocalPlayer()
    {
        var localPlayer = Scene.GetAllComponents<PlayerController>().FirstOrDefault( x => !x.IsProxy );
        if ( localPlayer.IsValid() )
        {
            LocalPlayerTeam = localPlayer.GameObject.Components.Get<PlayerTeam>() ?? localPlayer.GameObject.Components.GetInDescendants<PlayerTeam>();
            LocalPlayerGun = localPlayer.GameObject.Components.Get<PaintGun>() ?? localPlayer.GameObject.Components.GetInDescendants<PaintGun>();
        }
    }

    // Форматування таймера (наприклад, 120 -> "02:00")
    private string FormattedTime
    {
        get
        {
            if ( !Manager.IsValid() ) return "00:00";
            
            int totalSeconds = (int)MathF.Max( 0f, Manager.TimeRemaining );
            int minutes = totalSeconds / 60;
            int seconds = totalSeconds % 60;
            return $"{minutes:D2}:{seconds:D2}";
        }
    }

    // Розрахунок відсотка покриття для кожної з команд
    private float BluePercent
    {
        get
        {
            if ( !Manager.IsValid() ) return 0f;
            int capacity = Manager.TargetPaintCapacity > 0 ? Manager.TargetPaintCapacity : 300;
            return MathF.Min( 100f, ( Manager.BluePaintCount / (float)capacity ) * 100f );
        }
    }

    private float RedPercent
    {
        get
        {
            if ( !Manager.IsValid() ) return 0f;
            int capacity = Manager.TargetPaintCapacity > 0 ? Manager.TargetPaintCapacity : 300;
            return MathF.Min( 100f, ( Manager.RedPaintCount / (float)capacity ) * 100f );
        }
    }

    // Оновлення інтерфейсу тільки при зміні значень
    protected override int BuildHash()
    {
        if ( !Manager.IsValid() ) return 0;

        var localDead = LocalPlayerTeam.IsValid() && LocalPlayerTeam.IsDead;
        var localTimeLeft = LocalPlayerTeam.IsValid() ? (int)MathF.Ceiling(MathF.Max(0f, 5f - LocalPlayerTeam.TimeSinceDeath)) : 0;

        var hash = new System.HashCode();
        hash.Add( Manager.TimeRemaining );
        hash.Add( Manager.IsRoundActive );
        hash.Add( Manager.WinnerTeamId );
        hash.Add( Manager.BluePaintCount );
        hash.Add( Manager.RedPaintCount );
        hash.Add( localDead );
        hash.Add( localTimeLeft );
        hash.Add( LocalPlayerGun.IsValid() ? LocalPlayerGun.AmmoInMagazine : 0 );
        hash.Add( LocalPlayerGun.IsValid() ? LocalPlayerGun.AmmoReserve : 0 );
        hash.Add( LocalPlayerGun.IsValid() ? LocalPlayerGun.IsReloading : false );
        hash.Add( LocalPlayerTeam.IsValid() ? LocalPlayerTeam.Health : 0 );
        hash.Add( ShowControls );
        return hash.ToHashCode();
    }
}