//  HULU CONFIDENTIAL MATERIAL. DO NOT DISTRIBUTE.
//  Copyright (C) 2009-2010 Hulu, LLC
//  All Rights Reserved
/**
 *
 * htvControls2.js
 *
 * Defines non-specific controls (i.e. ImageControl) and user text input.
 */
/*jslint maxerr: 1000, nomen: false, evil: false, immed: true, plusplus: false */
/*global LOG, describe */
var $htv;

$htv.Controls.Keyboard = function () {
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.width = 0;
    this.height = 0;
    this.node = null;
    this.activeRow = 0;
    this.activeCol = 0;
    this.className = "Keyboard";
    this.keys = null;
    this.keyPressActions = {};
    this.spaceKey = null;
    this.symbolsKey = null;
    this.shift = false;
    this.symbols = false;
    this.canWrap = false;
    
    this.initialize = function (position, target, url, options) {
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        this.node = target;
        
        this.render(options);
    };
    
    this.render = function (options) {
        var r, c, i, key, letter, letters, spaceX;

        this.keys = [];

        // Alphanumerics
        letters = "abcdefghijklmnopqrstuvwxyz0123456789";
        for (i = 0; i < letters.length; i++) {
            r = Math.floor(i / 9);
            c = i % 9;
            if (r >= this.keys.length) {
                this.keys[r] = [];
            }
            letter = letters.charAt(i);
            key = $htv.ControlPool.getObject("ButtonControl");
            key.initialize({
                x: this.x + c * 47,
                y: this.y + r * 36,
                z: this.z,
                width: 42,
                height: 32
            }, null, null, {
                text: letter,
                focused_style: $htv.Styles.KeyboardKeyFocused,
                unfocused_style: $htv.Styles.KeyboardKeyUnfocused,
                focused_image: "images/btn-keyboard-green.jpg",
                unfocused_image: "images/btn-keyboard.jpg"
            });
            key.eventData = {
                type: "LETTER",
                letter: letter
            };
            this.keys[r][c] = key;
        }
        // Shift
        this.keys[4] = [];
        key = $htv.ControlPool.getObject("ButtonControl");
        key.initialize({
            x: this.x + 0 * 47,
            y: this.y + 4 * 36,
            z: this.z,
            width: 90,
            height: 32
        }, null, null, {
            text: "shift",
            focused_style: $htv.Styles.KeyboardKeyFocused,
            unfocused_style: $htv.Styles.KeyboardKeyUnfocusedDarkBackground,
            focused_image: "images/btn-keyboard-med-green.jpg",
            unfocused_image: "images/btn-keyboard-med-grey.jpg"
        });
        key.eventData =  {
            type: "SHIFT"
        };
        this.keys[4][0] = key;
        this.keys[4][1] = key;
        // Left
        if (!options.hasOwnProperty("login_setup")) {
            key = $htv.ControlPool.getObject("ButtonControl");
            key.initialize({
                x: this.x + 2 * 47,
                y: this.y + 4 * 36,
                z: this.z,
                width: 42,
                height: 32
            }, null, null, {
                text: "<",
                focused_style: $htv.Styles.KeyboardKeyFocused,
                unfocused_style: $htv.Styles.KeyboardKeyUnfocusedDarkBackground,
                focused_image: "images/btn-keyboard-green.jpg",
                unfocused_image: "images/btn-keyboard-grey.jpg"
            });
            key.eventData =  {
                type: "LEFT"
            };
            this.keys[4][2] = key;
        }
        
        // Space
        key = $htv.ControlPool.getObject("ButtonControl");
        this.spaceKey = key;       
        
        if (options.hasOwnProperty("login_setup")) {        
            spaceX = 2;
        }
        else {
            spaceX = 3;
        }

        key.initialize({
            x: this.x + spaceX * 47,
            y: this.y + 4 * 36,
            z: this.z,
            width: 137,
            height: 32
        }, null, null, {
            text: "space",
            focused_style: $htv.Styles.KeyboardKeyFocused,
            unfocused_style: $htv.Styles.KeyboardKeyUnfocused,
            focused_image: "images/btn-keyboard-long-green.jpg",
            unfocused_image: "images/btn-keyboard-long-white.jpg"
        });
        key.eventData =  {
            type: "LETTER",
            letter: " "
        };
        this.keys[4][spaceX] = key;
        this.keys[4][spaceX + 1] = key;
        this.keys[4][spaceX + 2] = key;        
        
        // Right
        if (!options.hasOwnProperty("login_setup")) {
            key = $htv.ControlPool.getObject("ButtonControl");
            key.initialize({
                x: this.x + 6 * 47,
                y: this.y + 4 * 36,
                z: this.z,
                width: 42,
                height: 32
            }, null, null, {
                text: ">",
                focused_style: $htv.Styles.KeyboardKeyFocused,
                unfocused_style: $htv.Styles.KeyboardKeyUnfocusedDarkBackground,
                focused_image: "images/btn-keyboard-green.jpg",
                unfocused_image: "images/btn-keyboard-grey.jpg"
            });
            key.eventData =  {
                type: "RIGHT"
            };
            this.keys[4][6] = key;
        }
       
        // Symbols 
        if (options.hasOwnProperty("login_setup")) {
            key = $htv.ControlPool.getObject("ButtonControl");
            this.symbolsKey = key;
            key.initialize({
                x: this.x + 5 * 47,
                y: this.y + 4 * 36,
                z: this.z,
                width: 90,
                height: 32
            }, null, null, {
                text: "@#$",
                focused_style: $htv.Styles.KeyboardKeyFocused,
                unfocused_style: $htv.Styles.KeyboardKeyUnfocusedDarkBackground,
                focused_image: "images/btn-keyboard-med-green.jpg",
                unfocused_image: "images/btn-keyboard-med-grey.jpg"
            });
            key.eventData =  {
                type: "SYMBOLS"
            };
            this.keys[4][5] = key;
            this.keys[4][6] = key;
        }

        // Backspace
        key = $htv.ControlPool.getObject("ButtonControl");
        key.initialize({
            x: this.x + 7 * 47,
            y: this.y + 4 * 36,
            z: this.z,
            width: 90,
            height: 32
        }, null, null, {
            text: "backsp",
            focused_style: $htv.Styles.KeyboardKeyFocused,
            unfocused_style: $htv.Styles.KeyboardKeyUnfocusedDarkBackground,
            focused_image: "images/btn-keyboard-med-green.jpg",
            unfocused_image: "images/btn-keyboard-med-grey.jpg"
        });
        key.eventData =  {
            type: "BACKSPACE"
        };
        this.keys[4][7] = key;
        this.keys[4][8] = key;
        
        // Search
        if (options.hasOwnProperty("go_label")) {
            this.keys[5] = [];
            key = $htv.ControlPool.getObject("ButtonControl");
            key.initialize({
                x: this.x + 2 * 47,
                y: this.y + 5 * 36,
                z: this.z,
                width: 231,
                height: 32
            }, null, null, {
                text: options.go_label,
                focused_style: $htv.Styles.KeyboardKeyFocused,
                unfocused_style: $htv.Styles.KeyboardKeyUnfocused,
                focused_image: "images/btn-keyboard-search-green.jpg",
                unfocused_image: "images/btn-keyboard-search-white.jpg"
            });
            key.eventData =  {
                type: "SUBMIT"
            };
            for (c = 0; c < this.keys[0].length; c++) {
                this.keys[5][c] = key;
            }
        }

        this.reset();
        this.setOptions(options);
    };
   
    this.setOptions = function (options) {
        if (options.hasOwnProperty("login_setup")) {
            if (options.login_setup === "password") {
                this.spaceKey.setText("space");
                this.spaceKey.eventData.letter = " ";
            }
            else {
                this.spaceKey.setText(".com");
                this.spaceKey.eventData.letter = ".com";
            }
        }
        if (options.hasOwnProperty("can_wrap")) {
            this.canWrap = options.can_wrap;
        }
    };

    this.activateKey = function (r, c) {
        this.deactivateKey(this.activeRow, this.activeCol);
        this.activeRow = r;
        this.activeCol = c;
        this.keys[r][c].focus();
    };
    
    this.deactivateKey = function (r, c) {
        this.keys[r][c].unfocus();
    };
    
    this.getActiveKey = function () {
        return this.keys[this.activeRow][this.activeCol];
    };
    
    this.hide = function () {
        var last, r, c;
        for (r = 0; r < this.keys.length; r++) {
            for (c = 0; c < this.keys[r].length; c++) {
                if (this.keys[r][c] !== last) {
                    this.keys[r][c].hide();
                    last = this.keys[r][c];
                }
            }
        }
    };
    
    this.show = function () {
        var last, r, c;
        for (r = 0; r < this.keys.length; r++) {
            for (c = 0; c < this.keys[r].length; c++) {
                if (this.keys[r][c] !== last) {
                    this.keys[r][c].show();
                    last = this.keys[r][c];
                }
            }
        }
    };
    
    this.focus = function (direction) {
        if (direction === undefined) {
            this.activateKey(this.activeRow, this.activeCol);
        }
        else {
            switch (direction) {
            case "left":
                this.activateKey(this.activeRow, this.keys[this.activeRow].length - 1);
                break;
            case "right":
                this.activateKey(this.activeRow, 0);
                break;
            case "up":
                this.activateKey(this.keys.length - 1, this.activeCol);
                break;
            case "down":
                this.activateKey(0, this.activeCol);
                break;
            default:
                this.activateKey(this.activeRow, this.activeCol);
                break;
            }
        }
    };
    
    this.unfocus = function () {
        this.deactivateKey(this.activeRow, this.activeCol);
    };
    
    this.dispose = function () {
        var last, r, c;
        for (r = 0; r < this.keys.length; r++) {
            for (c = 0; c < this.keys[r].length; c++) {
                if (this.keys[r][c] !== last) {
                    this.keys[r][c].dispose();
                    last = this.keys[r][c];
                }
            }
        }
        this.keys = null;
        this.symbolsKey = null;
        this.spaceKey = null;
        this.canWrap = false;
    };
    
    this.handleEvent = function (eventName, eventData) {
        if (eventName === "USER_INPUT") {
            this.handleInput(eventData);
        } else {
            
        }
    };

    this.reset = function () {
        this.activateKey(0, 0);
        this.shift = false;
        this.symbols = false;
        this.handleMetaKeys({type : ""});
    };
    
    
    this.findKey = function (character) {
        var r, c;
        for (r = 0; r < this.keys.length; r++) {
            for (c = 0; c < this.keys[r].length; c++) {
                if (this.keys[r][c].getText() === character) {
                    return {row: r, col: c};
                }
            }
        }
        return {row: -1, col: -1};
    };

    this.handleMetaKeys = function (eventData) {
        var i, r, c, key, letter, letters, symbolList;
        if (eventData.type === "SYMBOLS") {
            this.symbols = !this.symbols;
            this.shift = false;
        }
        if (eventData.type === "SHIFT") {
            this.shift = !this.shift;
            if (this.symbolsKey) {
                this.symbolsKey.setText("@#$");
            }
            this.symbols = false;
        }
        
        letters = "abcdefghijklmnopqrstuvwxyz0123456789";
        symbolList = "!@#$%^&*?.,:;/|\\\'\"()[]{}<>-_=+~`@@@@";
       
        for (i = 0; i < letters.length; i++) {
            r = Math.floor(i / 9);
            c = i % 9;
            
            if (this.symbols) {
                letter = symbolList.charAt(i);
            } else {
                if (this.shift) {
                    letter = letters.toUpperCase().charAt(i);
                } else {
                    letter = letters.charAt(i);
                }
            }
            
            key = this.keys[r][c];
            key.setText(letter);
            key.eventData = {
                type: "LETTER",
                letter: letter
            };
        }
        
        if (this.symbolsKey) {
            if (this.symbols) {
                this.symbolsKey.setText("abc");
            } else {
                this.symbolsKey.setText("@#$");
            }
        }
        
    };

    this.handleInput = function (eventData) {
        var oldKey, r, c, loc;
        if (!eventData.is_down || eventData.is_hold) {
            return;
        }
        
        switch (eventData.action) {
        case "KEY_RETURN":
            this.node.view.fireEvent("RETURN_KEY_PRESSED", {target: this.node});
            break; 
        case "MOVE_UP":
            oldKey = this.getActiveKey();
            for (r = this.activeRow - 1; r >= 0; r--) {
                if (this.keys[r][this.activeCol] !== oldKey) {
                    this.activateKey(r, this.activeCol);
                    return;
                }
            }
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "up"});
            break;
        case "MOVE_LEFT":
            oldKey = this.getActiveKey();
            for (c = this.activeCol - 1; c >= 0; c--) {
                if (this.keys[this.activeRow][c] !== oldKey) {
                    this.activateKey(this.activeRow, c);
                    return;
                }
            }
            if (this.canWrap) {
                this.activateKey(this.activeRow, this.keys[this.activeRow].length - 1);
                return;
            }
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "left"});
            break;
        case "MOVE_RIGHT":
            oldKey = this.getActiveKey();
            for (c = this.activeCol + 1; c < this.keys[this.activeRow].length; c++) {
                if (this.keys[this.activeRow][c] !== oldKey) {
                    this.activateKey(this.activeRow, c);
                    return;
                }
            }
            if (this.canWrap) {
                this.activateKey(this.activeRow, 0);
                return;
            }
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "right"});
            break;
        case "MOVE_DOWN":
            oldKey = this.getActiveKey();
            for (r = this.activeRow + 1; r < this.keys.length; r++) {
                if (this.keys[r][this.activeCol] !== oldKey) {
                    this.activateKey(r, this.activeCol);
                    return;
                }
            }
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "down"});
            break;
        case "ADVANCE_FORWARD":
            this.node.view.fireEvent("KEYBOARD_INPUT", {type: "RIGHT"});
            break;
        case "ADVANCE_BACKWARD":
            this.node.view.fireEvent("KEYBOARD_INPUT", {type: "LEFT"});
            break;
        case "START":
            this.node.view.fireEvent("KEYBOARD_INPUT", {type: "SUBMIT"});
            break;
        case "KEYBOARD":
            this.node.view.fireEvent("KEYBOARD_INPUT", {type: "LETTER", letter: eventData.character});
            break;
        case "TAB":
            this.node.view.fireEvent("KEYBOARD_INPUT", {type: "TAB"});
            break;        
        case "BACKSPACE":
            this.node.view.fireEvent("KEYBOARD_INPUT", {type: "BACKSPACE"});
            break;
        case "SPACE":
            this.node.view.fireEvent("KEYBOARD_INPUT", {type: "SPACE"});
            break;
        case "PRESS_OK":
            if (this.getActiveKey().eventData.type === "SHIFT" || this.getActiveKey().eventData.type === "SYMBOLS") {
                this.handleMetaKeys(this.getActiveKey().eventData);
            } else {
                this.node.view.fireEvent("KEYBOARD_INPUT", this.getActiveKey().eventData);
            }
            break;
        default:
            
            break;
        }
    };
};

