/*
 * RecipeIngredientsScreen.fx
 */

package cookery;

import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

import javafx.geometry.HPos;

import cookery_db.database.Recipe;
import cookery_db.database.Ingredient;
import cookery_db.database.RecipeIngredient;
import cookery_db.database.RecipeIngredientPK;

import java.util.Iterator;

import cookery_db.database.QuantityUnit;

import javafx.geometry.VPos;

import javafx.scene.layout.Container;

import javafx.util.Sequences;

import javafx.scene.layout.LayoutInfo;


/**
 * A screen that allows ingredients to be added and removed for a specific recipe by the user.
 * @author Nick Apperley
 */
public class RecipeIngredientsScreen extends Stage{
    def INGREDIENTS_WIDTH = 160;
    def NO_ITEM_SELECTED = -1;
    def EMPTY_LIST = 0;
    def DBL_CLICK = 2;
    public-init var recipe: Recipe;
    public-init var controller: Controller;
    var quantityListXPos: Float;
    var ingredientQuantityScreen: IngredientQuantityScreen;
    public var onRecipeIngredientsChanged: function(rise: RecipeIngredientsScreenEvent);

    def quantityList: ListView = ListView{
        width: bind scene.width - quantityListXPos
        onMouseClicked: function(me){
            recipeIngredientsList.select(quantityList.selectedIndex);
            if(me.clickCount == DBL_CLICK){
                showIngredientQuantityScreen();
            }
        }
    };
    def ingredientsList: ListView = ListView{
        layoutInfo: LayoutInfo{width: INGREDIENTS_WIDTH}
        onMouseClicked: function(me){
            if(me.clickCount == DBL_CLICK){
                includeIngredient();
            }
        }
    };
    def recipeIngredientsList: ListView = ListView{
        layoutInfo: LayoutInfo{width: INGREDIENTS_WIDTH}
        layoutX: 5
        onMouseClicked: function(me){
            if(me.clickCount == DBL_CLICK){
                unIncludeIngredient();
            }
            else
            {
                quantityList.select(recipeIngredientsList.selectedIndex);
            }
        }
    };
    def removeAllBtn = Button{
        vpos: VPos.BOTTOM
        layoutX: 5
        text: "<<"
        action: function(){
            for(item in recipeIngredientsList.items){
                insert item into ingredientsList.items;
            }
            delete recipeIngredientsList.items;
        }
        disable: bind if(sizeof recipeIngredientsList.items == EMPTY_LIST)
            then true else false
    };
    def addAllBtn = Button{
        vpos: VPos.TOP
        layoutX: 5
        text: ">>"
        action: function(){
            for(item in ingredientsList.items){
                insert item into recipeIngredientsList.items;
            }
            delete ingredientsList.items;
        }
        disable: bind if(sizeof ingredientsList.items == EMPTY_LIST)
            then true else false
    };
    def removeBtn = Button{
        vpos: VPos.BOTTOM
        layoutX: 5
        text: "<"
        action: unIncludeIngredient
        disable: bind if(recipeIngredientsList.selectedIndex == NO_ITEM_SELECTED)
            then true else false
    };
    def addBtn = Button{
        vpos: VPos.TOP
        layoutX: 5
        text: ">"
        action: function(){
            includeIngredient();
        }
        disable: bind if(ingredientsList.selectedIndex == NO_ITEM_SELECTED)
            then true else false
    };
    def closeBtn = Button{
        text: "Close"
        action: function(){saveRecipeIngredients(); close();}
    };
    def editQuantityBtn = Button{
        hpos: HPos.RIGHT
        disable: bind
            if(recipeIngredientsList.selectedIndex > NO_ITEM_SELECTED){
                false;
            }
            else
            {
                true;
            }
        text: "Edit Quantity"
        action: showIngredientQuantityScreen
    };
    def ingredientsLbl = Label{text: "Ingredients"};
    def recipeIngredientsLbl = Label{text: "Included Ingredients"};
    
