package games.colonier.ld44.chemistry;

import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.Rectangle;

import java.io.Serializable;
import java.util.*;

public class Molecule implements Serializable {

    public final int id;
    public final List<Atom> atoms = new ArrayList<> ();
    public final Rectangle boundingBox = new Rectangle ();

    public Molecule (int id) {
        this.id = id;
    }

    public Molecule atom (Atom atom) {
        atoms.add (atom);
        return this;
    }

    public int atomCount (Atom.Type t) {
        return (int) atoms.stream ().filter (a -> a.type == t).count ();
    }

    public int connectionCount (Atom.Type tA, Atom.Type tB) {

        Set<Connection> visited = new HashSet<> ();

        for (Atom a : atoms) {
            if (a.type == tA) {
                for (Atom b : a.connectedTo) {
                    if (b.type == tB) {
                        Connection c = new Connection (a.id, b.id);
                        visited.add (c);
                    }
                }
            }
        }

        return visited.size ();

    }

    public List<Symptom> getSymptoms () {

        List<Symptom> list = new ArrayList<> ();
        for (Symptom s : Symptom.values ()) {

            if (s.rule.appliesTo (this)) {
                list.add (s);
            }

        }
        return list;

    }

    public boolean isConnected () {

        Deque<Atom> bfsQueue = new ArrayDeque<> ();
        Set<Atom> bfsVisited = new HashSet<> ();

        if (atoms.isEmpty ()) {
            return true;
        }

        bfsQueue.addFirst (atoms.get (0));
        bfsVisited.add (atoms.get (0));

        while (!bfsQueue.isEmpty ()) {

            Atom current = bfsQueue.removeFirst ();

            for (Atom n : current.connectedTo) {
                if (bfsVisited.add (n)) {
                    bfsQueue.addLast (n);
                }
            }

        }

        return bfsVisited.size () == atoms.size ();

    }

    public void arrangeAtoms () {

        System.out.println ("Molecule.arrangeAtoms () : BEGIN");

        Random random = new Random ();

        while (true) {

            float w = atoms.size () * 24f;
            float h = atoms.size () * 24f;

            for (Atom a : atoms) {

                a.drawPosition (
                        random.nextFloat () * w - w / 2f,
                        random.nextFloat () * h - h / 2f
                );

            }

            boolean ok = true;
            all:
            for (Atom a1 : atoms) {
                for (Atom a2 : atoms) {
                    if (a1.equals (a2)) {
                        continue;
                    }
                    if (a1.drawPosition.dst (a2.drawPosition) <= 24f) {
                        ok = false;
                        break all;
                    }
                    if (a1.connectedTo.contains (a2)) {
                        for (Atom a3 : atoms) {
                            if (a1 == a3 || a2 == a3) continue;
                            if (Intersector.intersectSegmentCircle (a1.drawPosition, a2.drawPosition, a3.drawPosition, 144f)) {
                                ok = false;
                                break all;
                            }
                        }
                    }
                }
            }

            if (ok) {
                System.out.println ("Molecule.arrangeAtoms () : BREAK WHILE (TRUE)");
                break;
            }

            System.out.println ("Molecule.arrangeAtoms () : END WHILE (TRUE)");

        }

        boundingBox.setX ((float) atoms.stream ().mapToDouble (a -> a.drawPosition.x).min ().getAsDouble ());
        boundingBox.setY ((float) atoms.stream ().mapToDouble (a -> a.drawPosition.y).min ().getAsDouble ());

        boundingBox.setWidth (
                Math.abs (
                        boundingBox.x - (float) atoms.stream ()
                                .mapToDouble (a -> a.drawPosition.x)
                                .max ()
                                .getAsDouble ()
                )
        );

        boundingBox.setHeight (
                Math.abs (
                        boundingBox.y - (float) atoms.stream ()
                                .mapToDouble (a -> a.drawPosition.y)
                                .max ()
                                .getAsDouble ()
                )
        );

    }

    public Molecule copy () {

        Molecule cp = new Molecule (id);
        for (Atom a : atoms) {
            cp.atom (new Atom (a.id, a.type));
        }

        for (Atom a1 : atoms) {
            for (Atom a2 : atoms) {
                if (a1.isConnected (a2)) {
                    cp.atoms.stream ().filter (a -> a.id == a1.id)
                            .findFirst ()
                            .get ()
                            .connect (
                                    cp.atoms.stream ().filter (a -> a.id == a2.id).findFirst ().get ()
                            );
                }
            }
        }

        System.out.println ("Molecule.copy ()");

        return cp;

    }

}
