using System.Diagnostics;
using System.Text.Json;
using AntColony.Algorithm;

namespace AntColony;

class Program
{
    /// <summary></summary>
    /// <param name="file">Путь до файла с ребрами</param>
    /// <param name="json">Файл представлен в формате JSON вместо TSV</param>
    /// <param name="hasHeader">Файл содержит заголовок</param>
    /// <param name="iterations">Количество итераций</param>
    /// <param name="ants">Количество муравьев</param>
    /// <param name="alpha">Значение α (определяет вес феромонов)</param>
    /// <param name="beta">Значение β (определяет вес расстояния)</param>
    /// <param name="evaporationRate">Скорость испарения феромонов</param>
    /// <param name="q">Значение Q</param>
    public static void Main(FileInfo file, bool json = false, bool hasHeader = false, int iterations = 100, int ants = 20, double alpha = 1, double beta = 2, double evaporationRate = 0.1, double q = 100)
    {
        Console.Write("Чтение файла...");

        var sw = new Stopwatch();
        sw.Start();

        double[][] distances = json ? ParseEdgeJsonAsync(file.FullName).Result! : ParseEdgeFile(file.FullName, hasHeader);

        Console.WriteLine($"\rСчитано {distances.Length} вершин за {sw.Elapsed} ({sw.ElapsedMilliseconds} мс).\n");

        var aco = new AntColonyOptimizer(
            distances: distances,
            evaporationRate: evaporationRate
        ).AddAnts<DefaultAnt>(1500).ShuffleAnts();

        Console.CancelKeyPress += (e, f) =>
        {
            var res = aco.EarlyExit();
            Console.WriteLine($"\nРанняя остановка.\nЛучший путь: {string.Join(" -> ", res.tour)}\nДлина: {res.distance}");
        };

        sw.Restart();

        var (bestTour, bestDistance) = aco.Solve(iterations);

        Console.WriteLine($"Задача решена за {sw.Elapsed} ({sw.ElapsedMilliseconds} мс).");

        if (bestTour.Count == 0)
        {
            Console.WriteLine("Гамильтонов цикл не найден.");
            return;
        }

        Console.WriteLine($"Лучший путь: {string.Join(" -> ", bestTour)}.");
        Console.WriteLine($"Итоговое расстояние: {bestDistance}.");
    }

    private static async Task<double[][]?> ParseEdgeJsonAsync(string filePath)
    {
        using var s = File.OpenRead(filePath);
        return await JsonSerializer.DeserializeAsync<double[][]>(s);
    }

    private static double[][] ParseEdgeFile(string filePath, bool skipHeader)
    {
        List<string> lines = [.. File.ReadAllLines(filePath)];
        if (skipHeader) lines.RemoveAt(0);

        HashSet<int> vertices = [];
        int maxVertex = 0;

        foreach (string line in lines)
        {
            string[] parts = line.Split();
            if (parts.Length != 3)
                continue;

            if (int.TryParse(parts[0], out int v1))
                vertices.Add(v1);
            if (int.TryParse(parts[1], out int v2))
                vertices.Add(v2);

            maxVertex = int.Max(maxVertex, v1);
            maxVertex = int.Max(maxVertex, v2);
        }

        double[][] adjacencyMatrix = new double[maxVertex + 1][];

        for (int i = 0; i < maxVertex + 1; i++)
        {
            adjacencyMatrix[i] = new double[maxVertex + 1];
            for (int j = 0; j < maxVertex + 1; j++)
            {
                adjacencyMatrix[i][j] = 0.0;
            }
        }

        foreach (string line in lines)
        {
            string[] parts = line.Split();
            if (parts.Length != 3)
                continue;

            if (int.TryParse(parts[0], out int v1) &&
                int.TryParse(parts[1], out int v2) &&
                double.TryParse(parts[2], out double distance))
            {
                adjacencyMatrix[v1][v2] = distance;
                adjacencyMatrix[v2][v1] = distance;
            }
        }

        return adjacencyMatrix;
    }
}