$htv.Controls.TextInput = function () {
    this.height = 0;
    this.width = 0;
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.labelOffsetX = 0;
    this.labelOffsetY = 0;
    this.className = "TextInput";
    this.backgroundImage = null;
    this.focusedImage = null;
    this.unfocusedUnselectedImage = null;
    this.unfocusedSelectedImage = null;
    this.unfocusedImage = null;
    this.textContainer = null;
    this.leftLabel = null;
    this.rightLabel = null;
    this.cursor = null;
    this.cursorVisible = true;
    this.cursorImage = null;
    this.cursorTimer = null;
    this.cursorPosition = 0;
    this.node = null;
    this.actualText = "";
    this.actualTextLeft = "";
    this.actualTextRight = "";
    this.maskText = false;
    this.hasFocus = false;
    this.styleName = null;
    
    this.initialize = function (position, target, url, options) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        this.labelOffsetX = 7;
        this.labelOffsetY = 8;
        this.node = target;
        this.focusedImage = "images/search-text-input-focused.png";
        this.unfocusedUnselectedImage = "images/search-text-input-unfocused.png";
        this.styleName = $htv.Styles.LoginFieldLabel; 
        this.hasFocus = false;
        this.cursorImage = "images/cursor.png";
        this.cursorTimer = $htv.Platform.createTimer(500, this, this.cursorTimerTick); 
        this.cursorTimer.start();
        this.cursorPosition = 0;
        this.actualText = "";
        this.actualTextLeft = "";
        this.actualTextRight = "";

        if (options.hasOwnProperty("focused_image")) {
            this.focusedImage = options.focused_image;
        }
        if (options.hasOwnProperty("unfocused_image")) {
            this.unfocusedUnselectedImage = options.unfocused_image;
        }
        this.unfocusedImage = this.unfocusedUnselectedImage;
        this.maskText = options.hasOwnProperty("mask_text");
        if (options.hasOwnProperty("unfocused_selected_image")) {
            this.unfocusedSelectedImage = options.unfocused_selected_image;                
        }
        if (options.hasOwnProperty("styleName")) {
            this.styleName = options.styleName;
        }
        
        if (options.hasOwnProperty("label_offset_x")) {
            this.labelOffsetX = options.label_offset_x;
        }

        if (options.hasOwnProperty("label_offset_y")) {
            this.labelOffsetY = options.label_offset_y;
        }

        this.render();
    };
    
    this.render = function () {
        this.backgroundImage = $htv.Platform.createImage({
            x: this.x,
            y: this.y,
            z: this.z,
            width: this.width,
            height: this.height
        }, this.unfocusedImage, this.canvas);
        
        this.textContainer = $htv.Platform.createContainer({
            x: this.x + 25 + this.labelOffsetX,
            y: this.y + 21 + this.labelOffsetY,
            z: this.z + 1,
            width: this.width,
            height: this.height
        }, this.canvas, {vertical: false, spacing: 0, edgePadding: 0});

        this.leftLabel = $htv.Platform.createLabel({
            x: 0,
            y: 0,
            z: this.z + 1,
            width: this.width - 25 - this.labelOffsetX,
            height: this.height
        }, "", this.canvas, this.styleName ? {styleName : this.styleName} : {});
        
        this.cursor = $htv.Platform.createImage({
            x: 0,
            y: 0,
            z: this.z + 1,
            width: 2,
            height: 25
        }, this.cursorImage, this.canvas);

        this.rightLabel = $htv.Platform.createLabel({
            x: 0,
            y: 0,
            z: this.z + 1,
            width: this.width - 25 - this.labelOffsetX,
            height: this.height
        }, "", this.canvas, this.styleName ? {styleName : this.styleName} : {});

        this.textContainer.addItem(this.leftLabel, {padding: {right: 1}});
        this.textContainer.addItem(this.cursor);
        this.textContainer.addItem(this.rightLabel); 
    };
    
    this.getText = function () {
        return this.actualText;
    };
    
    this.setText = function (text) {
        this.cursorPosition += text.length - this.actualText.length;
        this.actualText = text;
        this._updateTextDisplay();
    };

    this.moveCursorLeft = function () {
        if (this.cursorPosition > 0) {
            this.cursorPosition--;
            this._updateTextDisplay();
        }
    };

    this.moveCursorRight = function () {
        if (this.cursorPosition < this.actualText.length) {
            this.cursorPosition++;
            this._updateTextDisplay();
        }
    };

    this._updateTextDisplay = function () {
        var maskedString = "*****************************************";
        this.actualTextLeft = this.actualText.substring(0, this.cursorPosition);
        this.actualTextRight = this.actualText.substring(this.cursorPosition, this.actualText.length);
        if (this.maskText === true) {
            this.leftLabel.setText(maskedString.substring(0, this.actualTextLeft.length));
            this.rightLabel.setText(maskedString.substring(0, this.actualTextRight.length));
        } else {
            this.leftLabel.setText(this.actualTextLeft);
            this.rightLabel.setText(this.actualTextRight);
        }
    };

    this.cursorTimerTick = function () {
        if (this.cursorVisible) {
            this.cursor.hide();
        } else {
            this.cursor.show();
        }
        this.cursorVisible = !this.cursorVisible;
    };

    this.hide = function () {
        this.cursorTimer.stop();
        this.cursor.hide();
        this.textContainer.hide();
        this.backgroundImage.hide();
    };
    
    this.show = function () {
        this.cursorTimer.start();
        this.textContainer.show();
        this.backgroundImage.show();
    };
    
    this.focus = function () {
        this.backgroundImage.setURL(this.focusedImage);
        this.hasFocus = true;
        $htv.Platform.updateSpotlight(this.x + this.width / 2, this.y + this.height / 2 + 10, 2);
    };
    
    this.unfocus = function () {
        this.backgroundImage.setURL(this.unfocusedImage);
        this.hasFocus = false;
    };
    
    this.setActive = function (active) {
        if (active) {
            this.cursorTimer.start();
            if (this.unfocusedSelectedImage) { 
                this.unfocusedImage = this.unfocusedSelectedImage;
            }
            if (!this.hasFocus) {
                this.backgroundImage.setURL(this.unfocusedImage);
            }
        }
        else {
            this.cursorTimer.stop();
            this.cursor.hide();
            this.unfocusedImage = this.unfocusedUnselectedImage;
            if (!this.hasFocus) {
                this.backgroundImage.setURL(this.unfocusedImage);            
            }
        }
    };

    this.dispose = function () {
        this.cursorTimer.stop();
        this.textContainer.removeItem(this.leftLabel);
        $htv.Platform.deleteItem(this.leftLabel);
        this.textContainer.removeItem(this.cursor);
        $htv.Platform.deleteItem(this.cursor);
        this.textContainer.removeItem(this.rightLabel);
        $htv.Platform.deleteItem(this.rightLabel);
        $htv.Platform.deleteItem(this.textContainer);
        $htv.Platform.deleteItem(this.backgroundImage);
        this.backgroundImage = null;
        this.rightLabel = null;
        this.leftLabel = null;
        this.cursorTimer = null;
        this.cursor = null;
        this.styleName = null;
        this.unfocusedUnselectedImage = null;
        this.unfocusedSelectedImage = null;
        this.unfocusedImage = null;
        this.textContainer = null;
        this.cursorImage = null;
        this.labelOffsetX = 0;
        this.labelOffsetY = 0;
        this.focusedImage = null;
    };
    
    this.handleEvent = function (eventName, eventData) {
        if (eventName === "USER_INPUT") {
            this.handleInput(eventData);
        } else {
            
        }
    };
    
    this.handleKeyboardInput = function (eventData) {
        var oldTextLeft, oldTextRight, oldText;
        
        oldTextLeft = this.actualTextLeft;
        oldTextRight = this.actualTextRight;
        oldText = oldTextLeft + oldTextRight;

        switch (eventData.type) {
        case "LETTER":
            if (oldText.length > 32) {
                // max letters.
                return;
            }
            this.setText(oldTextLeft + eventData.letter + oldTextRight);
            break;
        case "SHIFT":
            break;
        case "LEFT":
            this.moveCursorLeft();
            break;
        case "RIGHT":
            this.moveCursorRight();
            break;
        case "BACKSPACE":
            if (oldTextLeft.length > 0) {
                this.setText(oldTextLeft.substring(0, oldTextLeft.length - 1) + oldTextRight);
            }
            break;
        case "SUBMIT":
            break;
        default:
            
            break;
        }
        
        if (oldText !== this.getText()) {
            this.node.view.fireEvent("TEXT_INPUT_CHANGED", 
                {old_text: oldText, new_text: this.getText()});
        }
    };
    
    this.handleInput = function (eventData) {
        if (!eventData.is_down || eventData.is_hold) {
            return;
        }
        
        switch (eventData.action) {
        case "KEY_RETURN":
            this.node.view.fireEvent("RETURN_KEY_PRESSED", {target: this.node});
            break;
        case "MOVE_UP":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "up"});
            break;
        case "MOVE_LEFT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "left"});
            break;
        case "MOVE_RIGHT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "right"});
            break;
        case "MOVE_DOWN":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "down"});
            break;
        case "ADVANCE_FORWARD":
            this.moveCursorRight();
            break;
        case "ADVANCE_BACKWARD":
            this.moveCursorLeft();
            break;
        case "START":
        case "PRESS_OK":
            this.node.view.fireEvent("TEXT_INPUT_ACTIVATED", {
                text: this.getText(),
                target: this.node
            });
            break;
        case "BACKSPACE":
            this.handleKeyboardInput({type: "BACKSPACE"});
            break;
        case "KEYBOARD":
            this.handleKeyboardInput({type: "LETTER", letter: eventData.character});
            break;
        case "TAB":
            this.node.view.fireEvent("KEYBOARD_INPUT", {type: "TAB"});
            break;
        default:
            
            break;
        }
    };
};

