namespace omp
{
    class UndergroundException : Exception;

    public interface IConfigurator
    {
        Type PointType { get; }

        public int XSize { get; }
        public int YSize { get; }
        public int ZCoordinate { get; }
        public List<Point> Stations { get; }
    }

    public class Configurator : IConfigurator
    {
        public Type PointType { get; }

        public int XSize { get; }
        public int YSize { get; }
        public int ZCoordinate { get; }
        public List<Point> Stations { get; } = [];

        public Configurator(string[] args)
        {
            List<string> lines = ReadAllLines(args);

            bool hasZ = CheckHasZ(lines[1]);

            if (hasZ)
            {
                PointType = typeof(Point3D);
            }
            else
            {
                PointType = typeof(Point2D);
            }

            (XSize, YSize) = ParseSize(lines[0]);
            ZCoordinate = ParseZCoord(lines[1], hasZ);
            ParseStations(lines, hasZ);
        }

        private static List<string> ReadAllLines(string[] args)
        {
            List<string> lines;

            if (args.Length == 0)
            {
                Console.WriteLine("Input parameters:");

                lines = [];

                string? line;
                while ((line = Console.ReadLine()) != null && line != "")
                {
                    lines.Add(line);
                }
            }
            else if (args.Length == 1)
            {
                lines = new(File.ReadAllLines(args[0]));
            }
            else
            {
                throw new ArgumentException("You must either provide path to parameters file as an only cli argument, or insert it into STDIN");
            }

            return lines;
        }

        private static bool CheckHasZ(string line)
        {
            return line.Split(' ').Length == 1; // line contains a single number, z coordinate
        }

        private static (int, int) ParseSize(string line)
        {
            string[] size_parts = line.Split(' ', 2);
            int xSize = int.Parse(size_parts[0]);
            int ySize = int.Parse(size_parts[1]);

            return (xSize, ySize);
        }

        private int ParseZCoord(string line, bool hasZ)
        {
            int zCoordinate;
            if (hasZ)
            {
                zCoordinate = int.Parse(line);
                if (ZCoordinate < 0)
                {
                    throw new UndergroundException();
                }
            }
            else
            {
                zCoordinate = -1;
            }

            return zCoordinate;
        }

        private void ParseStations(IReadOnlyList<string> lines, bool hasZ)
        {
            if (hasZ) Parse3DStations(lines);
            else Parse2DStations(lines);
        }

        private void Parse2DStations(IReadOnlyList<string> lines)
        {
            for (int i = 1; i < lines.Count; ++i)
            {
                string[] point_parts = lines[i].Split(' ', 2);

                int x = int.Parse(point_parts[0]);
                int y = int.Parse(point_parts[1]);

                Stations.Add(new Point2D(x, y));
            }
        }

        private void Parse3DStations(IReadOnlyList<string> lines)
        {
            for (int i = 2; i < lines.Count; ++i)
            {
                string[] point_parts = lines[i].Split(' ', 3);

                int x = int.Parse(point_parts[0]);
                int y = int.Parse(point_parts[1]);
                int z = int.Parse(point_parts[2]);

                Stations.Add(new Point3D(x, y, z));
            }
        }
    }
}
