package be.iminds.ilabt.jfed.experimenter_gui.ui;

import be.iminds.ilabt.jfed.experimenter_gui.config.JFedGuiConfig;
import be.iminds.ilabt.jfed.experimenter_gui.util.StringConverters;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.MaintenanceInfo;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.ResourceClass;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.Server;
import be.iminds.ilabt.jfed.fedmon.webapi.service.json.ServerGlimpse;
import be.iminds.ilabt.jfed.ui.javafx.GlyphUtils;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.paint.Color;
import org.controlsfx.glyphfont.FontAwesome;
import org.controlsfx.glyphfont.Glyph;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * User: twalcari
 * Date: 8/18/2016
 * Time: 15:42
 */
public class ResourceCell extends ListCell<Server> {

    private static final Comparator<? super MaintenanceInfo> MAINTENANCE_MESSAGE_COMPARATOR =
            Comparator.nullsLast(Comparator.comparing(MaintenanceInfo::getStart))
                    .thenComparing(Comparator.nullsLast(Comparator.comparing(MaintenanceInfo::getEnd)))
                    .thenComparing(MaintenanceInfo::getMessage);


    private final JFedGuiConfig config;

    protected final HBox container = new HBox(2);

    protected final Label nameLabel = new Label("");
    protected final Glyph maintenanceIcon =
            GlyphUtils.createGlyph(FontAwesome.Glyph.WRENCH, Color.RED);
    protected final Glyph reputationGlyph = GlyphUtils.createGlyph(FontAwesome.Glyph.STAR, Color.GRAY);
    protected final Glyph healthStatusGlyph = GlyphUtils.createGlyph(FontAwesome.Glyph.HEART, Color.GRAY);

    private final boolean shouldUseVMCalculationMethod;
    @Nullable
    protected final AvailabilityMeter availabilityMeter;

    public ResourceCell(@Nullable ResourceClass resourceClass, @Nonnull JFedGuiConfig config) {
        this.config = config;

        Pane fillerPane = new Pane();
        HBox.setHgrow(fillerPane, Priority.ALWAYS);

        container.setPadding(new Insets(0, 5, 2, 0));
        container.getChildren().addAll(nameLabel, fillerPane, maintenanceIcon);
        if (config.isShowReputationEnabled()) {
            container.getChildren().add(reputationGlyph);
        }
        container.getChildren().add(healthStatusGlyph);

        boolean shouldUseVMCalculationMethod = false;
        if (resourceClass != null) {
            if (config.getResourceType(resourceClass) == JFedGuiConfig.ResourceType.RAW_PC) {
                availabilityMeter = new AvailabilityMeter();
                container.getChildren().add(availabilityMeter);
            } else if (config.getResourceType(resourceClass) == JFedGuiConfig.ResourceType.VM) {
                availabilityMeter = new AvailabilityMeter();
                shouldUseVMCalculationMethod = true;
                availabilityMeter.setUseVmCalculationMethod(true);
                container.getChildren().add(availabilityMeter);
            } else {
                availabilityMeter = null;
            }
        } else {
            availabilityMeter = null;
        }

        setGraphic(container);

        this.shouldUseVMCalculationMethod = shouldUseVMCalculationMethod;
    }