$htv.Controls.BoxControl = function () {
    this.height = 0;
    this.width = 0;
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.className = "BoxControl";
    this.node = null;
    this.box = null;
    this.style = null;
    
    this.initialize = function (position, target, style, options) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        this.node = target;
        this.style = null;
        this.box = null;
        this.render(style, options);
    };
    
    this.render = function (style, options) {
        if (this.style !== style || this.box === null) {
            this.style = style;
            this.box = $htv.Platform.createBox({
                x: this.x,
                y: this.y,
                z: this.z,
                width: this.width,
                height: this.height
            }, style, this.canvas);
        } else {
            this.box.show();
        }
    };
    
    this.hide = function () {
        this.box.hide();
    };
    
    this.show = function () {
        this.box.show();
    };
    
    this.focus = function () {
    };
    
    this.unfocus = function () {
    };
    
    this.dispose = function () {
        $htv.Platform.deleteItem(this.box);
        this.box = null;
        this.style = null;
    };
    
    this.handleEvent = function (eventName, eventData) {
        if (eventName === "USER_INPUT") {
            this.handleInput(eventData);
        } else {
            
        }
    };
    
    this.handleInput = function (eventData) {
        if (!eventData.is_down || eventData.is_hold) {
            return;
        }
        
        switch (eventData.action) {
        case "MOVE_UP":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "up"});
            break;
        case "MOVE_LEFT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "left"});
            break;
        case "MOVE_RIGHT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "right"});
            break;
        case "MOVE_DOWN":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "down"});
            break;
        default:
            
            break;
        }
    };
};

