/// <reference path="../Thing.ts" />
/// <reference path="../../../Elements/Classes/Say/OneOf.ts" />
/// <reference path="../Rulebook.ts" />
/// <reference path="../Rule.ts" />
interface LiquidOptions extends ThingOptions {
    taste? : OneOf;
}

interface LiquidContainer {
    liquidContents : Array<Liquid>;
}

interface LiquidMixture {
    quantities : Map<any, number>;
    result : Liquid;
    quantityMultiplier? : number;
    firstPriority : number;
    priority : number;
}

class Liquid extends Thing {
    public taste : OneOf;
    public constructor (options? : LiquidOptions) {
        super(options);
        options = options == undefined ? {} : options;
        if (options.taste != undefined) {
            this.taste = options.taste;
        } else {
            this.taste = new OneOf(
                OneOf.ROTATING_RANDOM,
                "It goes easily through your throat with no particular taste.",
                "You taste nothing as it softly slides through your tongue."
            );
        }
    }

    /**
     * This rulebook will return a LiquidMixture
     * @type {Rulebook<LiquidContainer>}
     */
    public static rulebookMixing = new Rulebook<LiquidContainer>("Mixing the liquid contents of something"); // noun = LiquidContainer
    public static mixtures : Array<LiquidMixture> = [];
    public static sortedMixtures = false;

    public static async mix (container : LiquidContainer) {
        let result = await Liquid.rulebookMixing.execute({noun : container});
        // Did we find a liquid?
        if (result != undefined) {
            let finalQuantity = result.quantityMultiplier != undefined ?
                                    (result.quantityMultiplier * container.liquidContents.length)
                                    :
                                    (container.liquidContents.length);


            container.liquidContents = new Array(finalQuantity);
            for (let i = 0; i < finalQuantity; i++) {
                container.liquidContents[i] = result.result;
            }
        }
    }

    public static getMixtures () {
        if (Liquid.sortedMixtures) {
            return Liquid.mixtures;
        }
        Liquid.mixtures.sort((a, b) => {
            if (b.firstPriority < a.firstPriority) return -1;
            if (a.firstPriority < b.firstPriority) return 1;
            if (b.priority < a.priority) return -1;
            if (a.priority < b.priority) return 1;
            return 0;
        });
        Liquid.sortedMixtures = true;
        return Liquid.mixtures;
    }

    public static addMixture(mixture : LiquidMixture) {
        Liquid.mixtures.push(mixture);
        Liquid.sortedMixtures = false;
    }

    public static ruleDefaultMixing = Liquid.rulebookMixing.createAndAddRule({
        name : "Mixing through Mixtures",
        code : runner => {
            if (runner.noun.liquidContents.length == 0) {
                return;
            }

            let mixture : LiquidMixture;
            for (let i = 0; i < Liquid.mixtures.length; i++) {
                mixture = Liquid.mixtures[i];
                let proportions = [];
                let mixtureIterator = mixture.quantities.entries();
                for (let mixtureLiquid = mixtureIterator.next(); !mixtureLiquid.done; mixtureLiquid = mixtureIterator.next()) {
                    let mixtureType = mixtureLiquid.value[0];
                    let mixtureQuantity = mixtureLiquid.value[1];
                    let matchedQuantity = 0;
                    runner.noun.liquidContents.forEach((liquidType) => {
                        try {
                            if (liquidType == mixtureType ||
                                (typeof mixtureType == "function" &&
                                    (liquidType instanceof mixtureType || mixtureType(liquidType))
                                )
                            ) {
                                matchedQuantity++;
                            }
                            // mixtureType is not always callable
                        } catch (e) {}
                    });

                    proportions.push(matchedQuantity / mixtureQuantity);
                }

                // If this is a valid mix, then every part of the recipe is represented proportionally
                if (proportions.every(function(element, index, array) {
                        return element === array[0];
                    })) {
                    return mixture;
                }
            }
        }
    });
}

// class Semen extends Liquid {}
// class OrcSemen extends Semen {}
// class MinotaurSemen extends Semen {}
// class PowerfulDrink extends Liquid {}
//
// let powerfulDrinkQuantities = new Map();
// powerfulDrinkQuantities.set(OrcSemen, 2);
// powerfulDrinkQuantities.set(MinotaurSemen, 1);
// Liquid.addMixture({
//     firstPriority : Rule.PRIORITY_HIGHEST,
//     priority : Rule.PRIORITY_MEDIUM,
//     quantityMultiplier : 0.5,
//     result : new PowerfulDrink("Powerful Drink"),
//     quantities : powerfulDrinkQuantities
// });
// let recipient = new Recipient("Cup");
// recipient.liquidContents.push(new OrcSemen("Semen of the Chief"));
// recipient.liquidContents.push(new OrcSemen("Semen of the Chief"));
// recipient.liquidContents.push(new OrcSemen("Semen of the Chief"));
// recipient.liquidContents.push(new OrcSemen("Semen of the Chief"));
// recipient.liquidContents.push(new MinotaurSemen("Semen of Harold"));
// recipient.liquidContents.push(new MinotaurSemen("Semen of Harold"));
//
// {
//     Settings.setDebug(false);
//     let t1 = performance.now();
//     let t0 = performance.now();
//     Liquid.mix(recipient);
//     t1 = performance.now();
//     console.log(t1 - t0);
// }