/// <reference path="../Controls.ts" />
/// <reference path="../Classes/StoredMemory.ts" />
/// <reference path="../../Elements/Elements.ts" />
interface KeyCode {
    evCode : string;
    representation : string;
    name : string;
}

module Controls.KeyHandler {
    let promiseStack : Array<(value?: KeyCode | PromiseLike<KeyCode>) => void> = [];
    let promiseOriginalStack : Array<Promise<KeyCode>> = [];

    export function rejectPromise (promise : (value?: KeyCode | PromiseLike<KeyCode>) => void) {
        promiseStack.splice(promiseStack.indexOf(promise), 1);
        promiseOriginalStack.splice(promiseStack.indexOf(promise), 1);
    }

    export function createKeyCode (ev : KeyboardEvent) {
        let name = ev.key.toUpperCase();
        let representation = name;
        let evCode = ev.code;
        if (ev.shiftKey) {
            name = "Shift + " + name;
            evCode = "Sh" + evCode;
            representation = "S" + representation;
        }
        if (ev.altKey) {
            name = "Alt + " + name;
            evCode = "Al" + evCode;
            representation = "A" + representation;
        }
        if (ev.ctrlKey) {
            name = "Control + " + name;
            evCode = "Ct" + evCode;
            representation = "C" + representation;
        }
        let keyCode : KeyCode =  {
            evCode : evCode,
            name : name,
            representation : representation
        };
        return keyCode;
    }

    window.addEventListener("mousedown", function (event) {
        let keyCode : KeyCode = {
            representation : "Click",
            evCode : "Click",
            name : "Click"
        };

        let promise = promiseStack.shift();
        if (promise != undefined) {
            promise(keyCode);
            promiseOriginalStack.shift();
            return;
        }
    });

    window.addEventListener("keydown", function (event) {
        //event.preventDefault(); This fucks up browser control. Let's just assume that this is always going to be the last thing to catch a key.
        let keyCode = createKeyCode(event);

        let promise = promiseStack.shift();
        if (promise != undefined) {
            promise(keyCode);
            promiseOriginalStack.shift();
            return;
        }

        if (keyCode.evCode == scrollKeyCode.getValue().evCode) {
            Elements.CurrentTurnHandler.scrollSpace();
            return;
        } else if (keyCode.evCode == imageKeyCode.getValue().evCode) {
            if (SayImage.imageViewer.style.display == "block") {
                SayImage.imageViewer.style.display = "none";
            } else {
                let images = Elements.CurrentTurnHandler.currentTurnTab.getElementsByClassName("contentImage");
                if (images.length > 0) {
                    (<HTMLElement> images[images.length - 1]).click();
                }
            }
            return;
        }

        let ele;

        Elements.getLinkElements().some((element : HTMLElement) => {
            let keyables = element.getElementsByClassName("keyable");
            for (let i = 0; i < keyables.length; i++) {
                if ((<HTMLElement> keyables[i]).dataset['shortcutcode'] == keyCode.evCode) {
                    ele = keyables[i];
                    return true;
                }
            }
            return false;
        });

        if (ele != undefined) {
            ele.click();
        }
    });

    export function createKeyCodes (codes : Array<string>, keys : Array<string>, mods? : Array<boolean>) {
        let arr = [];
        for (let mod = 0; mod < 4; mod++) {
            if (mod == 0 || mods[mod] == true) {
                for (let i = 0; i < codes.length; i++) {
                    let name = keys[i];
                    let representation = keys[i];
                    let evCode = codes[i];
                    if (mod == 1) {
                        name = "Shift + " + name;
                        evCode = "Sh" + evCode;
                        representation = "S" + representation;
                    }
                    if (mod == 2) {
                        name = "Alt + " + name;
                        evCode = "Al" + evCode;
                        representation = "A" + representation;
                    }
                    if (mod == 3) {
                        name = "Control + " + name;
                        evCode = "Ct" + evCode;
                        representation = "C" + representation;
                    }
                    let keyCode: KeyCode = {
                        evCode: evCode,
                        representation: representation,
                        name : name
                    };
                    arr.push(keyCode);
                }
            }
        }
        return arr;
    }

    export var scrollKeyCode = new StoredMemory("scrollKeyCode", <KeyCode> {
        evCode : "Space",
        representation : "Space"
    });

    export var imageKeyCode = new StoredMemory("imageKeyCode", <KeyCode> {
        evCode : "KeyI",
        representation : "I"
    });

    export var keyCodes1 = new StoredMemory(
        "keyCodesRoom",
        createKeyCodes( ["Digit1", "Digit2", "Digit3", "Digit4", "Digit5"],
                         ["1", "2", "3", "4", "5"],
                        [true, true, true, true])
    );

