using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Turing.Model
{
    //TODO: własny typ wyjątku i dodać do testów

    public class StanMaszyny
    {
        private string taśma;
        private char stanGłowicy;
        private int indeksGłowicy; //położenie głowicy na taśmie

        public StanMaszyny(string zawartość)
        {
            if (string.IsNullOrEmpty(zawartość)) throw new Exception("Taśma nie ma zawartości");
            if (!sprawdźTaśmę(zawartość)) throw new Exception("Taśma niepoprawna");
            taśma = wyodrębnijTaśmę(zawartość, out stanGłowicy, out indeksGłowicy);
        }

        private static bool sprawdźTaśmę(string taśma)
        {
            bool czyWszystkieZnakiSąLiterami = taśma.All(c => char.IsLetter(c));
            int liczbaMałychLiter = taśma.Count(c => char.IsLower(c));
            int liczbaDużychLiter = taśma.Count(c => char.IsUpper(c));
            bool złeLitery = taśma.Any(c => c == 'L' || c == 'R');
            bool czyTaśmaPoprawna = czyWszystkieZnakiSąLiterami && liczbaMałychLiter == 1 && liczbaMałychLiter + liczbaDużychLiter == taśma.Length && !złeLitery;
            return czyTaśmaPoprawna;
        }

        private static string wyodrębnijTaśmę(string zawartość, out char stanGłowicy, out int indeksGłowicy)
        {
            //int indeksStanuGłowicy = zawartość.First(c => char.IsLower(c));
            stanGłowicy = zawartość.FirstOrDefault(c => c == 's');
            indeksGłowicy = zawartość.IndexOf(stanGłowicy);
            string taśma = zawartość.Remove(indeksGłowicy, 1);            
            return taśma;
        }

        public static StanMaszyny Load(string ścieżkaPliku)
        {
            string zawartość = File.ReadAllText(ścieżkaPliku);
            StanMaszyny stanMaszyny = new StanMaszyny(zawartość);
            return stanMaszyny;
        }

        public override string ToString()
        {
            return taśma;
        }

        public char this[int indeks] //indekser
        {
            get
            {
                return taśma[indeks];
            }
        }

        public char StanGłowicy
        {
            get
            {
                return stanGłowicy;
            }
        }  
        
        public int IndeksGłowicy
        {
            get
            {
                return indeksGłowicy;
            }
        }

        public char BieżącaWartośćWKomórce
        {
            get
            {
                return taśma[indeksGłowicy];
            }
            private set
            {
                //TODO: zmiana na StringBuilder
                taśma = taśma.Remove(indeksGłowicy, 1).Insert(indeksGłowicy, value.ToString());
            }
        }

        public void WykonajInstrukcję((char, char, char, char) czwórka)
        {
            if (czwórka.Item1 != stanGłowicy || czwórka.Item2 != BieżącaWartośćWKomórce) throw new Exception("Instrukcja nie pasuje do stanu maszyny Turinga");
            else
            {
                stanGłowicy = czwórka.Item4;
                if (czwórka.Item3 == 'L')
                {
                    indeksGłowicy--;
                    if (indeksGłowicy < 0) throw new Exception("Wyjście poza taśmę z lewej strony");
                }
                else if(czwórka.Item3 == 'R')
                {
                    indeksGłowicy++;
                    if (indeksGłowicy < 0) throw new Exception("Wyjście poza taśmę z prawej strony");
                }
                else BieżącaWartośćWKomórce = czwórka.Item3;
            }
        }
    }
}
