package be.iminds.ilabt.jfed.rspec.model;

import be.iminds.ilabt.jfed.rspec.model.controller.ExperimentBarrierSegment;
import be.iminds.ilabt.jfed.rspec.model.controller.ExperimentCommand;
import be.iminds.ilabt.jfed.rspec.model.controller.Timestamp;
import be.iminds.ilabt.jfed.rspec.model.javafx_impl.*;
import be.iminds.ilabt.jfed.rspec.parser.StaxRspecParser;
import be.iminds.ilabt.jfed.rspec.parser.TestRspecReconstructable;
import javafx.embed.swing.JFXPanel;
import org.slf4j.LoggerFactory;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import javax.swing.*;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.testng.Assert.*;


/**
 * TestRspec
 */
public class TestRspecFX extends TestRspec {
    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(TestRspecFX.class);

    protected final FXRspecFactory fxRspecFactory;

    public TestRspecFX() {
        super(FXRspecFactory.getInstance());
        this.fxRspecFactory = (FXRspecFactory) rspecFactory;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    private static boolean isJavaFxSetup = false;

    private static void setupJavaFx() throws InterruptedException {
        long timeMillis = System.currentTimeMillis();

        final CountDownLatch latch = new CountDownLatch(1);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                // initializes JavaFX environment
                new JFXPanel();

                latch.countDown();
            }
        });

        System.out.println("javafx initialising...");
        latch.await();
        System.out.println("javafx is initialised in " + (System.currentTimeMillis() - timeMillis) + "ms");
    }

    @BeforeMethod()
    public void setupSuite() throws InterruptedException {
        if (!isJavaFxSetup) {
            setupJavaFx();
            isJavaFxSetup = true;
        }
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    @Test
    public void testBarrierAndCommandRspecModel() throws Exception {
        FXModelRspec testRspec = (FXModelRspec) generateBaseTestRspec(fxRspecFactory);

        testRspec.getBarrierSegments().add(new ExperimentBarrierSegment("first", 0));
        testRspec.getBarrierSegments().add(new ExperimentBarrierSegment("second", 1));
        testRspec.getBarrierSegments().add(new ExperimentBarrierSegment("third", 2));

        FXRspecNode origN1 = testRspec.getNodeByClientId("firstNode");
        FXRspecNode origN2 = testRspec.getNodeByClientId("secondNode");

        origN1.getCommands().add(new ExperimentCommand("com1", origN1.getUniqueId(), 0/*barrierSegmentID*/, "echo test1",
                new Timestamp(0.0/*ms*/) /*time*/, new Timestamp(1.0/*ms*/)));
        origN1.getCommands().add(new ExperimentCommand("com2", origN1.getUniqueId(), 1/*barrierSegmentID*/, "echo after test1 and test2",
                new Timestamp(0.0/*ms*/) /*time*/, new Timestamp(2.0/*ms*/)));
        origN2.getCommands().add(new ExperimentCommand("com3", origN2.getUniqueId(), 0/*barrierSegmentID*/, "echo test2",
                new Timestamp(2.0/*ms*/) /*time*/, new Timestamp(3.0/*ms*/)));

        String rspecXmlString = testRspec.toGeni3Rspec();
        LOG.debug("Test Rspec XML: " + rspecXmlString);
        FXModelRspec reconstructedModel = (FXModelRspec) new StaxRspecParser(rspecXmlString, rspecFactory).call();
        String reconstructedRspecXmlString = reconstructedModel.toGeni3Rspec();
        assertTrue(TestRspecReconstructable.testSameXml(rspecXmlString, reconstructedRspecXmlString));
        testBasics(reconstructedModel);

        List<ExperimentBarrierSegment> segments = reconstructedModel.getBarrierSegments();
        assertEquals(segments.size(), 3);
        ExperimentBarrierSegment s1 = segments.get(0);
        ExperimentBarrierSegment s2 = segments.get(1);
        ExperimentBarrierSegment s3 = segments.get(2);
        assertEquals(s1.getTag(), "first");
        assertEquals(s2.getTag(), "second");
        assertEquals(s3.getTag(), "third");
        assertEquals(s1.getOrderNumber(), 0);
        assertEquals(s2.getOrderNumber(), 1);
        assertEquals(s3.getOrderNumber(), 2);

        FXRspecNode n1 = reconstructedModel.getNodeByClientId("firstNode");
        FXRspecNode n2 = reconstructedModel.getNodeByClientId("secondNode");
        assertEquals(n1.getCommands().size(), 2);
        assertEquals(n2.getCommands().size(), 1);
        ExperimentCommand c1 = n1.getCommands().get(0);
        ExperimentCommand c2 = n1.getCommands().get(1);
        ExperimentCommand c3 = n2.getCommands().get(0);
        assertEquals(c1.getBarrierSegmentOrderNumber(), 0);
        assertEquals(c2.getBarrierSegmentOrderNumber(), 1);
        assertEquals(c3.getBarrierSegmentOrderNumber(), 0);
        assertEquals(c1.getCommand(), "echo test1");
        assertEquals(c2.getCommand(), "echo after test1 and test2");
        assertEquals(c3.getCommand(), "echo test2");
        assertEquals(c1.getTag(), "com1");
        assertEquals(c2.getTag(), "com2");
        assertEquals(c3.getTag(), "com3");
        assertEquals(c1.getStartingTime(), new Timestamp(0.0));
        assertEquals(c2.getStartingTime(), new Timestamp(0.0));
        assertEquals(c3.getStartingTime(), new Timestamp(2.0));
        assertEquals(c1.getDuration(), new Timestamp(1.0/*ms*/));
        assertEquals(c2.getDuration(), new Timestamp(2.0/*ms*/));
        assertEquals(c3.getDuration(), new Timestamp(3.0/*ms*/));
    }

    @Test
    public void testDelayNodeRspecModel() throws Exception {
        ModelRspec testRspec = generateBaseTestRspec(rspecFactory);

        DelayRspecNode delayRspecNode = new DelayRspecNode((FXModelRspec)testRspec, "delayNode");
        delayRspecNode.setComponentManagerId(TEST_COMP_MAN_URN2);
        testRspec.addNode(delayRspecNode);

        RspecLink linkA = rspecFactory.createLinkWithClientId(testRspec, "linkA", "lan");
        RspecLink linkB = rspecFactory.createLinkWithClientId(testRspec, "linkB", "lan");

        RspecNode origN1 = testRspec.getNodeByClientId("firstNode");
        RspecNode origN2 = testRspec.getNodeByClientId("secondNode");

        RspecInterface origIfaceA = rspecFactory.createInterface(delayRspecNode, linkA, "ifA");
        RspecInterface origIfaceB = rspecFactory.createInterface(delayRspecNode, linkB, "ifB");
        RspecInterface origIfaceAA = rspecFactory.createInterface(origN1, linkA, testRspec.nextIfaceName(origN1));
        RspecInterface origIfaceBB = rspecFactory.createInterface(origN2, linkB, testRspec.nextIfaceName(origN2));
        testRspec.addLink(linkA);
        testRspec.addLink(linkB);

        LinkSetting origLinkSetting1 = delayRspecNode.getLinkSetting(origIfaceA, origIfaceB);
        LinkSetting origLinkSetting2 = delayRspecNode.getLinkSetting(origIfaceB, origIfaceA);
        origLinkSetting1.setCapacity_Kbps(100l);
        origLinkSetting1.setLatency_ms(10);
        origLinkSetting1.setPacketLoss(1.0);
        origLinkSetting2.setCapacity_Kbps(200l);
        origLinkSetting2.setLatency_ms(20);
        origLinkSetting2.setPacketLoss(2.0);

        String rspecXmlString = testRspec.toGeni3Rspec();
        LOG.debug("Test Rspec XML: " + rspecXmlString);

        assertEquals(delayRspecNode.getSliverTypes().get(0).getExtraXml().size(), 1);

        ModelRspec reconstructedModel = new StaxRspecParser(rspecXmlString, rspecFactory).call();
        String reconstructedRspecXmlString = reconstructedModel.toGeni3Rspec();
        assertTrue(TestRspecReconstructable.testSameXml(rspecXmlString, reconstructedRspecXmlString));
        testBasics(reconstructedModel);

        RspecNode reconStructedNode = reconstructedModel.getNodeByClientId("delayNode");
        assertNotNull(reconStructedNode);

        assertEquals(reconStructedNode.getComponentManagerId().getValue(), TEST_COMP_MAN_URN2);
        assertEquals(reconStructedNode.getSliverTypeName(), "delay");
        assertEquals(reconStructedNode.getSliverTypes().size(), 1);
        assertEquals(reconStructedNode.getSliverTypes().get(0).getExtraXml().size(), 1);

        assertEquals(reconStructedNode.getClass(), DelayRspecNode.class);
        DelayRspecNode reconStructedDelayRspecNode = (DelayRspecNode) reconStructedNode;
        assertEquals(reconStructedDelayRspecNode.getLinkSettings().size(), 2);
        FXRspecInterface ifaceA = reconStructedDelayRspecNode.getInterfaceByClientId("ifA");
        FXRspecInterface ifaceB = reconStructedDelayRspecNode.getInterfaceByClientId("ifB");
        assertNotNull(ifaceA);
        assertNotNull(ifaceB);
        FXLinkSetting linkSetting1 = reconStructedDelayRspecNode.getLinkSetting(ifaceA, ifaceB);
        assertNotNull(linkSetting1);
        assertEquals(linkSetting1.getCapacity_Kbps(), 100l);
        assertEquals(linkSetting1.getLatency_ms(), 10);
        assertEquals(linkSetting1.getPacketLoss(), 1.0);
        FXLinkSetting linkSetting2 = reconStructedDelayRspecNode.getLinkSetting(ifaceB, ifaceA);
        assertNotNull(linkSetting2);
        assertEquals(linkSetting2.getCapacity_Kbps(), 200l);
        assertEquals(linkSetting2.getLatency_ms(), 20);
        assertEquals(linkSetting2.getPacketLoss(), 2.0);
    }

}