$htv.Controls.ImageControl = function () {
    this.height = 0;
    this.width = 0;
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.className = "ImageControl";
    this.image = null;
    this.node = null;
    this.url = null;
    
    this.initialize = function (position, target, url, options) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        this.node = target;
        this.url = null;
        this.render(url, options);
    };
    
    this.render = function (url, options) {
        if (this.url !== url || this.image === null) {
            this.url = url;
            this.image = $htv.Platform.createImage({
                x: this.x,
                y: this.y,
                z: this.z,
                width: this.width,
                height: this.height
            }, url, this.canvas);
        } else {
            this.image.show();
            
        }
    };
    
    // change src of image
    this.setSource = function (url) {
        if (this.image) {
            this.image.setURL(url);
        } else {
            this.render(url, {});
        }
    };
    
    this.hide = function () {
        this.image.hide();
    };
    
    this.show = function () {
        this.image.show();
    };
    
    this.focus = function () {
    };
    
    this.unfocus = function () {
    };
    
    this.dispose = function () {
        $htv.Platform.deleteItem(this.image);
        this.image = null;
        this.url = null;
    };
    
    this.handleEvent = function (eventName, eventData) {
        if (eventName === "USER_INPUT") {
            this.handleInput(eventData);
        } else {
            
        }
    };
    
    this.handleInput = function (eventData) {
        if (!eventData.is_down || eventData.is_hold) {
            return;
        }
        
        switch (eventData.action) {
        case "MOVE_UP":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "up"});
            break;
        case "MOVE_LEFT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "left"});
            break;
        case "MOVE_RIGHT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "right"});
            break;
        case "MOVE_DOWN":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "down"});
            break;
        default:
            
            break;
        }
    };
};

$htv.Controls.TextControl = function () {
    this.height = 0;
    this.width = 0;
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.className = "TextControl";
    this.textArea = null;
    this.node = null;
    
    this.initialize = function (position, target, url, options) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        this.node = target;
        
        this.render(options);
    };
    
    this.render = function (options) {
        var textOptions = {};
        if (options && options.hasOwnProperty("styleName")) {
            textOptions.styleName = options.styleName;
        }
        
        this.textArea = $htv.Platform.createTextArea(
            {
                x: this.x,
                y: this.y,
                z: this.z,
                width: this.width,
                height: this.height
            },
            (options.text ? options.text : ""), this.canvas, textOptions);
    };
    
    this.getText = function () {
        return this.textArea.getText();
    };
    
    this.setText = function (text) {
        this.textArea.setText(text);
    };
    
    this.getTextDimensions = function () {
        return this.textArea.getTextDimensions();
    };
    
    this.hide = function () {
        this.textArea.hide();
    };
    
    this.show = function () {
        this.textArea.show();
    };
    
    this.focus = function () {
    };
    
    this.unfocus = function () {
    };
    
    this.dispose = function () {
        $htv.Platform.deleteItem(this.textArea);
        this.textArea = null;
        this.height = 0;
        this.width = 0;
        this.x = 0;
        this.y = 0;
        this.z = 0;
        this.node = null;
    };
    
    this.handleEvent = function (eventName, eventData) {
        if (eventName === "USER_INPUT") {
            this.handleInput(eventData);
        } else {
            
        }
    };
    
    this.handleInput = function (eventData) {
        if (!eventData.is_down || eventData.is_hold) {
            return;
        }
        
        switch (eventData.action) {
        case "MOVE_UP":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "up"});
            break;
        case "MOVE_LEFT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "left"});
            break;
        case "MOVE_RIGHT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "right"});
            break;
        case "MOVE_DOWN":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "down"});
            break;
        case "PRESS_OK":
            this.node.view.fireEvent("MENU_ITEM_ACTIVATED", {
                item: {},
                target: this.node
            });
            break;
        default:
            
            break;
        }
    };
};

