/// <reference path="../Settings.ts" />
interface RuleOptions<T> {
    name : string;
    firstPriority? : number;
    priority? : number;
    code : (runner? : RulebookRunner<T>) => (Promise<any> | any);
    conditions? : (runner? : RulebookRunner<T>) => (boolean);
}

class Rule {
    private _priority : number = 0;
    public firstPriority : number = 0;
    public name : string;

    private code : Function;
    private createdWhere : Error;
    private conditions : (rulebook? : RulebookRunner<any>) => (boolean);

    public constructor (options : RuleOptions<any>) {
        this.priority = options.priority != undefined ? options.priority : Rule.PRIORITY_MEDIUM;
        this.firstPriority = options.firstPriority != undefined ? options.firstPriority : Rule.PRIORITY_MEDIUM;
        this.name = options.name;
        this.code = options.code;
        this.createdWhere = (new Error());

        this.conditions = options.conditions != undefined ? options.conditions : () => { return true; };
    }

    public async execute (rulebook? : RulebookRunner<any>) : Promise<any> {
        if (!this.conditions(rulebook)) {
            return;
        }
        console.debug(Rulebook.getIndentation() + "[RULE] " + this.name);
        Settings.hardDebug && console.debug(this.name, this.createdWhere);
        Rulebook.increaseIndentation(this);

        rulebook.rule = this;
        let result = this.code(rulebook);
        // Was the function async?
        if (result instanceof Promise) {
            result = await result;
        }

        if (result != undefined) {
            console.debug(Rulebook.getIndentation() + "Result:", result);
        }

        Rulebook.decreaseIndentation();

        return result;
    }

    get priority(): number {
        return this._priority;
    }

    set priority(value: number) {
        this._priority = value;
    }

    public compareTo (b : Rule) {
        var a = this;
        // Higher priority goes first as this is a rolling array
        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;
    }

    public static PRIORITY_HIGHEST : number = 20;
    public static PRIORITY_HIGH : number = 15;
    public static PRIORITY_MEDIUM : number = 10;
    public static PRIORITY_LOW : number = 5;
    public static PRIORITY_LOWEST : number = 0;
}

// var goblinJumpingRule = new Rule({
//     name : "Goblin Jumping Rule",
//     code : async (rule : Rule, ruleResolver : Function) => {
//         console.debug("weee");
//     }
// });
//
// goblinJumpingRule.execute();

/**
 var goblinJumpingRule = new Rule({name : "Goblin Jumping Rule", code : (function () {
    return function () {
        // this is now the Rule and has access to this.currentRulebook
        // If there is no need for own module, it's okay to use a simple function
        // IF YOUR FUNCTION SHOULD NOT COMPLETE AUTOMATICALLY, IT NEEDS TO RETURN FALSE
        // FUNCTIONS THAT RETURN ANYTHING ELSE WILL AUTO COMPLETE AT THE END OF THEIR CODE

        // If your rule does not auto-complete, then it must complete manually, or the game will STALL.

        // If a rule must return either "true" or "false" to the rulebook, you need to set the rule's result that way:
        // this.result = true (or false)
    };
 })()});
 */