import codedraw.CodeDraw;

import java.awt.*;
import java.io.IOException;

/**
 * Simulates the solar system.
 */
public class Simulation8 {

    // gravitational constant
    public static final double G = 6.6743e-11;

    // one astronomical unit (AU) is the average distance of earth to the sun.
    public static final double AU = 150e9; // meters

    // set some system parameters
    public static final double SECTION_SIZE = 10 * AU; // the size of the square region in space

    // all quantities are based on units of kilogram respectively second and meter.

    // The main simulation method using instances of other classes.
    public static void main(String[] args) {
        if (args.length != 2) {
            System.err.println("Error: wrong number of arguments.");
            System.exit(1);
        }

        String statePath = args[0];
        String date;
        try {
            date = ReadDataUtil.convertDate(args[1]);
        } catch (IllegalArgumentException e) {
            System.err.println("Error: State has wrong format (requires YYYY-MMM-DD), aborting.");
            System.exit(2);
            return;
        }

        // simulation
        CodeDraw cd = new CodeDraw();

        // create solar system with 13 bodies
        MassiveForceTreeMap forceOnBody = new MassiveForceTreeMap();

        forceOnBody.put(new NamedBody("Oumuamua", 8e6), new Vector3());
        forceOnBody.put(new NamedBody("Earth", 5.972E24), new Vector3());
        forceOnBody.put(new NamedBody("Moon", 7.349E22), new Vector3());
        forceOnBody.put(new NamedBody("Mars", 6.41712E23), new Vector3());
        forceOnBody.put(new NamedBody("Deimos", 1.8E20), new Vector3());
        forceOnBody.put(new NamedBody("Phobos", 1.08E20), new Vector3());
        forceOnBody.put(new NamedBody("Mercury", 3.301E23), new Vector3());
        forceOnBody.put(new NamedBody("Venus", 4.86747E24), new Vector3());
        forceOnBody.put(new NamedBody("Vesta", 2.5908E20), new Vector3());
        forceOnBody.put(new NamedBody("Pallas", 2.14E20), new Vector3());
        forceOnBody.put(new NamedBody("Hygiea", 8.32E19), new Vector3());
        forceOnBody.put(new NamedBody("Ceres", 9.394E20), new Vector3());

        MassiveIterator iter = forceOnBody.getKeys().iterator();
        while (iter.hasNext()) {
            Massive a = iter.next();
            if (a instanceof NamedBody b) {
                boolean remove = false;

                try {
                    boolean found = ReadDataUtil.readConfiguration(b, statePath + "/" + b.getName() + ".txt", date);
                    if (!found) {
                        System.err.println("Warning: State not available for " + b.getName() + ".");
                        remove = true;
                    }
                } catch (IOException e) {
                    if (e instanceof StateFileNotFoundException notFound) {
                        System.err.println("Warning: " + notFound.getMessage());
                    } else if (e instanceof StateFileFormatException format) {
                        System.err.println("Warning: " + format.getMessage());
                    } else {
                        System.err.println("Error: " + e.getMessage());
                        System.exit(3);
                    }
                    remove = true;
                }

                if (remove) {
                    System.err.println("Running simulation without " + b.getName());
                    iter.remove();
                }
            }
        }

        // add sun after states have been read from files.
        forceOnBody.put(new NamedBody("Sun", 1.989E30), new Vector3());

        System.out.println("Starting simulation");
        long seconds = 0;
        while (true) {
            seconds++;

            for (Massive b1 : forceOnBody.getKeys()) {
                Vector3 force = new Vector3();
                for (Massive b2 : forceOnBody.getKeys()) {
                    if (b1 != b2) {
                        force = force.plus(b1.gravitationalForce(b2));
                    }
                }
                forceOnBody.put(b1, force);
            }

            for (Massive body : forceOnBody.getKeys()) {
                body.move(forceOnBody.get(body));
            }

            if ((seconds % 3600) == 0) {
                cd.clear(Color.BLACK);
                for (Massive body : forceOnBody.getKeys()) {
                    body.draw(cd);
                }
                cd.show();
            }
        }
    }
}
