package mock.model;

import network.Messages.*;
import network.Messages.Enums.BoatLocationDeviceEnum;
import network.Utils.AC35UnitConverter;
import shared.model.Bearing;
import shared.model.CompoundMark;
import shared.model.Constants;
import shared.model.Mark;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by connortaylorbrown on 2/08/17.
 */
public class RaceServer {
    private MockRace race;
    private LatestMessages latestMessages;


    /**
     * The sequence number of the latest BoatLocation message sent or received.
     */
    private int boatLocationSequenceNumber = 1;


    public RaceServer(MockRace race, LatestMessages latestMessages) {
        this.race = race;
        this.latestMessages = latestMessages;
    }


    /**
     * Parses the race to create a snapshot, and places it in latestMessages.
     */
    public void parseSnapshot() {

        List<AC35Data> snapshotMessages = new ArrayList<>();

        //Parse the boat locations.
        snapshotMessages.addAll(parseBoatLocations());

        //Parse the marks.
        snapshotMessages.addAll(parseMarks());

        //Parse the race status.
        snapshotMessages.add(parseRaceStatus());

        latestMessages.setSnapshot(snapshotMessages);
    }


    /**
     * Parses an individual marker boat, and returns it.
     * @param mark The marker boat to parse.
     * @return The BoatLocation message.
     */
    private BoatLocation parseIndividualMark(Mark mark) {
        //Create message.
        BoatLocation boatLocation = new BoatLocation(
                mark.getSourceID(),
                mark.getPosition().getLatitude(),
                mark.getPosition().getLongitude(),
                this.boatLocationSequenceNumber,
                BoatLocationDeviceEnum.Mark,
                Bearing.fromDegrees(0),
                0,
                race.getRaceClock().getCurrentTimeMilli());

        //Iterates the sequence number.
        this.boatLocationSequenceNumber++;

        return boatLocation;
    }

    /**
     * Parse the compound marker boats, and returns a list of BoatLocation messages.
     * @return BoatLocation messages for each mark.
     */
    private List<BoatLocation> parseMarks() {

        List<BoatLocation> markLocations = new ArrayList<>(race.getCompoundMarks().size());

        for (CompoundMark compoundMark : race.getCompoundMarks()) {

            //Get the individual marks from the compound mark.
            Mark mark1 = compoundMark.getMark1();
            Mark mark2 = compoundMark.getMark2();

            //If they aren't null, parse them (some compound marks only have one mark).
            if (mark1 != null) {
                markLocations.add(this.parseIndividualMark(mark1));
            }

            if (mark2 != null) {
                markLocations.add(this.parseIndividualMark(mark2));
            }
        }

        return markLocations;
    }


    /**
     * Parse the boats in the race, and returns all of their BoatLocation messages.
     * @return List of BoatLocation messages, for each boat.
     */
    private List<BoatLocation> parseBoatLocations() {

        List<BoatLocation> boatLocations = new ArrayList<>(race.getBoats().size());

        //Parse each boat.
        for (MockBoat boat : race.getBoats()) {
            boatLocations.add(this.parseIndividualBoatLocation(boat));
        }

        return boatLocations;
    }

    /**
     * Parses an individual boat, and returns it.
     * @param boat The boat to parse.
     * @return The BoatLocation message.
     */
    private BoatLocation parseIndividualBoatLocation(MockBoat boat) {

        BoatLocation boatLocation = new BoatLocation(
                boat.getSourceID(),
                boat.getCurrentPosition().getLatitude(),
                boat.getCurrentPosition().getLongitude(),
                this.boatLocationSequenceNumber,
                BoatLocationDeviceEnum.RacingYacht,
                boat.getBearing(),
                boat.getCurrentSpeed(),
                race.getRaceClock().getCurrentTimeMilli());

        //Iterates the sequence number.
        this.boatLocationSequenceNumber++;

        return boatLocation;

    }



    /**
     * Parses the race status, and returns it.
     * @return The race status message.
     */
    private RaceStatus parseRaceStatus() {

        //A race status message contains a list of boat statuses.
        List<BoatStatus> boatStatuses = new ArrayList<>();

        //Add each boat status to the status list.
        for (MockBoat boat : race.getBoats()) {

            BoatStatus boatStatus = new BoatStatus(
                    boat.getSourceID(),
                    boat.getStatus(),
                    boat.getCurrentLeg().getLegNumber(),
                    boat.getEstimatedTimeAtNextMark().toInstant().toEpochMilli() );

            boatStatuses.add(boatStatus);
        }



        //Create race status object, and send it.
        RaceStatus raceStatus = new RaceStatus(
                RaceStatus.currentMessageVersionNumber,
                System.currentTimeMillis(),
                race.getRaceId(),
                race.getRaceStatusEnum(),
                race.getRaceClock().getStartingTimeMilli(),
                race.getWindDirection(),
                race.getWindSpeed(),
                race.getRaceType(),
                boatStatuses);

        return raceStatus;
    }
}
