using System;
using System.Collections.Generic;
using UnityEngine;

public class HomeGraph
{
    public HashSet<Arc> arcs;

    public HomeGraph(ModuleModel[,] homeMatrix)
    {
        this.arcs = new HashSet<Arc> ();
        int homeLength = homeMatrix.GetLength(0);
        int homeHeight = homeMatrix.GetLength(1);
        for(int i = 0; i< homeLength; i++)
        {
            for(int j = 0; j < homeHeight; j++)
            {
                ModuleModel m1 = homeMatrix[i, j];
                //look right
                if( i + 1 < homeLength && homeMatrix[i + 1, j]!=null && homeMatrix[i + 1, j] != m1)
                {
                    arcs.Add(new Arc(m1, homeMatrix[i + 1, j], i, j, i + 1, j));
                }
                //look up
                if ( j + 1 < homeHeight && homeMatrix[i, j + 1] != null && homeMatrix[i, j + 1] != m1)
                {
                    arcs.Add(new Arc(m1, homeMatrix[i, j + 1], i, j, i, j + 1));
                }
            }
        }
        this.pruneArcs(this.arcs);
    }


    /*
     * WARNING: this checks the exact direction!
     */
    public bool containsLinkBetweenWithDirection(Couple<int, int> p1, Couple<int, int> p2)
    {
        foreach (Arc a in this.arcs)
        {
            Couple<int, int> arcP1 = a.arc.b;
            Couple<int, int> arcP2 = a.arc.c;
            if (arcP1.a == p1.a && //match arc.x1 p1.x1
                arcP1.b == p1.b && //match y1
                arcP2.a == p2.a && //match x2
                arcP2.b == p2.b) //match x2
                return true;
        }
        return false;
    }

    public bool containsLinkBetween(Couple<int, int> p1, Couple<int, int> p2)
    {
        return containsLinkBetweenWithDirection(p1, p2) || containsLinkBetweenWithDirection(p2, p1);
    }

    private void pruneArcs(HashSet<Arc> arcs)
    {
        HashSet<ModuleModel> homeModules = new HashSet<ModuleModel>();
        ModuleModel randomModule = null;
        foreach(Arc arc in arcs)
        {
            homeModules.Add(arc.arc.a.a);
            homeModules.Add(arc.arc.a.b);
            randomModule = arc.arc.a.a;
        }
        if (randomModule == null) throw new Exception("nothing to prune or corrupted arcs!");
        Arc removedArc;
        do
        {
            Arc[] arcArray = new Arc[arcs.Count];
            arcs.CopyTo(arcArray);
            removedArc = arcArray[UnityEngine.Random.Range(0, arcs.Count)];
            arcs.Remove(removedArc);
        } while (homeModules.Count == this.getReachables(randomModule, new HashSet<Arc>(arcs)).Count);
        arcs.Add(removedArc);
    }


    /*
     * WARNING: will return a "damaged" arcs variable...
     */
    private HashSet<ModuleModel> getReachables(ModuleModel start, HashSet<Arc> arcs)
    {
        HashSet<ModuleModel> reachedModules = new HashSet<ModuleModel>();
        reachedModules.Add(start);
        Arc arc = removeArcWithFromArcSet(start,arcs);
        while(arc != null)
        {
            ModuleModel reachedModule = arc.getModuleLinkedTo(start);
            reachedModules.UnionWith(this.getReachables(reachedModule, arcs));
            arc = removeArcWithFromArcSet(start, arcs);
        }
        return reachedModules;
    }

    private Arc removeArcWithFromArcSet(ModuleModel start, HashSet<Arc> arcs)
    {
        foreach(Arc arc in arcs)
        {
            if (arc.arc.a.a == start || arc.arc.a.b == start)
            {
                arcs.Remove(arc);
                return arc;
            }
        }
        return null;
    }
}