$htv.Controls.ButtonControl = function () {
    this.height = 0;
    this.width = 0;
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.className = "ButtonControl";
    this.label = null;
    this.node = null;
    this.image = null;
    this.focusedImage = null;
    this.unfocusedImage = null;
    this.focusedTextStyle = null;
    this.unfocusedTextStyle = null;
    this.iconImage = null;
    this.iconImageUrl = null;
    this.iconImagePosition = null;
    this.leftBracket = null;
    this.rightBracket = null;
    this.hasFocus = false;
    this.visible = true;
    this.include_brackets = false;
    
    this.initialize = function (position, target, url, options) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        this.focusedImage = null;
        this.unfocusedImage = null;
        this.focusedTextStyle = null;
        this.unfocusedTextStyle = null;
        this.iconImage = null;
        this.iconImageUrl = null;
        this.iconImagePosition = null;
        this.node = target;
        this.hasFocus = false;
        this.visible = true;
        this.include_brackets = false;

        if (options.hasOwnProperty("focused_image")) {
            this.focusedImage = options.focused_image;
        }
        if (options.hasOwnProperty("unfocused_image")) {
            this.unfocusedImage = options.unfocused_image;
        }
        if (options.hasOwnProperty("focused_style")) {
            this.focusedTextStyle = options.focused_style;
        }
        if (options.hasOwnProperty("unfocused_style")) {
            this.unfocusedTextStyle = options.unfocused_style;
        }
        if (options.hasOwnProperty("include_brackets")) {
            this.include_brackets = options.include_brackets;
        }
        if (options.hasOwnProperty("icon_image")) {
            this.iconImageUrl = options.icon_image;
            
            if (options.hasOwnProperty("icon_image_position")) {
                this.iconImagePosition = options.icon_image_position;
            }
            else {
                this.iconImagePosition = $htv.Utils.createPosition();
            }
        }
        
        this.render(options);
    };
    
    this.render = function (options) {
        if (this.focusedImage || this.unfocusedImage) {
            this.image = $htv.Platform.createImage({
                x: this.x,
                y: this.y,
                z: this.z,
                width: this.width,
                height: this.height
            }, this.unfocusedImage, this.canvas);
        }
        
        // TODO: Remove this.focusedImage === null condition once all ButtonControls are skinned
        if (this.focusedImage === null || this.include_brackets === true) {
            this.leftBracket = $htv.Platform.createImage({
                x: this.x - 20,
                y: this.y + 1,
                z: this.z,
                width: 6,
                height: Math.min(this.height, 27)
            }, "images/bracket_small_left.png", this.canvas);
            this.leftBracket.hide();
            this.rightBracket = $htv.Platform.createImage({
                x: this.x + this.width + 14 + (this.iconImageUrl === null ? 0 : this.iconImagePosition.width),
                y: this.y + 1,
                z: this.z,
                width: 6,
                height: Math.min(this.height, 27)
            }, "images/bracket_small_right.png", this.canvas);
            this.rightBracket.hide();
        }
        
        if (this.iconImageUrl !== null) {
            this.iconImage = $htv.Platform.createImage({
                x: this.x + this.iconImagePosition.x,
                y: this.y + this.iconImagePosition.y,
                z: this.z + this.iconImagePosition.z,
                width: this.iconImagePosition.width,
                height: this.iconImagePosition.height
            }, this.iconImageUrl, this.canvas);
        }
        
        this.label = $htv.Platform.createLabel({
            x: this.x + (this.iconImageUrl === null ? 0 : this.iconImagePosition.width),
            y: this.y,
            z: this.z + 1,
            width: this.width,
            height: this.height
        }, (options.text ? options.text : ""), this.canvas, {styleName: this.unfocusedTextStyle});
    };
    
    // Update the control's properties and mimic the render() method with moves 
    this.move = function (position) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        
        if (this.focusedImage || this.unfocusedImage) {
            this.image.move({
                x: this.x,
                y: this.y,
                z: this.z,
                width: this.width,
                height: this.height
            });
        }
        
        if (this.focusedImage === null || this.include_brackets === true) {
            this.leftBracket.move({
                x: this.x - 20,
                y: this.y + 1,
                z: this.z,
                width: 6,
                height: Math.min(this.height, 27)
            });
            this.rightBracket.move({
                x: this.x + this.width + 14 + (this.iconImageUrl === null ? 0 : this.iconImagePosition.width),
                y: this.y + 1,
                z: this.z,
                width: 6,
                height: Math.min(this.height, 27)
            });
        }
        
        if (this.iconImageUrl !== null) {
            this.iconImage.move({
                x: this.x + this.iconImagePosition.x,
                y: this.y + this.iconImagePosition.y,
                z: this.z + this.iconImagePosition.z,
                width: this.iconImagePosition.width,
                height: this.iconImagePosition.height
            });
        }
        
        this.label.move({
            x: this.x + (this.iconImageUrl === null ? 0 : this.iconImagePosition.width),
            y: this.y,
            z: this.z + 1,
            width: this.width,
            height: this.height
        });
    };
    
    this.layoutBrackets = function () {
        if (this.leftBracket && this.visible) {
            this.leftBracket.show();
            this.rightBracket.show();
        }
    };
    
    this.getText = function () {
        return this.label.getText();
    };
    
    this.setText = function (text) {
        this.label.setText(text);
    };
    
    this.hide = function () {
        this.visible = false;
        if (this.label) {
            this.label.hide();
        }
        if (this.image) {
            this.image.hide();
        }
        if (this.iconImage) {
            this.iconImage.hide();
        }
        if (this.leftBracket) {
            this.leftBracket.hide();
            this.rightBracket.hide();
        }
    };
    
    this.show = function () {
        this.visible = true;
        this.label.show();
        if (this.image) {
            this.image.show();
        }
        if (this.iconImage) {
            this.iconImage.show();
        }
        if (this.hasFocus) {
            this.layoutBrackets();
        }
    };
    
    this.setImages = function (options) {
        if (options.hasOwnProperty("focused_image")) {
            this.focusedImage = options.focused_image;
        }
        if (options.hasOwnProperty("unfocused_image")) {
            this.unfocusedImage = options.unfocused_image;
        }
        
        if (this.image === null) {
            this.image = $htv.Platform.createImage({
                x: this.x,
                y: this.y,
                z: this.z,
                width: this.width,
                height: this.height
            }, "", this.canvas);
        }
        if (this.hasFocus) {
            this.image.setURL(this.focusedImage);
        }
        else {
            this.image.setURL(this.unfocusedImage);
        }
    };
    
    this.focus = function () {
        this.hasFocus = true;
        if (this.image && this.focusedImage) {
            this.image.setURL(this.focusedImage);
        }
        if (this.focusedTextStyle) {
            this.label.setStyle(this.focusedTextStyle);
        }
        this.layoutBrackets();
        $htv.Platform.updateSpotlight(this.x + this.width / 2, this.y + this.height / 2, 1);
    };
    
    this.unfocus = function () {
        this.hasFocus = false;
        if (this.image && this.unfocusedImage) {
            this.image.setURL(this.unfocusedImage);
        }
        if (this.unfocusedTextStyle) {
            this.label.setStyle(this.unfocusedTextStyle);
        }
        if (this.leftBracket) {
            this.leftBracket.hide();
            this.rightBracket.hide();
        }
    };
    
    this.dispose = function () {
        $htv.Platform.deleteItem(this.label);
        if (this.image) {
            $htv.Platform.deleteItem(this.image);
        }
        if (this.iconImage) {
            $htv.Platform.deleteItem(this.iconImage);
        }
        if (this.leftBracket) {
            $htv.Platform.deleteItem(this.leftBracket);
            $htv.Platform.deleteItem(this.rightBracket);
        }
        this.label = null;
        this.focusedImage = null;
        this.unfocusedImage = null;
        this.focusedTextStyle = null;
        this.unfocusedTextStyle = null;
        this.image = null;
        this.iconImage = null;
        this.leftBracket = null;
        this.rightBracket = null;
    };
    
    this.handleEvent = function (eventName, eventData) {
        if (eventName === "USER_INPUT") {
            this.handleInput(eventData);
        } else {
            
        }
    };
    
    this.handleInput = function (eventData) {
        if (!eventData.is_down || eventData.is_hold) {
            return;
        }
        
        switch (eventData.action) {
        case "MOVE_UP":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "up"});
            break;
        case "MOVE_LEFT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "left"});
            break;
        case "MOVE_RIGHT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "right"});
            break;
        case "MOVE_DOWN":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "down"});
            break;
        case "KEY_RETURN": 
            this.node.view.fireEvent("RETURN_KEY_PRESSED", {target: this.node});
            break;
        case "KEY_EXIT": 
            this.node.view.fireEvent("EXIT_KEY_PRESSED", {target: this.node});
            break;
        case "PRESS_OK":
            this.node.view.fireEvent("BUTTON_ACTIVATED", {
                target: this.node,
                text: this.getText()
            });
            break;
        default:
            
            break;
        }
    };
};

