package seng302.Model;


import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.Mockito;
import org.xml.sax.SAXException;
import seng302.DataInput.BoatDataSource;
import seng302.DataInput.BoatXMLReader;
import seng302.DataInput.RaceDataSource;
import seng302.DataInput.RaceXMLReader;
import seng302.Exceptions.StreamedCourseXMLException;
import seng302.MockOutput;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;


import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;

/**
 * Created by esa46 on 15/03/17.
 */
public class RaceTest{


    public static final CompoundMark ORIGIN = new CompoundMark(new Mark(1, "test origin 1", new GPSCoordinate(0, 0)));
    public static final CompoundMark THREE_NM_FROM_ORIGIN = new CompoundMark(new Mark(2, "test mark 2", new GPSCoordinate(0.050246769, 0)));
    public static final CompoundMark FIFTEEN_NM_FROM_ORIGIN = new CompoundMark(new Mark(3, "test mark 3", new GPSCoordinate(0.251233845, 0)));
    public static ArrayList<Leg> TEST_LEGS = new ArrayList<>();
    public static final int START_LEG_DISTANCE = 3;
    public static final int MIDDLE_LEG_DISTANCE = 12;
    public static final Leg START_LEG = new Leg("Start", ORIGIN, THREE_NM_FROM_ORIGIN, 0);
    public static final Leg MIDDLE_LEG = new Leg("Middle", THREE_NM_FROM_ORIGIN, FIFTEEN_NM_FROM_ORIGIN, 1);
    public static final Leg FINISH_LEG = new Leg("Finish", FIFTEEN_NM_FROM_ORIGIN, FIFTEEN_NM_FROM_ORIGIN, 2);

    @Before
    public void setUp() {
        TEST_LEGS.add(START_LEG);
        TEST_LEGS.add(MIDDLE_LEG);
        TEST_LEGS.add(FINISH_LEG);
    }

