using System;
using System.Collections.Generic;
using System.Threading;

namespace MultiThreadedApp.AppLogic;


class Game
{
    // Különböző vonalak x pozíciója (start, depo, finish)
    public const int StartLinePosition = 150;
    public const int DepoPosition = 300;
    public const int FinishLinePosition = 450;

    private ManualResetEvent startEvent = new ManualResetEvent(false);

    public List<Bike> Bikes { get; } = new List<Bike>();

    private List<int> stepLog = new List<int>();
    private readonly object logLock = new object();

    /// <summary>
    /// Verseny előkészítése (biciklik létrehozása és felsorakoztatása
    /// a startvonalhoz)
    /// </summary>
    public void PrepareRace()
    {
        CreateBike();
        CreateBike();
        CreateBike();
    }

    /// <summary>
    /// Elindítja a bicikliket a startvonalról.
    /// </summary>
    public void StartBikes()
    {

        startEvent.Set();
    }

    /// <summary>
    /// Elindítja a következő biciklit a depóból (mindig csak egyet)
    /// </summary>
    public void StartNextBikeFromDepo()
    {
        depoEvent.Set();
    }

    /// <summary>
    /// Létrehoz egy biciklit.
    /// </summary>
    private void CreateBike()
    {
        // Az új bicikli a következő rajtszámot kapja paraméterben (az első bicikli a 0 rajtszámot kapja)
        var bike = new Bike(Bikes.Count);
        Bikes.Add(bike);

        //var bikeThread = new Thread(() => BikeThreadFunction(bike));
        var thread = new Thread(BikeThreadFunction);
        thread.IsBackground = true; 
        thread.Start(bike);
    }

    private readonly object winnerLock = new object();
    private bool hasWinner = false;

    public Action<Bike> BikeUpdated;

    private AutoResetEvent depoEvent = new AutoResetEvent(false);
    /// <summary>
    /// Bicikli szálának függvénye, ami a bicikli lép
    /// </summary>
    void BikeThreadFunction(object bikeAsObject)
    {
        Bike bike = (Bike)bikeAsObject;
        while (bike.Position <= StartLinePosition)
        {
            int step = bike.Step();
            lock (logLock)
            {
                stepLog.Add(step);
                BikeUpdated?.Invoke(bike);
            }

            Thread.Sleep(100);
        }

        startEvent.WaitOne();

        while (bike.Position < FinishLinePosition)
        {
            if (bike.Position >= DepoPosition && bike.IsInDepo)
            {
                depoEvent.WaitOne();
                bike.StartFromDepo();
                BikeUpdated?.Invoke(bike);
            }

            int step = bike.Step();
            lock (logLock)
            {
                stepLog.Add(step);
                BikeUpdated?.Invoke(bike);
            }

            Thread.Sleep(100);
        }

        lock (winnerLock)
        {
            if (!hasWinner)
            {
                //Thread.Sleep(2000);
                hasWinner = true;
                bike.SetAsWinner();
                BikeUpdated?.Invoke(bike);
            }
        }
    }

}
