package ch.zhaw.gartenverwaltung;

import ch.zhaw.gartenverwaltung.io.HardinessZoneNotSetException;
import ch.zhaw.gartenverwaltung.plantList.PlantListModel;
import ch.zhaw.gartenverwaltung.types.HardinessZone;
import ch.zhaw.gartenverwaltung.types.Plant;
import ch.zhaw.gartenverwaltung.types.Seasons;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.stage.Modality;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.URL;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;

public class PlantsController implements Initializable {
    private static final Logger LOG = Logger.getLogger(PlantsController.class.getName());
    private final PlantListModel plantListModel = new PlantListModel();
    private Plant selectedPlant = null;
    private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A;

    // TODO: move to model
    private final ListProperty<Plant> plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList());

    @FXML
    private VBox seasons;

    @FXML
    private VBox climate_zones;

    @FXML
    private Label description_plant;

    @FXML
    private ImageView img_plant;

    @FXML
    private ListView<Plant> list_plants;

    @FXML
    private Button selectSowDay_button;

    @FXML
    private TextField search_plants;

    /**
     * open new window to select sow or harvest day to save the crop
     * @param event event
     */
    @FXML
    void selectSowDate(ActionEvent event) throws IOException {
        Parent root;
        FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("SelectSowDay.fxml")));
        root = fxmlLoader.load();
        SelectSowDayController controller = fxmlLoader.getController();
        controller.getSelectedPlant(selectedPlant);
        Stage stage = new Stage();
        stage.setScene(new Scene(root));
        stage.initModality(Modality.APPLICATION_MODAL);
        stage.setResizable(false);
        stage.showAndWait();
    }

    /**
     * fill list view with current hardiness zone
     * set default values
     * create filter of season and hardiness zone
     * create event listener for selected list entry and search by query
     * {@inheritDoc}
     */
    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        setListCellFactory();
        fillPlantListWithHardinessZone();
        list_plants.itemsProperty().bind(plantListProperty);

        description_plant.setText("");
        selectSowDay_button.setDisable(true);

        createFilterSeasons();
        createFilterHardinessZone();
        lookForSelectedListEntry();
        try {
            viewFilteredListBySearch();
        } catch (HardinessZoneNotSetException e) {
            LOG.log(Level.WARNING, "Hardiness Zone not set!");
        } catch (IOException e) {
            LOG.log(Level.WARNING, "Could not retrieve data!", e);
        }
    }

    /**
     *  set text of list view to plant name
     */
    private void setListCellFactory() {
        list_plants.setCellFactory(param -> new ListCell<>() {
            @Override
            protected void updateItem(Plant plant, boolean empty) {
                super.updateItem(plant, empty);

                if (empty || plant == null || plant.name() == null) {
                    setText(null);
                } else {
                    setText(plant.name());
                }
            }
        });
    }

    /**
     * get plant list according to param season and hardiness zone
     * fill list view with plant list
     * @param season enum of seasons
     * @throws HardinessZoneNotSetException throws exception
     * @throws IOException throws exception
     */
    private void viewFilteredListBySeason(Seasons season) throws HardinessZoneNotSetException, IOException {
        clearListView();
        plantListProperty.addAll(plantListModel.getFilteredPlantListBySaisonWithoutGrowthPhase(plantListModel.getCurrentZone(), season.getStartDate(), season.getEndDate()));
    }

    /**
     * get plant list filtered by search plant entry and hardiness zone
     * fill list view with plant list
     * @throws HardinessZoneNotSetException throws exception when no hardiness zone is defined
     * @throws IOException throws exception
     */
    private void viewFilteredListBySearch() throws HardinessZoneNotSetException, IOException {
        search_plants.textProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue.isEmpty()) {
                fillPlantListWithHardinessZone();
            } else {
                try {
                    List<Plant> filteredPlants = plantListModel.getFilteredPlantListByString(DEFAULT_HARDINESS_ZONE, newValue);
                    clearListView();
                    plantListProperty.addAll(filteredPlants);
                } catch (HardinessZoneNotSetException e) {
                    LOG.log(Level.WARNING, "Hardiness Zone not set!");
                } catch (IOException e) {
                    LOG.log(Level.WARNING, "Could not retrieve data!", e);
                }
            }
        });
    }

    /**
     * get plant list of current hardiness zone
     * fill list view with plant list
     */
    private void fillPlantListWithHardinessZone() {
        try {
            clearListView();
            plantListProperty.addAll(plantListModel.getPlantList(plantListModel.getCurrentZone()));
        } catch (HardinessZoneNotSetException e) {
            LOG.log(Level.WARNING, "Hardiness Zone not set!");
        } catch (IOException e) {
            LOG.log(Level.WARNING, "Could not retrieve data!", e);
        }
    }

    /**
     * creates radio buttons for the hardiness zones defined in enum HardinessZone
     * defines default value as selected
     * when selected filter viewList according to hardiness zone
     */
    private void createFilterHardinessZone() {
        ToggleGroup hardinessGroup = new ToggleGroup();
        for (HardinessZone zone : HardinessZone.values()) {
            RadioButton radioButton = new RadioButton(zone.name());
            radioButton.setToggleGroup(hardinessGroup);
            radioButton.setPadding(new Insets(0,0,10,0));
            if (zone.equals(DEFAULT_HARDINESS_ZONE)) {
                radioButton.setSelected(true);
            }
            radioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
                plantListModel.setCurrentZone(zone);
                fillPlantListWithHardinessZone();
            });
            climate_zones.getChildren().add(radioButton);
        }
    }

    /**
     * creates radio buttons for the seasons defined in enum Seasons
     * defines default value as selected
     * when selected filter viewList according to seasons
     */
    private void createFilterSeasons() {
        ToggleGroup seasonGroup = new ToggleGroup();
        for (Seasons season : Seasons.values()) {
            RadioButton radioButton = new RadioButton(season.getName());
            radioButton.setToggleGroup(seasonGroup);
            radioButton.setPadding(new Insets(0,0,10,0));
            if (season.equals(Seasons.AllSEASONS)) {
                radioButton.setSelected(true);
            }
            radioButton.selectedProperty().addListener((observable, oldValue, newValue) -> {
                if (season.equals(Seasons.AllSEASONS)) {
                    fillPlantListWithHardinessZone();
                } else {
                    try {
                        viewFilteredListBySeason(season);
                    } catch (HardinessZoneNotSetException e) {
                        LOG.log(Level.WARNING, "Hardiness Zone not set!");
                    } catch (IOException e) {
                        LOG.log(Level.WARNING, "Could not retrieve data!", e);
                    }
                }

            });
            seasons.getChildren().add(radioButton);
        }
    }

    /**
     * observes changes in the selectedProperty of ListView and updates:
     * the description label
     * image of the plant
     */
    private void lookForSelectedListEntry() {
        selectedPlant = null;
        description_plant.setText("");
        selectSowDay_button.setDisable(true);
        Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
        img_plant.setImage(img);
        list_plants.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if(newValue != null) {
                selectedPlant = newValue;
                description_plant.setText(selectedPlant.description());
                selectSowDay_button.setDisable(false);
                Image img1;
                if(selectedPlant.image() != null) {
                    img1 = selectedPlant.image();
                } else {
                    img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
                }
                img_plant.setImage(img1);
            } else {
                selectedPlant = null;
                description_plant.setText("");
                selectSowDay_button.setDisable(true);
                Image img1 = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png")));
                img_plant.setImage(img1);
            }
        });
    }


    /**
     * clears the ListView of entries
     */
    private void clearListView() {
        plantListProperty.clear();
    }
}
