//using Czwórka = System.ValueTuple<char, char, char, char>;

namespace Turing.Model
{ 
    public struct Czwórka
    {
        public char PoczątkowyStanGłowicy, KońcowyStanGłowicy;
        public char PoczątkowyStanTaśmy, KońcowyStanTaśmy;

        public static Czwórka Parsuj(string instrukcja)
        {
            if (instrukcja.Length != 4) throw new Exception("Nieodpowiednia długiść instrukcji");
            Czwórka czwórka = new Czwórka();
            czwórka.PoczątkowyStanGłowicy = instrukcja[0];
            czwórka.PoczątkowyStanTaśmy = instrukcja[1];
            czwórka.KońcowyStanTaśmy = instrukcja[2];
            czwórka.KońcowyStanGłowicy = instrukcja[3];
            if (!czwórka.CzyPoprawna) throw new Exception("Niepoprawna instrukcja " + czwórka.ToString());
            return czwórka;
        }

        private static bool czyInstrukcjaPoprawna(Czwórka czwórka)
        {
            //mała, duża, duża, mała (tylko litery)
            if (!char.IsLower(czwórka.PoczątkowyStanGłowicy)) return false;
            if (!char.IsUpper(czwórka.PoczątkowyStanTaśmy)) return false;
            if (!char.IsUpper(czwórka.KońcowyStanTaśmy)) return false;
            if (!char.IsLower(czwórka.KońcowyStanGłowicy)) return false;
            return true;
        }

        public bool CzyPoprawna { get { return czyInstrukcjaPoprawna(this); } }

        public override string ToString()
        {
            return $"({PoczątkowyStanGłowicy}, {PoczątkowyStanTaśmy}, {KońcowyStanTaśmy}, {KońcowyStanGłowicy})";
        }
    }

    internal class Program
    {
        //TODO: nie chcę umożliwiać zmiany programu z zewnątrz
        public Czwórka[] Czwórki { get; private set; } //auto-implemented property        

        private bool czyProgramJestJednoznaczny(Czwórka[] instrukcje)
        {
            //czy nie ma dwóch instrukcji zaczynających się od tych samych dwóch znaków
            for(int i = 0; i < instrukcje.Length; ++i)
            {
                for(int j = i + 1; j < instrukcje.Length; ++j)
                {
                    Czwórka i1 = instrukcje[i];
                    Czwórka i2 = instrukcje[j];
                    if (i1.PoczątkowyStanGłowicy == i2.PoczątkowyStanGłowicy && i1.PoczątkowyStanTaśmy == i2.PoczątkowyStanTaśmy)
                        return false;
                }
            }
            return true;
        }

        private Czwórka[] parsujProgram(string[] instrukcje)
        {
            Czwórka[] czwórki = new Czwórka[instrukcje.Length];
            for(int i = 0; i < czwórki.Length; i++) 
            {
                //czwórki[i] = parsujInstrukcję(instrukcje[i]);
                czwórki[i] = Czwórka.Parsuj(instrukcje[i]);
            }
            return czwórki;
        }

        public Program(string[] instrukcje)
        {
            //TODO: sprawdzenie poprawności programu i poprawności instrukcji
            Czwórki = parsujProgram(instrukcje);
            if (!czyProgramJestJednoznaczny(Czwórki)) throw new Exception("Program nie jest jednoznaczny");
        }

        //pseudokonstruktor
        public static Program Load(string ścieżkaPliku)
        {
            string[] instrukcje = File.ReadAllLines(ścieżkaPliku);
            Program program = new Program(instrukcje);
            return program;
        }

        public Czwórka? ZnajdźInstrukcjęDlaStanu(char stanGłowicy, char wartośćWKomórceTaśmy)
        {
            try
            {
                return Czwórki.First(c => c.PoczątkowyStanGłowicy == stanGłowicy && c.PoczątkowyStanTaśmy == wartośćWKomórceTaśmy);
            }
            catch(InvalidOperationException)
            {
                return null;
            }
        }
    }
}
