package be.iminds.jfed.gts_highlevel.jobs;

/**
 * User: twalcari
 * Date: 10/7/2015
 * Time: 10:20
 */

public class GtsStartTestbedsJob { /*extends AbstractJob<Void> {
    private static final Logger LOG = LoggerFactory.getLogger(GtsStartTestbedsJob.class);

    private static final int PASSWORD_LENGTH = 12;
    private static final int VPN_USERNAME_SUFFIX_LENGTH = 5;

    private final String experimentName;
    private final RequestRspecSource requestRspec;
    private final GtsModel model;
    private final GtsTaskFactory gtsTaskFactory;


    // private CyclicBarrier barrier;
    private CountDownLatch finalLatch;


    //results
    private ObjectProperty<GtsProject> project = new SimpleObjectProperty<>();
    private ObservableMap<GeantTestbedType, GtsReservation> reservations = FXCollections.observableHashMap();
    private String vpnUsername;
    private String vpnPassword;


    GtsStartTestbedsJob(@Nonnull String experimentName,
                        @Nonnull RequestRspecSource requestRspec,
                        TaskThread tt,
                        GtsModel model,
                        GtsTaskFactory gtsTaskFactory) {
        super("Start GTS Testbeds in " + experimentName, experimentName, hltf, tt);
        this.experimentName = experimentName;
        this.requestRspec = requestRspec;
        this.model = model;
        this.gtsTaskFactory = gtsTaskFactory;
    }

    @Override
    public Void execute() throws Exception {
        FXModelRspec modelRspec = (FXModelRspec) requestRspec.getModelRspec(ModelRspecType.FX);
        assert modelRspec != null;
        assert !modelRspec.getGeantTestbedTypes().isEmpty() : "Expected one or more GeantTestbedTypes!";

        //make sure we are logged in
        LOG.trace("Logging into GTS");
        if (model.getCredentials() == null) {
            TaskExecution loginTask = submitTaskAndWait(gtsTaskFactory.createLoginGtsTask());

            if (loginTask.getState() != TaskExecution.TaskState.SUCCESS) {
                throw new GtsHighLevelException("Could not login", loginTask.getException());
            }

        }

        //create a project for this slice
        vpnUsername = experimentName + "_" + PasswordUtil.generatePassword(VPN_USERNAME_SUFFIX_LENGTH);
        vpnUsername = PasswordUtil.generatePassword(PASSWORD_LENGTH);

        CreateGtsProjectTask createGtsProjectTask =
                gtsTaskFactory.createCreateGtsProjectTask(experimentName, vpnUsername, vpnPassword,
                        Collections.singletonList(model.getUsername()));

        LOG.trace("Creating GTS project {}", experimentName);
        TaskExecution createTaskExecution = submitTaskAndWait(createGtsProjectTask);

        if (createTaskExecution.getState() != TaskExecution.TaskState.SUCCESS) {
            throw new GtsHighLevelException("Could not create a GTS project for slice " + experimentName,
                    createTaskExecution.getException());
        }

        LOG.trace("Got GTS project {}", createGtsProjectTask.getProject());
        project.setValue(createGtsProjectTask.getProject());
        assert project.get() != null;

        //   barrier = new CyclicBarrier(modelRspec.getGeantTestbedTypes().size());
        this.finalLatch = new CountDownLatch(modelRspec.getGeantTestbedTypes().size());


        modelRspec.getGeantTestbedTypes().forEach(gtt -> new StartTestbedThread(gtt).start());

        finalLatch.await();

        return null;
    }

    public String getVpnPassword() {
        return vpnPassword;
    }

    public String getVpnUsername() {
        return vpnUsername;
    }

    public GtsProject getProject() {
        return project.get();
    }

    public ObjectProperty<GtsProject> projectProperty() {
        return project;
    }

    public ObservableMap<GeantTestbedType, GtsReservation> getReservations() {
        return /*FXCollections.unmodifiableObservableMap(* /reservations / *)* /;
    }

    private class StartTestbedThread extends Thread {
        private final GeantTestbedType geantTestbedType;


        private StartTestbedThread(GeantTestbedType geantTestbedType) {
            this.geantTestbedType = geantTestbedType;

            setName("StartTestbedThread-" + experimentName + "-" + geantTestbedType.getName());
            this.logMarker = MarkerFactory.getMarker("GTS-" + experimentName + "-" + geantTestbedType.getName());
        }

        @Override
        public void run() {
            LOG.trace(logMarker, "Starting GTS resources {} in project {}",
                    geantTestbedType.getName(), project.get().getId());
            StartGtsResourcesTask startGtsResourcesTask =
                    gtsTaskFactory.createStartGtsResourcesTask(
                            geantTestbedType.getName(), geantTestbedType.getDsl(), project.get());

            try {
                TaskExecution taskExecution = submitTaskAndWait(startGtsResourcesTask);

                if (taskExecution.getState() != TaskExecution.TaskState.SUCCESS) {
                    LOG.error(logMarker, "Could not start GTS resources", taskExecution.getException());
                    return;
                }
                assert startGtsResourcesTask.getReservation() != null;

                LOG.debug("Registering result reservation {} for GTS type {}",
                        model.getReservation(startGtsResourcesTask.getReservation().getId()), geantTestbedType.getName());
                //we add the object from the model, because that instance will be kept in sync!
                reservations.put(geantTestbedType, model.getReservation(startGtsResourcesTask.getReservation().getId()));

                //list resources before continuing, this allows GUI to show status
                submitTaskAndWait(gtsTaskFactory.createGtsListResourcesTask(project.get()));

                LOG.trace(logMarker, "Waiting for resources in reservation {} of project {} to become ready",
                        startGtsResourcesTask.getReservation().getId(), project.get().getId());
                UpdateGtsReservationsUntilReadyTask updateGtsReservationsUntilReadyTask =
                        gtsTaskFactory.createUpdateGtsReservationsUntilReadyTask(project.get(), startGtsResourcesTask.getReservation().getId());

                scheduleTask(updateGtsReservationsUntilReadyTask, 10000);
                /* We cannot wait for the TaskExecution to finish, as this task
                    will re-schedule itself when necessary
                    (and thus generate more TaskExecutions).
                * /
                while (!updateGtsReservationsUntilReadyTask.isFinished()) {
                    Thread.sleep(1000);
                }

                //now list resources

                submitTaskAndWait(gtsTaskFactory.createGtsListResourcesTask(project.get()));


            } catch (InterruptedException e) {
                LOG.error(logMarker, "Interrupted while starting GTS Testbed {} in slice {}", geantTestbedType.getName(), experimentName, e);
            }
            finalLatch.countDown();
        }
    } */
}
