/**
* Copyright (c) 2016 Aleksi 'Allexit' Talarmo
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var Std;
(function (Std) {
    class IO {
        constructor(terminal) {
            this.mousePos = { x: -1, y: -1 };
            this.listeners = {};
            this.mouseListeners = {};
            $(document).keypress(evt => {
                Object.getOwnPropertyNames(this.listeners).forEach(id => {
                    this.listeners[parseInt(id)].handleKeypress(evt);
                });
            });
            $(document).keydown(evt => {
                Object.getOwnPropertyNames(this.listeners).forEach(id => {
                    this.listeners[parseInt(id)].handleKeydown(evt);
                });
            });
            $(document).keyup(evt => {
                Object.getOwnPropertyNames(this.listeners).forEach(id => {
                    this.listeners[parseInt(id)].handleKeyup(evt);
                });
            });
            let termElem = $(terminal.selector);
            $(terminal.selector).mousemove(evt => {
                let term = translatePageToTerminal(termElem, terminal, evt.pageX, evt.pageY);
                let x = term[0];
                let y = term[1];
                let changed = x != this.mousePos.x || y != this.mousePos.y;
                if (changed) {
                    let mousePos = { x: x, y: y };
                    Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
                        this.mouseListeners[parseInt(id)].mousemove(this.mousePos, mousePos);
                    });
                    this.mousePos = mousePos;
                }
            });
            $(terminal.selector).mouseleave(evt => {
                if (this.mousePos.x != -1) {
                    Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
                        this.mouseListeners[parseInt(id)].mousemove(this.mousePos, { x: -1, y: -1 });
                    });
                    this.mousePos = { x: -1, y: -1 };
                }
            });
            $(terminal.selector).mousedown(evt => {
                let term = translatePageToTerminal(termElem, terminal, evt.pageX, evt.pageY);
                Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
                    this.mouseListeners[parseInt(id)].mousedown(evt.button, term[0], term[1]);
                });
            });
            $(terminal.selector).mouseup(evt => {
                let term = translatePageToTerminal(termElem, terminal, evt.pageX, evt.pageY);
                Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
                    this.mouseListeners[parseInt(id)].mouseup(evt.button, term[0], term[1]);
                });
            });
            $(terminal.selector).click(evt => {
                let term = translatePageToTerminal(termElem, terminal, evt.pageX, evt.pageY);
                Object.getOwnPropertyNames(this.mouseListeners).forEach(id => {
                    this.mouseListeners[parseInt(id)].click(evt.button, term[0], term[1]);
                });
            });
        }
        /**
        * Add a listener which then receives all key-events.
        * Returns the ID of the listener.
        */
        addListener(listener) {
            let id = this.listenerCounter++;
            this.listeners[id] = listener;
            listener.onListen(id);
            return id;
        }
        /**
        * Remove listener with the specified ID.
        */
        removeListener(id) {
            delete this.listeners[id];
        }
        /**
        * Add a mouse listener which then receives all mouse-events.
        * Returns the ID of the mouse listener.
        */
        addMouseListener(listener) {
            let id = this.mouseListenerCounter++;
            this.mouseListeners[id] = listener;
            listener.onMouseListen(id);
            return id;
        }
        /**
        * Remove mouse listener with the specified ID.
        */
        removeMouseListener(id) {
            delete this.mouseListeners[id];
        }
        /**
        * Set standard output channel.
        * Used when calling print, println, clear and refresh.
        */
        setOutput(output) {
            this.output = output;
        }
        /**
        * Set standard input channel
        * Used when calling readline.
        */
        setInput(input) {
            this.input = input;
        }
        /**
        * Set standard audio output channel
        * Used when calling playSound, stopSound, and loadSound
        */
        setAudioOutput(audioOutput) {
            this.audioOutput = audioOutput;
        }
        /**
        * Calls print for the assigned IO interface.
        * Standard IO prints out a line to the cursor's last location
        */
        print(text, style) {
            if (!this.output) {
                return;
            }
            this.output.print(text, style);
        }
        /**
        * Calls println for the assigned IO interface.
        * Standard IO prints out a line to a new line relatively to the cursor's last location.
        */
        println(text, style) {
            if (!this.output) {
                return;
            }
            this.output.println(text, style);
        }
        /**
        * Calls refresh for the assigned IO interface.
        * Standard IO refreshes the IO buffer so it can be drawn and then draws this.
        * This last step can be avouded though with the boolean parameter.
        */
        refresh(alsoRefreshTerminal = true) {
            if (!this.output) {
                return;
            }
            this.output.refresh(alsoRefreshTerminal);
        }
        /**
        * Calls clear for the assigned IO interface.
        * Standard IO clears the (bash and terminal) screen.
        */
        clear(style) {
            if (!this.output) {
                return;
            }
            this.output.clear(style);
        }
        /**
        * Calls readline for the assigned IO inteface
        * Sandard IO starts taking input and calls the callback with a string it readed while printing it out.
        * Note: Some IO interfaces might not implement this method. Only use it
        * When using stdio
        */
        readline(readline) {
            if (!this.input) {
                return;
            }
            this.input.readline(readline);
        }
        /**
        * Calls play for the assigned AudioOutput interface.
        * Jukebox (Standerd IO) starts playing a sound in the given channel. If no channel is given, channel 0.
        * Note: Some IO interfaces might not implement this method.
        */
        playSound(channel, sound) {
            if (!this.audioOutput) {
                return;
            }
            this.audioOutput.play(channel, sound);
        }
        /**
        * Calls pause for the assigned AudioOutput interface.
        * Jukebox (Standerd IO) pauses the sound at the specified channel. Sound can be resumed with stdio.play(channel).
        * Note: Some IO interfaces might not implement this method.
        */
        pauseSound(channel) {
            if (!this.audioOutput) {
                return;
            }
            this.audioOutput.pause(channel);
        }
        /**
        * Calls stop for the assigned AudioOutput interface.
        * Jukebox (Standerd IO) stops the current sound at given channel. If no channel is given, channel 0.
        * Note: Some IO interfaces might not implement this method.
        */
        stopSound(channel) {
            if (!this.audioOutput) {
                return;
            }
            this.audioOutput.stop(channel);
        }
        /**
        * Calls stop for the assigned AudioOutput interface.
        * Jukebox (Standerd IO) loads the given sound and returns a refrence string, which points to a loaded sound.
        * Note: Some IO interfaces might not implement this method. If AudioOutput-interface not specified (or Jukebox launched), it will return an empty string.
        */
        loadSound(path, onready) {
            if (!this.audioOutput) {
                return "";
            }
            return this.audioOutput.loadSound(path, onready);
        }
    }
    Std.IO = IO;
    function translatePageToTerminal(termElem, terminal, x, y) {
        let cellW = termElem.outerWidth() / terminal.width; // Portional width of a single cell;
        let cellH = termElem.outerHeight() / terminal.height; // Portional height of a single cell;
        let termX = Math.floor((x - termElem.offset().left) / cellW);
        let termY = Math.floor((y - termElem.offset().top) / cellH);
        return [termX, termY];
    }
})(Std || (Std = {}));