$htv.Controls.RadioButtonControl = function () {
    this.height = 0;
    this.width = 0;
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.className = "RadioButtonControl";
    this.label = null;
    this.node = null;
    this.focusedTextStyle = null;
    this.unfocusedTextStyle = null;
    this.iconImage = null;
    this.iconImageUrl = null;
    this.iconImagePosition = null;
    this.leftBracket = null;
    this.rightBracket = null;
    this.hasFocus = false;
    this.visible = true;
    this.include_brackets = false;
    this.selected = false;
    
    this.initialize = function (position, target, url, options) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        this.focusedTextStyle = null;
        this.unfocusedTextStyle = null;
        this.iconImage = null;
        this.iconImageUrl = null;
        this.iconImagePosition = null;
        this.node = target;
        this.hasFocus = false;
        this.visible = true;
        this.include_brackets = false;

        if (options.hasOwnProperty("focused_style")) {
            this.focusedTextStyle = options.focused_style;
        }
        if (options.hasOwnProperty("unfocused_style")) {
            this.unfocusedTextStyle = options.unfocused_style;
        }
        if (options.hasOwnProperty("include_brackets")) {
            this.include_brackets = options.include_brackets;
        }
        if (options.hasOwnProperty("is_selected")) {
            this.selected = options.is_selected;
        }
        
        this.render(options);
    };
    
    this.render = function (options) {
        if (this.include_brackets === true) {
            this.leftBracket = $htv.Platform.createImage({
                x: this.x - 15,
                y: this.y + 1,
                z: this.z,
                width: 6,
                height: Math.min(this.height, 27)
            }, "images/bracket_small_left.png", this.canvas);
            this.leftBracket.hide();
            this.rightBracket = $htv.Platform.createImage({
                x: this.x + this.width + 9,
                y: this.y + 1,
                z: this.z,
                width: 6,
                height: Math.min(this.height, 27)
            }, "images/bracket_small_right.png", this.canvas);
            this.rightBracket.hide();
        }
        
        this.iconImage = $htv.Platform.createImage({
            x: this.x,
            y: this.y + this.height / 2 - 9,
            z: this.z + 1,
            width: 16,
            height: 17
        }, "images/btn-radio-inactive.png", this.canvas);
        
        this.label = $htv.Platform.createLabel({
            x: this.x + 16,
            y: this.y,
            z: this.z + 1,
            width: Math.max(0, this.width - 16),
            height: this.height
        }, (options.text ? options.text : ""), this.canvas, {styleName: this.unfocusedTextStyle});
    };
    
    // Update the control's properties and mimic the render() method with moves 
    this.move = function (position) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        
        if (this.include_brackets === true) {
            this.leftBracket.move({
                x: this.x - 20,
                y: this.y + 1,
                z: this.z,
                width: 6,
                height: Math.min(this.height, 27)
            });
            this.rightBracket.move({
                x: this.x + this.width + 14 + 16,
                y: this.y + 1,
                z: this.z,
                width: 6,
                height: Math.min(this.height, 27)
            });
        }
        
        this.iconImage.move({
            x: this.x,
            y: this.y + 1,
            z: this.z + 1,
            width: 16,
            height: 17
        });
        
        this.label.move({
            x: this.x + 16,
            y: this.y,
            z: this.z + 1,
            width: this.width,
            height: this.height
        });
    };
    
    this.layoutBrackets = function () {
        if (this.leftBracket && this.visible) {
            this.leftBracket.show();
            this.rightBracket.show();
        }
    };
    
    this.getText = function () {
        return this.label.getText();
    };
    
    this.setText = function (text) {
        this.label.setText(text);
    };
    
    this.hide = function () {
        this.visible = false;
        if (this.label) {
            this.label.hide();
        }
        
        if (this.iconImage) {
            this.iconImage.hide();
        }
        if (this.leftBracket) {
            this.leftBracket.hide();
            this.rightBracket.hide();
        }
    };
    
    this.show = function () {
        this.visible = true;
        this.label.show();
        
        if (this.iconImage) {
            this.iconImage.show();
        }
        if (this.hasFocus) {
            this.layoutBrackets();
        }
    };

    this.select = function () {
        this.selected = true;
        if (this.hasFocus) {
            this.iconImage.setURL("images/btn-radio-active-hover.png");
        } else {
            this.iconImage.setURL("images/btn-radio-active.png");
        }
    };
    
    this.deselect = function () {
        this.selected = false;
        if (this.hasFocus) {
            this.iconImage.setURL("images/btn-radio-hover.png");
        } else {
            this.iconImage.setURL("images/btn-radio-inactive.png");
        }
    };
    
    this.focus = function () {
        this.hasFocus = true;
        
        if (this.selected === true) {
            this.iconImage.setURL("images/btn-radio-active-hover.png");
        }
        else {
            this.iconImage.setURL("images/btn-radio-hover.png");
        }
        
        if (this.focusedTextStyle) {
            this.label.setStyle(this.focusedTextStyle);
        }
        this.layoutBrackets();
        
        $htv.Platform.updateSpotlight(this.x + this.width / 2, this.y + this.height / 2, 1);
    };
    
    this.unfocus = function () {
        this.hasFocus = false;
        
        if (this.selected === true) {
            this.iconImage.setURL("images/btn-radio-active.png");
        }
        else {
            this.iconImage.setURL("images/btn-radio-inactive.png");
        }
        
        if (this.unfocusedTextStyle) {
            this.label.setStyle(this.unfocusedTextStyle);
        }
        if (this.leftBracket) {
            this.leftBracket.hide();
            this.rightBracket.hide();
        }
    };
    
    this.dispose = function () {
        $htv.Platform.deleteItem(this.label);
        
        if (this.iconImage) {
            $htv.Platform.deleteItem(this.iconImage);
        }
        if (this.leftBracket) {
            $htv.Platform.deleteItem(this.leftBracket);
            $htv.Platform.deleteItem(this.rightBracket);
        }
        
        this.label = null;
        this.focusedTextStyle = null;
        this.unfocusedTextStyle = null;
        this.iconImage = null;
        this.leftBracket = null;
        this.rightBracket = null;
    };
    
    this.handleEvent = function (eventName, eventData) {
        if (eventName === "USER_INPUT") {
            this.handleInput(eventData);
        } else {
            
        }
    };
    
    this.handleInput = function (eventData) {
        if (!eventData.is_down || eventData.is_hold) {
            return;
        }
        
        switch (eventData.action) {
        case "MOVE_UP":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "up"});
            break;
        case "MOVE_LEFT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "left"});
            break;
        case "MOVE_RIGHT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "right"});
            break;
        case "MOVE_DOWN":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "down"});
            break;
        case "KEY_RETURN": 
            this.node.view.fireEvent("RETURN_KEY_PRESSED", {target: this.node});
            break;
        case "KEY_EXIT": 
            this.node.view.fireEvent("EXIT_KEY_PRESSED", {target: this.node});
            break;
        case "PRESS_OK":
            this.node.view.fireEvent("RADIO_BUTTON_ACTIVATED", {
                target: this.node,
                text: this.getText()
            });
            break;
        default:
            
            break;
        }
    };
};