    def labelsLayout = HBox{content: [
        Container{
            layoutInfo: LayoutInfo{width: INGREDIENTS_WIDTH}
            content: ingredientsLbl
        },
        Container{id: "labelsSpace"},
        Container{
            layoutInfo: LayoutInfo{width: INGREDIENTS_WIDTH}
            content: recipeIngredientsLbl
        }]
    };
    def subLayout = HBox{
        spacing: 5
        content: [
        ingredientsList,
        Container{
            id: "btnLayout"
            height: ingredientsList.height
            content: VBox{
                layoutY: (ingredientsList.height / 2) - (removeAllBtn.height * 2)
                content: [removeAllBtn, addBtn, removeBtn, addAllBtn]
                spacing: 2
            }
        },
        recipeIngredientsList,
        VBox{
            content: [quantityList, editQuantityBtn]
        }]
    };
    def mainLayout = VBox{
        spacing: 5
        content: [
        labelsLayout,
        subLayout,
        HBox{
            content: [closeBtn] spacing: 5
            translateY: 25
        }
        ]
    };

    /**
     * Displays the ingredient quantity screen.
    */
    function showIngredientQuantityScreen(): Void{
        ingredientQuantityScreen.close();
        ingredientQuantityScreen = IngredientQuantityScreen{
            controller: controller
            ingredient: getCurrentRecipeIngredient();
            onIngredientQuantityChanged: function(iqse){
                updateQuantityList();
            }
        };
    }

    /**
     * Selects an existing recipe ingredient.
     @param ingredientName The name of the recipe ingredient to select
    */
    function selectRecipeIngredient(ingredientName: String): Void{
        for(ingredient in recipeIngredientsList.items){
            if(ingredientName.equals(ingredient)){
                recipeIngredientsList.select(indexof ingredient);
                quantityList.select(indexof ingredient);
                break;
            }
        }
    }

    /**
     * Moves an ingredient from the included ingredients list view to the recipe ingredients list view.
    */
    function includeIngredient(): Void{
        addRecipeIngredient();
        insert ingredientsList.items[ingredientsList.selectedIndex] into recipeIngredientsList.items;
        selectRecipeIngredient((ingredientsList.selectedItem as String));
        delete ingredientsList.items[ingredientsList.selectedIndex];
    }

    /**
     * Moves an recipe ingredient from the recipe ingredients list view to the ingredients list view.
    */
    function unIncludeIngredient(): Void{
        removeRecipeIngredient();
        insert recipeIngredientsList.items[recipeIngredientsList.selectedIndex] into ingredientsList.items;
        delete recipeIngredientsList.items[recipeIngredientsList.selectedIndex];
        delete quantityList.items[recipeIngredientsList.selectedIndex];
    }

    /**
     * Adds a new recipe ingredient.
    */
    function addRecipeIngredient(): Void{
        def DEFAULT_QUANTITY = 1.0;
        def DEFAULT_QUANTITY_UNIT = "item";
        def ingredient: Ingredient = (controller.performAction("Get ingredient", ingredientsList.items[ingredientsList.selectedIndex])[0] as Ingredient);
        def pk: RecipeIngredientPK = new RecipeIngredientPK(recipe.getRecipeid(), ingredient.getIngredientid());
        def recipeIngredient: RecipeIngredient = new RecipeIngredient(pk);
        def quantityUnit: QuantityUnit = (controller.performAction("Get quantity unit", DEFAULT_QUANTITY_UNIT)[0] as QuantityUnit);

        recipeIngredient.setQuantity(DEFAULT_QUANTITY);
        recipeIngredient.setQuantityunit(quantityUnit);
        recipeIngredient.setIngredient1(ingredient);
        recipeIngredient.setRecipe1(recipe);
        recipe.getRecipeIngredientCollection().add(recipeIngredient);
        updateQuantityList();
    }

    /**
     * Removes an existing recipe ingredient.
    */
    function removeRecipeIngredient(): Void{
        def iter: Iterator = recipe.getRecipeIngredientCollection().iterator();

        while(iter.hasNext()){
            def ingredient: RecipeIngredient = (iter.next() as RecipeIngredient);

            if(ingredient.getIngredient1().getName().equals(recipeIngredientsList.items[recipeIngredientsList.selectedIndex])){
                recipe.getRecipeIngredientCollection().remove(ingredient);
                break;
            }
        }
    }