    export var keyCodes2 = new StoredMemory(
        "keyCodesActions",
        createKeyCodes( ["KeyQ", "KeyW", "KeyE", "KeyR", "KeyT"],
            ["Q", "W", "E", "R", "T"],
            [true, true, true, true])
    );



    export var keyCodes3 = new StoredMemory(
        "keyCodesGlobal",
        createKeyCodes( ["KeyF", "KeyV", "KeyG", "KeyB"],
            ["F", "V", "G", "B"],
            [true, true, true, true])
    );

    export var keyCodeNorth = new StoredMemory(
        "keyCodeNorth",
        <KeyCode> {
            evCode : "KeyS",
            representation : "S"
        }
    );

    export var keyCodeEast = new StoredMemory(
        "keyCodeEast",
        <KeyCode> {
            evCode : "KeyC",
            representation : "C"
        }
    );

    export var keyCodeSouth = new StoredMemory(
        "keyCodeSouth",
        <KeyCode> {
            evCode : "KeyX",
            representation : "X"
        }
    );

    export var keyCodeWest = new StoredMemory(
        "keyCodeWest",
        <KeyCode> {
            evCode : "KeyZ",
            representation : "Z"
        }
    );

    export var keyCodeUp = new StoredMemory(
        "keyCodeUp",
        <KeyCode> {
            evCode : "KeyA",
            representation : "A"
        }
    );

    export var keyCodeDown = new StoredMemory(
        "keyCodeDown",
        <KeyCode> {
            evCode : "KeyD",
            representation : "D"
        }
    );

    // TODO: Make this safer by creating it using Room.DIRECTIONS.
    var directionCodeByIndex = [keyCodeNorth, undefined, keyCodeEast, undefined,
        keyCodeSouth, undefined, keyCodeWest, undefined, keyCodeUp, keyCodeDown];

    export function getDirectionCode (direction : Direction) {
        return getDirectionCodeByIndex(Room.DIRECTIONS.indexOf(direction));
    }

    export function getDirectionCodeByIndex (index : number) {
        return directionCodeByIndex[index].getValue();
    }

    export var available1 : Array<KeyCode> = [];
    export var available2 : Array<KeyCode> = [];
    export var available3 : Array<KeyCode> = [];

    export function reset () {
        available1 = keyCodes1.getValue().slice();
        available2 = keyCodes2.getValue().slice();
        available3 = keyCodes3.getValue().slice();
    }

    let showKeys = new StoredMemory("ShowKeys", !Elements.isMobile);

    export function applyCode (element : HTMLElement, code : KeyCode) {
        if (code == undefined) {
            delete(element.dataset['shortcut']);
            delete(element.dataset['shortcutcode']);
            element.classList.remove("keyable");
        } else {
            element.dataset['shortcutcode'] = code.evCode;
            if (showKeys.getValue()) {
                element.dataset['shortcut'] = code.representation;
            }
            element.classList.add("keyable");
        }
    }

    export function getFirstKeyCode () {
        let chosen = available1.length > 0 ? available1 :
            available2.length > 0 ? available2 :
                available3.length > 0 ? available3 : undefined;
        if (chosen != undefined) {
            return chosen.shift();
        }
        return undefined;
    }

    export function getSecondKeyCode () {
        let chosen = available2.length > 0 ? available2 :
            available3.length > 0 ? available3 : undefined;
        if (chosen != undefined) {
            return chosen.splice(0, 1)[0];
        }
        return undefined;
    }

    export function getThirdKeyCode () {
        let chosen = available3.length > 0 ? available3 : undefined;
        if (chosen != undefined) {
            return chosen.splice(0, 1)[0];
        }
        return undefined;
    }

    /**
     * Returns a promise of a key press.
     * If after timeout seconds a key was not pressed, the promise fails.
     * @param {number} timeout
     */
    export function getNextKey (timeout? : number) : Promise<KeyCode> {
        let timeoutNumber;
        let promise = <Promise<KeyCode>> (new Promise((resolve, reject) => {
            promiseStack.push(resolve);

            if (timeout != undefined) {
                timeoutNumber = setTimeout(() => {
                    KeyHandler.rejectPromise(resolve);
                    reject();
                }, timeout * 1000);
            }
        }));

        promise.then(() => {
            clearTimeout(timeoutNumber);
        });

        return promise;
    }

    export function stopGivingNextKey (originalPromise : Promise<KeyCode>) {
        let index = promiseOriginalStack.indexOf(originalPromise);
        if (index != -1) {
            rejectPromise(promiseStack[index]);
        }
    }
}