$htv.Controls.SuggestResults = function () {
    this.height = 0;
    this.width = 0;
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.activeIndex = 0;
    this.className = "SuggestResults";
    
    this.suggestedTerms = [];
    this.suggestionContainerPosition = null;
    this.suggestionContainer = null;
    
    this.hasHorizontalLine = false;
    this.horizontalLine = null;
    this.horizontalLineContainer = null;
    this.leftBracket = null;
    this.leftBracketLarge = null;
    this.rightBracket = null;
    this.rightBracketLarge = null;
    
    var SPACER_WIDTH = 6,
        LEFT_PADDING_FOR_TITLE = 5,
        RIGHT_PADDING_FOR_TITLE = 5,
        WEB_ONLY_WIDTH = 54;
        
    this.has_focus = false;
    this.visible = false;
    this.background = null;
    this.header = null;
    this.node = null;
    
    this.initialize = function (position, target, url, options) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        this.node = target;
        this.has_focus = false;
        this.visible = true;
        this.leftBracket = $htv.Platform.createImage({
                x: -100,
                y: -100,
                z: this.z + 2,
                width: 6,
                height: 27
            }, "images/bracket-small-left.png", this.canvas);
        this.leftBracket.hide();
        this.rightBracket = $htv.Platform.createImage({
                x: -100,
                y: -100,
                z: this.z + 2,
                width: 6,
                height: 27
            }, "images/bracket-small-right.png", this.canvas);
        this.rightBracket.hide();
        this.suggestionContainerPosition = {
                x: this.x + 12,
                y: this.y + 37,
                z: this.z + 2,
                width: this.width - 20,
                height: 250
            };
        this.suggestionContainer = $htv.Platform.createContainer(this.suggestionContainerPosition, this.canvas, {vertical: true});
        this.suggestionContainer.hide();
        
        this.render();
    };
    
    this.render = function () {
        this.background = $htv.Platform.createImage({
            x: this.x,
            y: this.y + 32,
            z: this.z,
            width: 327,
            height: 277
        }, "images/search-suggest-back.png", this.canvas);
        this.header = $htv.Platform.createLabel({
            x: this.x,
            y: this.y,
            z: this.z + 1,
            width: this.width,
            height: this.height
        }, "Suggested search terms:", this.canvas, { styleName: $htv.Styles.SearchSuggestionTitle });
    };
    
    this.fetchSuggestions = function (query) {
        
        var url = $htv.Utils.applyGlobalContext($htv.Constants.Endpoints.menu +
            "/search?hulu_only=1&suggested_results=1&query=" + query +
            "&items_per_page=" + $htv.Constants.DEFAULT_PAGE_SIZE);
        $htv.Platform.loadURL(url, this, this.onSuggestResponse, { xmlparse: true });
    };
    
    this.populateSuggestions = function (xpathData, parsingWebonly) {
        var suggestedTerm, termLabelPosition, containerPosition, text, item, i, result = xpathData;
        
        
       
        
        for (i = 0; i < result.length && this.suggestedTerms.length < 6; i++) {
        
            item = $htv.Platform.getXPathResultAt(result, i);
            
            // TODO: We want to eventually hightlight the search substrings. We
            //       don't want to strip the html as it contains embedded
            //       information regarding the search.
            text = $htv.Utils.stripHTML($htv.Platform.getXMLNodeText(item));
            
            
            termLabelPosition = {
                x: 0,
                y: 0,
                z: this.z + 3,
                height: 29,
                width: this.width - 45
            };
            containerPosition = {
                x: 0,
                y: 0,
                z: this.z + 3,
                height: 27,
                width: this.width - 23
            };
            suggestedTerm = {
                termLabel: null,
                container: null,
                leftSpacer: null,
                rightSpacer: null,
                webOnlyBadge: null,
                isWebOnly: null,
                actualText: text
            };
            
            if (parsingWebonly === true) {
                if (text.length > 29) {
                    text = text.substring(0, 27) + "...";
                }
                suggestedTerm.isWebOnly = true;
            } else {
                if (text.length > 35) {
                    text = text.substring(0, 33) + "...";
                }
                suggestedTerm.isWebOnly = false;
            }
            suggestedTerm.termLabel = $htv.Platform.createLabel(termLabelPosition, text, this.canvas, {
                styleName: $htv.Styles.SearchSuggestionUnfocused
            });
            
            suggestedTerm.container = $htv.Platform.createContainer(containerPosition, this.canvas, {
                edgePadding: 0,
                spacing: 0
            });
            // Assemble the suggested title container
            suggestedTerm.leftSpacer = $htv.Platform.createSpacer({
                width: SPACER_WIDTH,
                height: 27
            }, this.canvas, {});
            suggestedTerm.rightSpacer = $htv.Platform.createSpacer({
                width: SPACER_WIDTH,
                height: 27
            }, this.canvas, {});
            if (suggestedTerm.isWebOnly === true) {
                suggestedTerm.webOnlyBadge = $htv.Platform.createImage({
                    x: 0,
                    y: 5,
                    z: this.z + 2,
                    width: 48,
                    height: 14
                }, "images/web-only-badge-unfocused.png", this.canvas);
            }
            suggestedTerm.container.addItem(suggestedTerm.leftSpacer);
            // yes, I know top: 5 doesn't look vertical-aligned on Firefox; it totally does the job on devices though
            suggestedTerm.container.addItem(suggestedTerm.termLabel, {padding: {top: 5, left: 5, right: 5}});
            if (suggestedTerm.isWebOnly === true) {
                suggestedTerm.container.addItem(suggestedTerm.webOnlyBadge, {padding: {top: 6, bottom: 5, right: 5, left: 2}});
            }
            suggestedTerm.container.addItem(suggestedTerm.rightSpacer);

            // Action!
            if (suggestedTerm.isWebOnly === true) {
                suggestedTerm.webOnlyBadge.show();
            }
            suggestedTerm.leftSpacer.show();
            suggestedTerm.container.show();
            suggestedTerm.rightSpacer.show();
            suggestedTerm.termLabel.show();
            
            this.suggestionContainer.addItem(suggestedTerm.container);
            
            this.suggestedTerms.push(suggestedTerm);
        }
    };
    
    this.onSuggestResponse = function (responseData, options) {
        var result, i, item, text, suggestedTerm, termLabelPosition, containerPosition, parsing_webonly = false;
        
        
        if (this.visible !== true) {
            
            return;
        }
        
        result = $htv.Platform.evaluateXPath(responseData, "word-match/word-match/name");
        this.activeIndex = 0;
        
        // Delete all old suggestions
        while (this.suggestedTerms.length > 0) {
            suggestedTerm = this.suggestedTerms.pop();
            
            // Delete all the items
            $htv.Platform.deleteItem(suggestedTerm.termLabel);
            $htv.Platform.deleteItem(suggestedTerm.leftSpacer);
            $htv.Platform.deleteItem(suggestedTerm.rightSpacer);
            $htv.Platform.deleteItem(suggestedTerm.container);
            this.suggestionContainer.removeItem(suggestedTerm.container);
            suggestedTerm.titleLabel = null;
            suggestedTerm.leftSpacer = null;
            suggestedTerm.rightSpacer = null;
            suggestedTerm.container = null;
            if (suggestedTerm.isWebOnly === true) {
                $htv.Platform.deleteItem(suggestedTerm.webOnlyBadge);
                suggestedTerm.webOnlyBadge = null;
            }
        }
        
        this.populateSuggestions(result, false);
        result = $htv.Platform.evaluateXPath(responseData, "site-only/site-only/name");
        this.populateSuggestions(result, true);
        
        this.suggestionContainer.show();
    };
   
    this.hasSuggestions = function () {
        return (this.suggestedTerms.length > 0);
    };

    this.hide = function () {
        this.visible = false;
        var i, suggestedTerm;
        for (i = 0; i < this.suggestedTerms.length; i++) {
            suggestedTerm = this.suggestedTerms[i];
            suggestedTerm.leftSpacer.hide();
            suggestedTerm.rightSpacer.hide();
            suggestedTerm.termLabel.hide();
            suggestedTerm.container.hide();
            if (suggestedTerm.isWebOnly === true) {
                suggestedTerm.webOnlyBadge.hide();
            }
        }
        this.leftBracket.hide();
        this.rightBracket.hide();
        this.background.hide();
        this.header.hide();
    };
    
    this.selectLabel = function (index) {
        var selectedLabelPosition, suggestedTerm;
        
        // if we're changing activeIndex
        if (index !== this.activeIndex) {
            this.deselectLabel(this.activeIndex);
        }
        this.activeIndex = index;
        
        suggestedTerm = this.suggestedTerms[index];
        suggestedTerm.termLabel.setStyle($htv.Styles.SearchSuggestionFocused);
        
        // remove spacers
        if (suggestedTerm.container.containsItem(suggestedTerm.leftSpacer)) {
            suggestedTerm.container.removeItem(suggestedTerm.leftSpacer);
        }
        
        if (suggestedTerm.container.containsItem(suggestedTerm.rightSpacer)) {
            suggestedTerm.container.removeItem(suggestedTerm.rightSpacer);
        }
        
        suggestedTerm.leftSpacer.hide();
        suggestedTerm.rightSpacer.hide();
        
        // add brackets
        if (suggestedTerm.container.containsItem(this.leftBracket) !== true) {
            suggestedTerm.container.addItemAtIndex(this.leftBracket, 0);
        }
        
        if (suggestedTerm.container.containsItem(this.rightBracket) !== true) {
            suggestedTerm.container.addItem(this.rightBracket);
        }

        if (suggestedTerm.isWebOnly === true) {
            suggestedTerm.webOnlyBadge.setURL("images/web-only-badge-focused.png");
            suggestedTerm.webOnlyBadge.show();
        }
        
        this.leftBracket.show();
        this.rightBracket.show();
        
        selectedLabelPosition = suggestedTerm.termLabel.getPosition();
        $htv.Platform.updateSpotlight(selectedLabelPosition.x + 40, selectedLabelPosition.y + 10, 1);
    };
    
    this.deselectLabel = function (index) {
        var suggestedTerm = this.suggestedTerms[index];
        suggestedTerm.termLabel.setStyle($htv.Styles.SearchSuggestionUnfocused);
        
        // remove brackets
        if (suggestedTerm.container.containsItem(this.leftBracket)) {
            suggestedTerm.container.removeItem(this.leftBracket);
            this.leftBracket.hide();
        }
        
        if (suggestedTerm.container.containsItem(this.rightBracket)) {
            suggestedTerm.container.removeItem(this.rightBracket);
            this.rightBracket.hide();
        }
        
        // replace with spacers
        if (suggestedTerm.container.containsItem(suggestedTerm.leftSpacer) !== true) {
            suggestedTerm.container.addItemAtIndex(suggestedTerm.leftSpacer, 0);
        }

        if (suggestedTerm.container.containsItem(suggestedTerm.rightSpacer) !== true) {
            suggestedTerm.container.addItem(suggestedTerm.rightSpacer);
        }
        
        if (suggestedTerm.isWebOnly === true) {
            suggestedTerm.webOnlyBadge.setURL("images/web-only-badge-unfocused.png");
        }

        suggestedTerm.leftSpacer.show();
        suggestedTerm.rightSpacer.show();
    };
    
    this.getSelectedLabel = function () {
        // hacky?  but this is the way the view was coded..
        // todo: fix the way view gets the suggest terms.
        var text = this.suggestedTerms[this.activeIndex].actualText;
        return {
            getText: function () {
                return text;
            }
        };
    };
    
    this.show = function () {
        this.visible = true;
        var i, suggestedTerm;
        for (i = 0; i < this.suggestedTerms.length; i++) {
            suggestedTerm = this.suggestedTerms[i];
            suggestedTerm.leftSpacer.show();
            suggestedTerm.rightSpacer.show();
            if (suggestedTerm.isWebOnly === true) {
                suggestedTerm.webOnlyBadge.show();
            }
            suggestedTerm.termLabel.show();
            suggestedTerm.container.show();
        }
        if (this.has_focus === true) {
            this.leftBracket.show();
            this.rightBracket.show();
        }
        this.background.show();
        this.header.show();
    };
    
    this.focus = function () {
        if (this.hasSuggestions()) {
            this.selectLabel(this.activeIndex);
            this.has_focus = true;
        }
        else {
            this.node.view.fireEvent("LOSING_FOCUS", {
                nav_dir: "left"
            });
            return;
        }
    };
    
    this.unfocus = function () {
        if (this.hasSuggestions()) {
            this.deselectLabel(this.activeIndex);
            this.has_focus = false;
        }
    };
    
    this.dispose = function () {
        this.visible = false;
        var suggestedTerm;
        while (this.suggestedTerms.length > 0) {
            suggestedTerm = this.suggestedTerms.pop();
            
            suggestedTerm.container.removeItem(suggestedTerm.termLabel);
            suggestedTerm.container.removeItem(suggestedTerm.leftSpacer);
            suggestedTerm.container.removeItem(suggestedTerm.rightSpacer);
            if (suggestedTerm.isWebOnly === true) {
                suggestedTerm.container.removeItem(suggestedTerm.webOnlyBadge);
                $htv.Platform.deleteItem(suggestedTerm.webOnlyBadge);
            }
            this.suggestionContainer.removeItem(suggestedTerm.container);
            
            // Delete all the items
            $htv.Platform.deleteItem(suggestedTerm.leftSpacer);
            $htv.Platform.deleteItem(suggestedTerm.rightSpacer);
            $htv.Platform.deleteItem(suggestedTerm.termLabel);
            $htv.Platform.deleteItem(suggestedTerm.container);
        }
        $htv.Platform.deleteItem(this.leftBracket);
        this.leftBracket = null;
        $htv.Platform.deleteItem(this.rightBracket);
        this.rightBracket = null;
        $htv.Platform.deleteItem(this.suggestionContainer);
        this.suggestionContainer = null;
        $htv.Platform.deleteItem(this.background);
        this.background = null;
        $htv.Platform.deleteItem(this.header);
        this.header = null;
        this.suggestionContainerPosition = null;
        this.has_focus = false;
    };
    
    this.handleEvent = function (eventName, eventData) {
        if (eventName === "USER_INPUT") {
            this.handleInput(eventData);
        } else {
            
        }
    };
    
    this.handleInput = function (eventData) {
        if (!eventData.is_down || eventData.is_hold) {
            return;
        }
        
        switch (eventData.action) {
        case "KEY_RETURN":
            this.node.view.fireEvent("RETURN_KEY_PRESSED", {target: this.node});
            break;
        case "MOVE_UP":
            if (this.activeIndex > 0) {
                this.selectLabel(this.activeIndex - 1);
            }
            else {
                this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "up"});
            }
            break;
        case "MOVE_LEFT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "left"});
            break;
        case "MOVE_RIGHT":
            this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "right"});
            break;
        case "MOVE_DOWN":
            if (this.activeIndex < this.suggestedTerms.length - 1) {
                this.selectLabel(this.activeIndex + 1);
            }
            else {
                this.node.view.fireEvent("LOSING_FOCUS", {nav_dir: "down"});
            }
            break;
        case "PRESS_OK":
            $htv.Controller.pushView("SearchResultsView", {
                query: $htv.Utils.stripHTML(this.getSelectedLabel().getText().replace("[", "").replace("]", ""))
            });
            break;
        default:
            
            break;
        }
    };
};

