package games.colonier.ld44.chemistry.rule;

import games.colonier.ld44.chemistry.Atom;
import games.colonier.ld44.chemistry.MoleculeRule;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public final class RuleTextifier {

    private RuleTextifier () {
        throw new UnsupportedOperationException ();
    }

    private static String textifyAtomType (Atom.Type t) {
        switch (t) {

            case Red:
                return "red";
            case Green:
                return "green";
            case Blue:
                return "blue";
            default:
                return "unknown";
        }
    }

    private static String textifyDecider (SimpleAtomicMolecularRule.Decider decider, int parameter) {
        StringBuilder sb = new StringBuilder ();
        if (decider == SimpleAtomicMolecularRule.AtLeastDecider) {
            sb.append (">= ")
                    .append (parameter).append (" ");
        } else if (decider == SimpleAtomicMolecularRule.AtMostDecider) {
            sb.append ("<= ")
                    .append (parameter).append (" ");
        } else if (decider == SimpleAtomicMolecularRule.EvenDecider) {
            sb.append ("% 2 = 0 ");
        } else if (decider == SimpleAtomicMolecularRule.OddDecider) {
            sb.append ("% 2 = 1 ");
        } else if (decider == SimpleAtomicMolecularRule.MoreThanDecider) {
            sb.append ("> ")
                    .append (parameter).append (" ");
        } else if (decider == SimpleAtomicMolecularRule.LessThanDecider) {
            sb.append ("< ")
                    .append (parameter).append (" ");
        } else {
            sb.append ("??? ");
        }
        return sb.toString ();
    }

    private static String textifyCounter (SimpleAtomicMolecularRule.Counter counter) {
        StringBuilder sb = new StringBuilder ();
        if (counter == SimpleAtomicMolecularRule.RedAtomCounter) {
            sb.append (textifyAtomType (Atom.Type.Red)).append (" atoms");
        } else if (counter == SimpleAtomicMolecularRule.GreenAtomCounter) {
            sb.append (textifyAtomType (Atom.Type.Green)).append (" atoms");
        } else if (counter == SimpleAtomicMolecularRule.BlueAtomCounter) {
            sb.append (textifyAtomType (Atom.Type.Blue)).append (" atoms");
        } else if (counter instanceof SimpleAtomicMolecularRule.ConnectionCounter) {
            SimpleAtomicMolecularRule.ConnectionCounter cc = (SimpleAtomicMolecularRule.ConnectionCounter) counter;
            sb.append (textifyAtomType (cc.t1))
                    .append ("-")
                    .append (textifyAtomType (cc.t2))
                    .append (" atomic connections");
        }
        return sb.toString ();
    }

    public static String textify (MoleculeRule rule) {

        StringBuilder sb = new StringBuilder ();

        if (rule instanceof SimpleAtomicMolecularRule) {

            SimpleAtomicMolecularRule r = (SimpleAtomicMolecularRule) rule;

            if (r.counter instanceof SimpleAtomicMolecularRule.SumCounter) {
                SimpleAtomicMolecularRule.SumCounter sc = (SimpleAtomicMolecularRule.SumCounter) r.counter;
                sb.append (textifyCounter (sc.c1))
                        .append (" + ")
                        .append (textifyCounter (sc.c2))
                        .append (textifyDecider (r.decider, r.parameter));
            } else if (r.counter instanceof SimpleAtomicMolecularRule.DifferenceCounter) {
                SimpleAtomicMolecularRule.DifferenceCounter dc = (SimpleAtomicMolecularRule.DifferenceCounter) r.counter;
                sb.append ("abs (").append (textifyCounter (dc.c1))
                        .append (" - ")
                        .append (textifyCounter (dc.c2))
                        .append (") ")
                        .append (textifyDecider (r.decider, r.parameter));
            } else {

                sb.append (textifyCounter (r.counter)).append (" ").append (textifyDecider (r.decider, r.parameter));

            }

        } else if (rule instanceof StatisticalMoleculeRule) {

            StatisticalMoleculeRule r = (StatisticalMoleculeRule) rule;

            if (r.red > 0 || r.green > 0 || r.blue > 0
                    || r.redRed > 0 || r.redGreen > 0 || r.redBlue > 0
                    || r.greenGreen > 0 || r.greenBlue > 0
                    || r.blueBlue > 0) {

                sb.append ("Have ");
                List<String> parts = new ArrayList<> ();

                if (r.red > 0) {
                    parts.add (r.red + " red atoms");
                }
                if (r.green > 0) {
                    parts.add (r.green + " green atoms");
                }
                if (r.blue > 0) {
                    parts.add (r.blue + " blue atoms");
                }
                if (r.redRed > 0) {
                    parts.add (r.redRed + " red-red atomic connection");
                }
                if (r.redGreen > 0) {
                    parts.add (r.redGreen + " red-green atomic connection");
                }
                if (r.redBlue > 0) {
                    parts.add (r.redBlue + " red-blue atomic connection");
                }
                if (r.greenGreen > 0) {
                    parts.add (r.greenGreen + " green-green atomic connection");
                }
                if (r.greenBlue > 0) {
                    parts.add (r.greenBlue + " green-blue atomic connection");
                }
                if (r.blueBlue > 0) {
                    parts.add (r.blueBlue + " blue-blue atomic connection");
                }

                sb.append (parts.stream ().collect (Collectors.joining (", ")));

            }

        } else if (rule instanceof CompoundMolecularRule) {

            CompoundMolecularRule r = (CompoundMolecularRule) rule;

            String delimiter = r.operator == CompoundMolecularRule.Operator.And ? " and " :
                    r.operator == CompoundMolecularRule.Operator.Not ? " not "
                            : " or ";

            sb.append (
                    r.rules
                            .stream ()
                            .map (RuleTextifier::textify)
                            .map (s -> s.isEmpty () ? s : s.substring (0, 1).toLowerCase () + s.substring (1))
                            .collect (Collectors.joining (delimiter))
            );

        }

        String str = sb.toString ();
        if (str.isEmpty ()) {
            return str;
        }

        return str.substring (0, 1).toUpperCase () + str.substring (1);

    }

}
