namespace Turing.Model
{
    public class TuringException : Exception
    {
        public TuringException(string message, Exception innerException)
            : base(message, innerException)
        {
        }
    }

    internal class Turing
    {
        public StanMaszyny StanMaszyny { get; private set; } = null;
        public Program Program { get; private set; } = null;

        public Turing(string ścieżkaPlikuMaszyny = null, string ścieżkaPlikuProgramu = null)
        {
            try
            {
                StanMaszyny = StanMaszyny.Load(ścieżkaPlikuMaszyny);                
            }
            catch (Exception exc)
            {
                throw new TuringException("Błąd przy próbie wczytania taśmy: " + exc.Message, exc);
            }

            try
            {
                Program = Program.Load(ścieżkaPlikuProgramu);                
            }
            catch (Exception exc)
            {
                throw new TuringException("Błąd przy próbie wczytania programu: " + exc.Message, exc);
            }
        }

        public void Uruchom()
        {
            //InstrukcjaWykonana += (object sender, DaneInstrukcjiEventArgs e) => Console.Write("*");

            Czwórka? pasującaCzwórka = Program.ZnajdźInstrukcjęDlaStanu(StanMaszyny.StanGłowicy, StanMaszyny.BieżącaWartośćWKomórce);
            while (pasującaCzwórka.HasValue)
            {
                //Console.WriteLine(pasującaCzwórka.Value);
                StanMaszyny.WykonajInstrukcję(pasującaCzwórka.Value);
                gdyInstrukcjaWykonana(pasującaCzwórka.Value);
                pasującaCzwórka = Program.ZnajdźInstrukcjęDlaStanu(StanMaszyny.StanGłowicy, StanMaszyny.BieżącaWartośćWKomórce);
            }            
        }

        //EventHandler<>

        public class DaneInstrukcjiEventArgs : EventArgs
        {
            public Czwórka WykonywanaInstrukcja;
        }

        public delegate void _InstrukcjaWykonana(object? sender, DaneInstrukcjiEventArgs e);

        public event _InstrukcjaWykonana InstrukcjaWykonana;

        private void gdyInstrukcjaWykonana(Czwórka wykonywanaInstrukcja)
        {
            if (InstrukcjaWykonana != null) InstrukcjaWykonana(this, new DaneInstrukcjiEventArgs() { WykonywanaInstrukcja = wykonywanaInstrukcja });
        }
    }
}
