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

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

namespace Turing.Model
{    
    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 static bool czyInstrukcjaPoprawna(Czwórka czwórka)
        {
            //mała, duża, duża, mała (tylko litery)
            if (!char.IsLower(czwórka.Item1)) return false;
            if (!char.IsUpper(czwórka.Item2)) return false;
            if (!char.IsUpper(czwórka.Item3)) return false;
            if (!char.IsLower(czwórka.Item4)) return false;
            return true;
        }

        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.Item1 == i2.Item1 && i1.Item2 == i2.Item2)
                        return false;
                }
            }
            return true;
        }

        private Czwórka parsujInstrukcję(string instrukcja)
        {
            if (instrukcja.Length != 4) throw new Exception("Nieodpowiednia długiść instrukcji");
            Czwórka czwórka = new Czwórka();
            czwórka.Item1 = instrukcja[0];
            czwórka.Item2 = instrukcja[1];
            czwórka.Item3 = instrukcja[2];
            czwórka.Item4 = instrukcja[3];
            if (!czyInstrukcjaPoprawna(czwórka)) throw new Exception("Niepoprawna instrukcja " + czwórka.ToString());
            return czwórka;
        }

        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]);
            }
            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.Item1 == stanGłowicy && c.Item2 == wartośćWKomórceTaśmy);
            }
            catch(InvalidOperationException exc)
            {
                return null;
            }
        }
    }
}