$htv.Controls.LoadingIndicator = function () {
    this.height = 0;
    this.width = 0;
    this.x = 0;
    this.y = 0;
    this.z = 0;
    this.node = null;
    this.className = "LoadingIndicator";
    this.canvas = null;
    this.loadingDots = null;
    this.loadingState = 0;
    this.animationTimer = null;
    
    this.initialize = function (position, target, url, options) {
        this.height = position.height;
        this.width = position.width;
        this.x = position.x;
        this.y = position.y;
        this.z = position.z;
        this.loadingState = 0;
        this.node = target;
        this.animationTimer = $htv.Platform.createTimer(0, null, null);
        
        if (options && options.hasOwnProperty("canvas")) {
            this.canvas = options.canvas;
        }
        
        this.loadingDots = $htv.Platform.createImage({
            x: this.x,
            y: this.y,
            z: this.z,
            width: 140,
            height: 10
        }, "images/loading-dot-" + this.loadingState + ".png", this.canvas);
        this.startAnimation();
    };
    
    // does this need to be explicit?
    this.startAnimation = function () {
        this.animationTimer.setCallback(this, this.timerFired);
        this.animationTimer.setInterval(250);
        this.animationTimer.start();
    };
    
    this.timerFired = function () {
        this.loadingState++;
        if (this.loadingState === 9) {
            this.loadingState = 0;
        }
        this.loadingDots.setURL("images/loading-dot-" + this.loadingState + ".png");
    };
    
    this.stopAnimation = function () {
        this.animationTimer.stop();
    };
    
    this.dispose = function () {
        $htv.Platform.deleteItem(this.loadingDots);
        this.loadingDots = null;
        this.animationTimer.stop();
        this.animationTimer = null;
    };
    
    this.focus = function () {
    };
    
    this.unfocus = function () {
    };
    
    this.hide = function () {
        this.loadingDots.hide();
        this.stopAnimation();
    };
    
    this.show = function () {
        this.loadingDots.show();
        this.startAnimation();
    };
    
    this.handleEvent = function (eventName, eventData) {
        
    };
};