    @Ignore
    @Test
    public void countdownTimerSendsBoatLocations() {

        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
            RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
            Race testRace = new Race(raceDataSource, mockOutput);
            testRace.initialiseBoats();
            testRace.countdownTimer.handle(1);
            verify(mockOutput, atLeast(boatDataSource.getBoats().size())).parseBoatLocation(anyInt(), anyDouble(), anyDouble(), anyDouble(), anyDouble());

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void countdownTimerSendsRaceStatusMessages() {

        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
            Race testRace = new Race(dataSource, mockOutput);
            testRace.initialiseBoats();
            testRace.countdownTimer.handle(1);
            verify(mockOutput, atLeast(1)).parseRaceStatus(any());

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void checkPositionFinishedUpdatesNumberFinishedBoats() {

        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
            Race testRace = new Race(dataSource, mockOutput);
            testRace.initialiseBoats();
            Boat testBoat = testRace.getBoats().get(0);
            testBoat.setCurrentLeg(FINISH_LEG);
            testBoat.setDistanceTravelledInLeg(1);
            testRace.checkPosition(testBoat, 1);

            assertEquals(testRace.getNumberOfActiveBoats(), 0);

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void checkPositionSetFinishedBoatVelocityTo0() {

        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
            Race testRace = new Race(dataSource, mockOutput);
            testRace.initialiseBoats();
            Boat testBoat = testRace.getBoats().get(0);
            testBoat.setCurrentLeg(FINISH_LEG);
            testBoat.setDistanceTravelledInLeg(1);
            testRace.checkPosition(testBoat, 1);

            assertEquals(testBoat.getCurrentSpeed(), 0, 1e-8);

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void checkPositionSetsFinishTime() {

        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
            Race testRace = new Race(dataSource, mockOutput);
            testRace.initialiseBoats();
            Boat testBoat = testRace.getBoats().get(0);
            testBoat.setCurrentLeg(FINISH_LEG);
            testBoat.setDistanceTravelledInLeg(1);
            testRace.checkPosition(testBoat, 1);

            assertEquals(testBoat.getTimeFinished(), 1, 1e-8);

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void checkPositionUnfinishedDoesntUpdateNumberFinishedBoats() {

        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
            Race testRace = new Race(dataSource, mockOutput);
            testRace.initialiseBoats();
            Boat testBoat = testRace.getBoats().get(0);
            testBoat.setCurrentLeg(START_LEG);
            testBoat.setDistanceTravelledInLeg(START_LEG_DISTANCE);
            testRace.checkPosition(testBoat, 1);

            assertEquals(testRace.getNumberOfActiveBoats(), 1);

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }


    @Ignore
    @Test
    public void distanceTravelledBeforeUpdatingLegIsRetained() {

        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            RaceDataSource dataSource = new RaceXMLReader("mockXML/raceTest.xml", new BoatXMLReader("mockXML/boatTest.xml", new Polars()));
            Race testRace = new Race(dataSource, mockOutput);
            testRace.initialiseBoats();
            Boat testBoat = testRace.getBoats().get(0);
            testBoat.setCurrentLeg(START_LEG);
            testBoat.setDistanceTravelledInLeg(START_LEG_DISTANCE + 1);
            testRace.checkPosition(testBoat, 0);

            assertEquals(testBoat.getDistanceTravelledInLeg(), 1, 1e-7);

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void doNotFinishAnswersYesIf100PercentChance() {

        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
            RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
            Race testRace = new Race(raceDataSource, mockOutput);

            testRace.setDnfChance(100);
            assertTrue(testRace.doNotFinish());

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void doNotFinishAnswersNoIf0PercentChance() {

        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
            RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
            Race testRace = new Race(raceDataSource, mockOutput);
            testRace.setDnfChance(0);
            assertFalse(testRace.doNotFinish());

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void boatsAreSetToDNF() {
        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
            RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
            Race testRace = new Race(raceDataSource, mockOutput);
            testRace.setDnfChance(100);
            Boat testBoat = testRace.getBoats().get(0);
            testBoat.setCurrentLeg(START_LEG);
            testBoat.setDistanceTravelledInLeg(START_LEG_DISTANCE + 1);
            testRace.checkPosition(testBoat, 1);
            assertEquals(testBoat.getCurrentLeg().getName(), "DNF");

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }

    }

    @Ignore
    @Test
    public void updatePositionIgnoresFinishedBoats() {
        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
            RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
            Race testRace = new Race(raceDataSource, mockOutput);
            Boat testBoat = testRace.getBoats().get(0);
            testBoat.setCurrentLeg(FINISH_LEG);
            testBoat.setCurrentPosition(ORIGIN.getAverageGPSCoordinate());
            testRace.updatePosition(testBoat, 1, 1);
            assertEquals(testBoat.getCurrentPosition(), ORIGIN.getAverageGPSCoordinate());

        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void updatePositionChangesBoatPosition() {
        try {
            MockOutput mockOutput = Mockito.mock(MockOutput.class);
            BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml", new Polars());
            RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
            Race testRace = new Race(raceDataSource, mockOutput);
            testRace.initialiseBoats();
            Boat testBoat = testRace.getBoats().get(0);
            testBoat.setCurrentLeg(START_LEG);
            testBoat.setDistanceTravelledInLeg(START_LEG_DISTANCE - 1);
            testBoat.setCurrentPosition(ORIGIN.getAverageGPSCoordinate());
            testRace.updatePosition(testBoat, 100, 100);
            assertFalse(testBoat.getCurrentPosition() == ORIGIN.getAverageGPSCoordinate());
        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
            e.printStackTrace();
            fail();
        }
    }

    @Ignore
    @Test
    public void windDirectionCorrectValues(){
//        try {
//            MockOutput mockOutput = Mockito.mock(MockOutput.class);
//            BoatDataSource boatDataSource = new BoatXMLReader("mockXML/boatTest.xml");
//            RaceDataSource raceDataSource = new RaceXMLReader("mockXML/raceTest.xml", boatDataSource);
//            Race testRace = new Race(raceDataSource, mockOutput);
//            testRace.setChangeWind(1);
//            testRace.setWindDir(65535);
//            testRace.changeWindDir();
//            assertEquals(100, testRace.getWind());
//        } catch (ParserConfigurationException | IOException | SAXException | ParseException | StreamedCourseXMLException e) {
//            e.printStackTrace();
//            fail();
//        }
    }


}