    /**
     * Gets the currently selected recipe ingredient.
     @return A recipe ingredient if one has been selected.
    */
    function getCurrentRecipeIngredient(): RecipeIngredient{
        def recipeIngredients: RecipeIngredient[] = (controller.performAction("Convert recipe ingredient collection to array", recipe.getRecipeIngredientCollection()) as RecipeIngredient[]);
        def recipeIngredient = recipeIngredientsList.items[recipeIngredientsList.selectedIndex];
        var result: RecipeIngredient;

        for(ingredient in recipeIngredients){
            if(ingredient.getIngredient1().getName().equals(recipeIngredient)){
                result = ingredient;
                break;
            }
        }
        return result;
    }

    /**
     * Saves changes to recipe ingredients.
    */
    function saveRecipeIngredients(): Void{
        controller.performAction("Update record", recipe);
        onRecipeIngredientsChanged(RecipeIngredientsScreenEvent{});
        this.close();
    }

    /**
     * Updates the recipe ingredients list with the current recipe ingredients.
    */
    function updateRecipeIngredientsList(){
        def recipeIngredients: RecipeIngredient[] = (controller.performAction("Convert recipe ingredient collection to array", recipe.getRecipeIngredientCollection()) as RecipeIngredient[]);

        if(recipeIngredients != null){
            recipeIngredientsList.items =
            for(ingredient in recipeIngredients){
                ingredient.getIngredient1().getName();
            };
        }
    }

    /**
     * Updates the quantity list with the quantities
     * (the amount and the quantity unit) of the current recipe ingredients.
    */
    function updateQuantityList(): Void{
        def recipeIngredients: RecipeIngredient[] = (controller.performAction("Convert recipe ingredient collection to array", recipe.getRecipeIngredientCollection()) as RecipeIngredient[]);

        quantityList.items = for(ingredient in recipeIngredients){
            "{ingredient.getQuantity()} {ingredient.getQuantityunit().getName()}"
        }
    }

    /**
     * Updates the ingredients list with all the ingredients that are not used in the recipe.
    */
    function updateIngredientsList(){
        ingredientsList.items =
        for(ingredient in (controller.performAction("All ingredients", null) as Ingredient[])){
            ingredient.getName();
        }
    }

    /**
     * Adds an ingredient to the ingredients list.
     @param ingredient The existing ingredient to add to the list.
    */
    public function addIngredient(ingredient: Ingredient): Void{
        insert ingredient.getName() into ingredientsList.items;
        ingredientsList.items = sortSequence((ingredientsList.items) as String[]);
    }

    /**
     * Removes an existing ingredient from the recipe ingredients and ingredients lists.
    */
    public function removeIngredient(ingredient: Ingredient){
        delete ingredient.getName() from recipeIngredientsList.items;
        delete ingredient.getName() from ingredientsList.items;
    }

    /**
     * Sorts the String items in the sequence in ascending order by name.
    */
    public function sortSequence(items: String[]): String[]{
        var result: String[] = [];

        result = (Sequences.sort(items, Sorter{}) as String[]);
        return result;
    }

    init{
        def PADDING = 10;
        def btnLayout = subLayout.lookup("btnLayout");
        def labelsSpace = labelsLayout.lookup("labelsSpace");

        title = "Ingredients For {if(recipe.getName() != null) then
            recipe.getName() else "New Recipe"}";
        scene = Scene{
            content: [mainLayout]
        };
        onClose = function(){
            ingredientQuantityScreen.close();
            controller.closeScreen("Recipe Ingredients");
        };
        ingredientsLbl.layoutX = (INGREDIENTS_WIDTH / 2) - (ingredientsLbl.width / 2);
        recipeIngredientsLbl.layoutX = (INGREDIENTS_WIDTH / 2) - (recipeIngredientsLbl.width / 2);
        labelsSpace.layoutInfo = LayoutInfo{width: btnLayout.layoutBounds.width + PADDING};
    }

    postinit{
        def PADDING = 15;
        def btnLayout = subLayout.lookup("btnLayout");

        updateRecipeIngredientsList();
        updateIngredientsList();
        ingredientsList.items = sortSequence((ingredientsList.items as String[]));
        updateQuantityList();
        quantityListXPos = ingredientsList.width +
            btnLayout.layoutBounds.width + INGREDIENTS_WIDTH +
            PADDING;
    }
}

/**
 * An event that represents any changes that have been made on the recipe ingredients screen.
*/
public class RecipeIngredientsScreenEvent{
    public def changedRecipe: Recipe = recipe;
}