    @Override
    protected void updateItem(Server item, boolean empty) {
        super.updateItem(item, empty);
        if (!empty && item != null) {
            nameLabel.setText(StringConverters.SERVER_STRING_CONVERTER.toString(item));

            ServerGlimpse serverGlimpse = config.getServerGlimpse(item);


            if (serverGlimpse != null) {
                ServerGlimpse.HealthInfo health = serverGlimpse.getHealthInfo();
                if (health != null && health.getHealth() != null) {
                    final int healthPercent = health.getHealth();

                    if (healthPercent == 100) {
                        healthStatusGlyph.setTextFill(Color.GREEN);
                    } else if (healthPercent > 66) {
                        healthStatusGlyph.setTextFill(Color.YELLOWGREEN);
                    } else if (healthPercent > 33) {
                        healthStatusGlyph.setTextFill(Color.ORANGE);
                    } else {
                        healthStatusGlyph.setTextFill(Color.RED);
                    }
                    Tooltip.install(healthStatusGlyph, new Tooltip(String.format("%s has a health rating of %d%%",
                            StringConverters.SERVER_STRING_CONVERTER.toString(item),
                            healthPercent)));

                    healthStatusGlyph.setVisible(true);
                } else {
                    healthStatusGlyph.setVisible(false);
                }

                if (serverGlimpse.getReputation() != null && config.isShowReputationEnabled()) {
                    final int reputation = serverGlimpse.getReputation();

                    if (reputation > 95) {
                        reputationGlyph.setTextFill(Color.GREEN);
                    } else if (reputation > 66) {
                        reputationGlyph.setTextFill(Color.YELLOWGREEN);
                    } else if (reputation > 33) {
                        reputationGlyph.setTextFill(Color.ORANGE);
                    } else {
                        reputationGlyph.setTextFill(Color.RED);
                    }
                    Tooltip.install(reputationGlyph, new Tooltip(String.format("%s has a reputation rating of %d%%",
                            StringConverters.SERVER_STRING_CONVERTER.toString(item),
                            reputation)));


                    reputationGlyph.setVisible(true);
                } else {
                    reputationGlyph.setVisible(false);
                }

                if (serverGlimpse.getMaintenance() != null) {
                    List<MaintenanceInfo> activeMaintenances =
                            serverGlimpse.getMaintenance().stream()
                                    .filter(mm -> mm.getStartTime() == null || mm.getStartTime().isBefore(ZonedDateTime.now()))
                                    .filter(mm -> mm.getEndTime() == null || mm.getEndTime().isAfter(ZonedDateTime.now()))
                                    .sorted(MAINTENANCE_MESSAGE_COMPARATOR)
                                    .collect(Collectors.toList());

                    List<MaintenanceInfo> futureMaintenances =
                            serverGlimpse.getMaintenance().stream()
                                    .filter(mm -> mm.getStartTime() != null && mm.getStartTime().isAfter(ZonedDateTime.now()))
                                    .sorted(MAINTENANCE_MESSAGE_COMPARATOR)
                                    .collect(Collectors.toList());

                    if (!activeMaintenances.isEmpty()) {
                        Tooltip.install(maintenanceIcon, new Tooltip(HealthStatusPane.formatMaintenanceInfos(activeMaintenances)));
                        maintenanceIcon.setTextFill(Color.RED);
                        maintenanceIcon.setVisible(true);
                    } else if (!futureMaintenances.isEmpty()) {
                        Tooltip.install(maintenanceIcon, new Tooltip(HealthStatusPane.formatMaintenanceInfos(futureMaintenances)));
                        maintenanceIcon.setTextFill(Color.ORANGE);
                        maintenanceIcon.setVisible(true);
                    } else {
                        maintenanceIcon.setVisible(false);
                    }
                } else {
                    maintenanceIcon.setVisible(false);
                }

                ServerGlimpse.AdvertisementInfo advInf = serverGlimpse.getAdvertisementInfo();
                if (advInf != null && availabilityMeter != null) {
                    
                    Integer rawAvail = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.RAW_PC, ServerGlimpse.AdvertisementInfo.CountScope.AVAILABLE);
                    Integer rawTotal = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.RAW_PC, ServerGlimpse.AdvertisementInfo.CountScope.ALL);
                    Integer vmAvail = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.VM, ServerGlimpse.AdvertisementInfo.CountScope.AVAILABLE);
                    Integer vmTotal = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.VM, ServerGlimpse.AdvertisementInfo.CountScope.ALL);
                    Integer anyAvail = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.ANY, ServerGlimpse.AdvertisementInfo.CountScope.AVAILABLE);
                    Integer anyTotal = advInf.getCount(ServerGlimpse.AdvertisementInfo.CountedObject.ANY, ServerGlimpse.AdvertisementInfo.CountScope.ALL);
                    
                    if (shouldUseVMCalculationMethod
                            && vmAvail != null
                            && vmTotal != null
                            && vmTotal != 0) {
                        availabilityMeter.setUseVmCalculationMethod(true);

                        availabilityMeter.setAvailable(vmAvail);
                        availabilityMeter.setTotal(vmTotal);
                        availabilityMeter.setVisible(true);
                        availabilityMeter.setUndetermined(false);

                        Tooltip.install(availabilityMeter,
                                new Tooltip(vmAvail + " of "
                                        + vmTotal + " VM available"));
                    } else {
                        availabilityMeter.setUseVmCalculationMethod(false);
                        if (rawAvail != null
                                && rawTotal != null
                                && rawTotal != 0) {
                            availabilityMeter.setAvailable(rawAvail);
                            availabilityMeter.setTotal(rawTotal);
                            availabilityMeter.setVisible(true);
                            availabilityMeter.setUndetermined(false);

                            Tooltip.install(availabilityMeter,
                                    new Tooltip(rawAvail + " of "
                                            + rawTotal + " bare metal available"));

                            availabilityMeter.setVisible(true);
                        } else if (anyAvail != null
                                && anyTotal != null
                                && anyTotal != 0) {
                            availabilityMeter.setAvailable(anyAvail);
                            availabilityMeter.setTotal(anyTotal);
                            availabilityMeter.setVisible(true);
                            availabilityMeter.setUndetermined(false);

                            Tooltip.install(availabilityMeter,
                                    new Tooltip(anyAvail + " of "
                                            + anyTotal + " resources available"));
                        } else {
                            availabilityMeter.setAvailable(0);
                            availabilityMeter.setTotal(0);
                            availabilityMeter.setVisible(true);
                            availabilityMeter.setUndetermined(true);
                        }
                    }
                }


            } else {
                healthStatusGlyph.setVisible(false);
                reputationGlyph.setVisible(false);
                maintenanceIcon.setVisible(false);
                if (availabilityMeter != null)
                    availabilityMeter.setVisible(false);
            }


        } else {
            nameLabel.setText("");
            healthStatusGlyph.setVisible(false);
            reputationGlyph.setVisible(false);
            maintenanceIcon.setVisible(false);
            if (availabilityMeter != null)
                availabilityMeter.setVisible(false);
        }
    }

}
