/*globals
 jQuery ngv loadableMovie, setTimeout, window, document, clearInterval, setInterval */
(function() {
    var view, timelineview;
    /**
     *     find the index of the nearest number to (i), from an array of numbers (a) in descending order
     * @param {integer} i
     * @param {array} a
     */
    function nearestNeighbor(i, a) {
        var c, m;
        c = 0;
        if (!isNaN(i) && a instanceof Array) {
            do {
                m = Math.abs(a[c] - i);
                c++;
            } while (m >= Math.abs((a[c] || 0) - i));
            return Math.min(c - 1, a.length - 1);
        }
        return -1;
    }
    function stripHash(s) {
        var o = /[^#]+/.exec(s);
        return o ? o[0] : "";
    }
    var attrCache = {};
    function findAttr(name) {
        var attr, lin;
        if (attrCache[name]) {
            attr = attrCache[name];
        } else {
            lin = jQuery(".menu a[href=#" + name + "]");
            attr = lin.parents("div.magnify");
            attrCache[name] = attr;
        }
        return attr;
    } //A helper function to figure out how to most effeciently
    //interact with the dom when I have to change a group of
    // elements.
    function updateElements(prev, curr, equals, remove, update, add) {
        var i;
        for (i in prev) {
            if (prev.hasOwnProperty(i)) {
                if (! (i in curr)) {
                    remove(i, curr[i]);
                }
                if (curr[i] && !equals(prev[i], curr[i])) {
                    update(i, curr[i]);
                }
            }
        }
        for (i in curr) {
            if (! (i in prev)) {
                add(i, curr[i]);
                update(i, curr[i]);
            }
        }
    }
    function updateTrack(parentelname, cache, data, equals, updateElement) {
        var state = data.state || {};
        var parentel = view["_" + parentelname] || (view["_" + parentelname] = jQuery("#" + parentelname));
        var pfd = view["_" + parentelname + "Digest"] || {};
        updateElements(pfd, //previous state
        state, //current state
        equals, //equality test function
        function(i) { //what to do when an element needs removing
            cache[i].remove();
            delete cache[i];
        },
        function(i, state) { //what to do when i need to update
            updateElement(cache[i], state, i);
        },
        function(i) { //what to do when I need to add
            var gid = (++timelineview.gidCounter);
            var fe = jQuery("<div/>").attr("id", parentelname.substr(0, 1) + gid);
            parentel.append(fe);
            cache[i] = fe;
        });
        view["_" + parentelname + "Digest"] = state;
    }
    function findPrevKey(i, a) {
        if (!isNaN(i) && a instanceof Object) {
            while (a[i] === undefined && i >= 0) {
                i--;
            }
            return i;
        }
        return -1;
    }
    function findNextKey(i, a, c) {
        c = a.length || c;
        if (!isNaN(i) && a instanceof Object) {
            while (a[i] === undefined && i < c) {
                i++;
            }
            return i < c ? i : c;
        }
    }
    view = {
        libtabsui: null,
        stageHeight: 527,
        stageWidth: 982,
        currentBackground: null,
        currentActionText: "",
        stage: null,
        description: null,
        descriptionEl: null,
        stageActorElements: null,
        currentOrnaments: {},
        currentOrnamentsGID: [],
        libraryVisible: true,
        libraryUserShow: true,
        _stageactionel: null,
        _stageactionvisible: null,
        ngvactorsquery: null,
        shownAttrGui: "",
        shownAttrGuiCategory: "",
        charAttributesListElements: {},
        currentStageActionType: "",
        stageActionGuiTextEl: null,
        stageActionGuiTextText: "",
        editable:false,
        alert: function(e, d, r) {
            var m = d.msg;
            var alertel = jQuery("<div/>");
            var tel = jQuery("<p/>");
            alertel.append(tel);
            var stageel = jQuery("#stage");
            alertel.addClass("fmalert");
            alertel.css("margin", "auto");
            function dismiss() {
                if(alertel) {alertel.fadeOut(100);}
            }
            alertel.click(dismiss);
            tel.html(m);
            stageel.append(alertel);
            setTimeout(dismiss,
            6000);
            setTimeout(function() {
                alertel.remove();
            },
            7000);
        },
        stoppedPlaying: function(e, d, r) {
            this.playheadDraggable = true;
            if(this.editable){
            jQuery(".stageactor").draggable("enable");
            r.replyTo("enableStageClick");
            }
        },
        startedPlaying: function(e, d, r) {
            this.playheadDraggable = false;
            if(this.editable){
            jQuery(".stageactor").draggable("disable");
            r.replyTo(e, "disableStageClick");
            }
        },
        enableStageClick: function(e, d, r) {
            function charGuiClicked(e, ui) {
                if (e.target.id !== "stage") {
                    r.cause("stageActorClicked", {
                        name: e.target.id.substr(1)
                    });
                }
            }
            
            if(this.editable){
            jQuery("#stage").bind("click", charGuiClicked);
            }
        },
        disableStageClick: function() {
            jQuery("#stage").unbind("click");
        },
        setCurrentBackgroundGui: function(e, d, r) {
            var newbackground = d.backgroundname;
            var stage;
            stage = this.stage || (this.stage = jQuery("#stage"));
            if (newbackground !== this.currentBackground) {
                this.currentBackground = newbackground;
                stage.css({
                    backgroundImage: "url(" + d.backgroundstageimage + ")",
                    backgroundColor: d.backgroundcolor
                });
            }
        },
        setCurrentDescriptionGui: function(e, d, r) {
            var des, desEl, desEl2;
            des = d.text || "";
            desEl = this.descriptionEl || (this.descriptionEl = jQuery("#sceneDescription"));
            desEl2 = this.descriptionEl2 || (this.descriptionEl2 = jQuery("#playbackTextOverlay"));
            if (des !== this.description) {
                this.description = des;
                desEl.attr("value", des);
                desEl2.text(des);
                desEl2.show();
                if(!(/[^\s]/.test(des))) {
                    desEl2.fadeOut(80);
                }
            }
        },
        sceneDescriptionGuiChanged: function(e, d, r) {
            this.description = d.text;
        },
        //in this function, we might be modifying an actor that's already on stage
        //or we might be adding some actors
        //when we add an actor, we attach some event handlers onto them, and make them draggable
        modifyStageActors: function(e, d, r) { //modify or add any actors in the list provided via
            //d.actors
            var view = this;
            var newchr = d.actors;
            var stagewidth, stageheight, stage, chrs, cssclass, options, content, styles, chr, chrEl, chrI, left, top, scale, scalei, hflip, yoffset, xoffset, offsetCSS, widthCSS, heightCSS, availableScales;
            stagewidth = this.stageWidth / 1000;
            stageheight = this.stageHeight / 1000;
            stage = this.stage || (this.stage = jQuery("#stage"));
            chrs = this.stageActorElements || (this.stageActorElements = {});
            cssclass = "stageactor";
            function start() {
                r.cause("disableStageClick", {
                    frameSync: true
                });
                r.cause("hideLibraryGui", {
                    frameSync: true
                });
            }
            function stop(e, ui) {
                var l, t, w, h, offstage;
                l = ui.position.left / 1000;
                t = ui.position.top / 1000;
                w = ui.helper.width() / 1000;
                h = ui.helper.height() / 1000;
                offstage = l + w < 0 || t + h < 0 || l > stagewidth || t > stageheight; //addactor won't fire if dragged off the stage
                if (offstage) {
                    r.cause("actorGuiDroppedOffStage", {
                        name: e.target.id.slice(1)
                    });
                }
                r.cause("enableStageClick", {
                    frameSync: true
                });
                if (view.libraryUserShow) {
                    r.cause("showLibraryGui", {
                        frameSync: true
                    });
                }
            }
            for (chr in newchr) {
                if (newchr.hasOwnProperty(chr)) {
                    chrI = newchr[chr];
                    content = chrI.content || {};
                    left = chrI.x * stagewidth || 0;
                    top = chrI.y * stagewidth || 0;
                    scale = chrI.scale;
                    availableScales = content.scales || [150, 100, 75];
                    scalei = nearestNeighbor(scale, availableScales);
                    scale = availableScales[scalei] * 0.01 || 1;
                    widthCSS = Math.round(content.basewidth * (scale) * stagewidth);
                    heightCSS = Math.round(content.baseheight * (scale) * stagewidth);
                    left -= widthCSS / 2;
                    top -= heightCSS / 2; //center on x,y
                    //we're assuming offsets are based on a 1000*1000 image, so we need to scale
                    hflip = content.flipoffsets[chrI.hflip + 0];
                    yoffset = -(hflip * ((content.stageimageheight || 1000) / 1000));
                    xoffset = -content.scaleoffsets[scalei] * (content.stageimagewidth / 1000);
                    offsetCSS = xoffset + "px " + yoffset + "px";
                    styles = {
                        "z-index": 1,
                        position: "absolute",
                        left: left,
                        top: top,
                        width: widthCSS,
                        height: heightCSS,
                        opacity: chrI.opacity === 1 ? "" : chrI.opacity,
                        backgroundPosition: offsetCSS,
                        backgroundImage: "url(" + content.stageimage + ")"
                    };
                    options = {
                        distance: 3,
                        start: start,
                        stop: stop
                    };
                    if (chrs[chr]) {
                        chrs[chr].css(styles);
                    } else { // .addClass(chr).
                        chrEl = jQuery("<div><div/></div>").css(styles).addClass(cssclass).attr("id", "c" + chr);
                        stage.append(chrEl);
                        if(view.editable){
                            chrEl.draggable(options); 
                        }
                        //add this element to this.stageActorElements, here aliased as chrs
                        chrs[chr] = chrEl;
                    }
                }
            }
        },
        deleteStageActors: function(e, d, r) {
            var chr, chrs, newchr; //delete any actors that are NOT in the provided list
            // the list provided via d.actors.
            chrs = this.stageActorElements || (this.stageActorElements = {});
            newchr = d.actors;
            if (newchr) {
                for (chr in chrs) {
                    if (! (chr in newchr)) {
                        chrs[chr].remove();
                        delete chrs[chr];
                    }
                }
            }
        },
        setCurrentOrnamentsGui: function(e, d, r) {
            var co = this.currentOrnaments;
            var stagewidth = this.stageWidth / 1000;
            var stage = this.stage || (this.stage = jQuery("#stage"));
            var orns = d.ornaments,
            i, orn, cont;
            var css, left, top;
            var pl, pt, pr, pb; //todo:
            function makeMouseOver() {
                var el = co[orn.id];
                return function() {
                    el.css("z-index", 0);
                    return true;
                };
            } //here you should delete any ornaments that shouldn't be on stage
            //
            for (i in co) {
                if (!orns[i]) {
                    co[i].remove();
                    delete co[i];
                }
            } //todo: draw text box onto ornament.
            for (i in orns) {
                if (orns.hasOwnProperty(i)) {
                    orn = orns[i];
                    cont = orn.content;
                    pl = cont.paddingleft || 0;
                    pr = cont.paddingright || 0;
                    pt = cont.paddingtop || 0;
                    pb = cont.paddingbottom || 0; //todo: scale padding by the scaling factor.
                    left = (orn.x - (cont.basewidth / 2)) * stagewidth;
                    top = (orn.y - (cont.baseheight / 2)) * stagewidth;
                    if (co[orns[i].id]) {
                        co[orns[i].id].css({
                            left: left,
                            top: top,
                            opacity: orns[i].opacity === 1 ? "" : orns[i].opacity,
                            "z-index": 2
                        });
                        co[orns[i].id].text(orn.text || "");
                    } else {
                        orn = orns[i];
                        cont = orn.content; //make a new ornament element
                        css = {
                            position: "absolute",
                            "z-index": 2,
                            left: left,
                            top: top,
                            opacity: orns[i].opacity === 1 ? "" : orns[i].opacity,
                            backgroundImage: "url(" + cont.stageimage + ")",
                            width: cont.basewidth * stagewidth - pl - pr,
                            height: cont.baseheight * stagewidth - pt - pb,
                            "padding-left": pl,
                            "padding-right": pr,
                            "padding-top": pt,
                            "padding-bottom": pb,
                            "text-align": "center"
                        };
                        co[orn.id] = jQuery("<div/>").css(css).attr("id", orn.id);
                        co[orn.id].bind("mouseover", makeMouseOver()).text(orn.text || "");
                        stage.append(co[orn.id]);
                    }
                }
            }
        },
        hideStageOrnamentsGui: function(e, d, r) {
            var i;
            var co = this.currentOrnaments;
            for (i in co) {
                if (co.hasOwnProperty(i)) {
                    co[i].remove();
                    delete co[i];
                }
            } //remove any ornaments on screen.
        },
        setCurrentActorsGui: function(e, d, r) {
            var changed = d.actorsChanged,
            newchr = d.actors;
            if (newchr) {
                this.deleteStageActors(e, {
                    actors: newchr
                },
                r);
                this.modifyStageActors(e, {
                    actors: newchr
                },
                r);
            }
            if (changed) {
                this.modifyStageActors(e, {
                    actors: newchr
                },
                r);
            }
        },
        showLibraryGui: function(e, d, r) {
            if (!this.libraryVisible) {
                this.libraryVisible = true;
                if ((d || {}).user) {
                    this.libraryUserShow = true;
                }
                jQuery("#tabs").animate({
                    top: -109
                });
                jQuery("#hideTabs").animate({
                    bottom: 82,
                    height: 27
                }); //todo: pull these values out of csds
            }
        },
        hideLibraryGui: function(e, d, r) {
            if (this.libraryVisible) {
                this.libraryVisible = false;
                if ((d || {}).user) {
                    this.libraryUserShow = false;
                }
                jQuery("#tabs").animate({
                    top: -28
                });
                jQuery("#hideTabs").animate({
                    bottom: 0,
                    height: 0
                });
                jQuery("div.magnify").fadeOut("fast");
                return false;
            }
        },
        _guiPlaying:false,
        showGuiPlaying: function(e, d, r) {
            if(!this._guiPlaying){
                this._guiPlaying = true;
                jQuery("#play a").addClass("pause");
                jQuery("#play").unbind().click(function() {
                    r.cause("guiPause");
                    return false;
                });
                view.hideLibraryGui();
            
                this.charmap[32] = "guiPause";
            }
        },
        showGuiStopped: function(e, d, r) {
            if(this._guiPlaying){
                this._guiPlaying = false;
                jQuery("#play a").removeClass("pause");
                jQuery("#play").unbind().click(function() {
                    r.cause("guiPlay");
                    return false;
                });
                this.charmap[32] = "guiPlay";
            }
        },
        showStageActionGui: function() {
            var sael = this._stageactionel || (this._stageactionel = jQuery("#playbackText"));
            if (!this._stageactionvisible) {
                this._stageactionvisible = true;
                sael.show();
            }
        },
        hideStageActionGui: function() {
            var sael = this._stageactionel || (this._stageactionel = jQuery("#playbackText"));
            if (this._stageactionvisible || this._stageactionvisible === null) {
                this._stageactionvisible = false;
                sael.fadeOut();
            }
        },
        setStageActionGuiText: function(e, d, r) {
            var text = d.text || "(empty)";
            var telmnt;
            if (text !== this.stageActionGuiTextText) {
                telmnt = this.stageActionGuiTextEl || (this.stageActionGuiTextEl = jQuery("#playbackText p"));
                telmnt.text(text);
                this.currentStageActionType = d.id;
                this.stageActionGuiTextText = text;
            }
        },
        selectLibraryGui: function(e, d, r) {
            var lib = d.name;
            if (lib && this.libtabsui) {
                this.libtabsui.tabs("select", lib);
            }
        },
        showAttrGui: function(e, d, r) {
            var i, n, name = d.name,
            cat = d.category,
            subjectEl, firstchr, attr, objectEl;
            n = true;
            if (name !== "#") {
                attr = findAttr(name);
                r.replyTo(e, "showLibraryGui");
                r.replyTo(e, "selectLibraryGui", {
                    name: cat
                });
                jQuery(".magnify").fadeOut("fast");
                attr.fadeIn("fast");
                this.shownAttrGui = name;
                this.shownAttrGuiCategory = d.category;
                if (d.category === "emotions") {
                    if (d.keyname !== d.name) {
                        attr.find("form").hide();
                        attr.find(".disabled").show();
                    } else {
                        attr.find("form").show();
                        attr.find(".disabled").hide();
                    }
                }
                if (d.category === "actions") {
                    if (d.keyname !== d.name) {
                        attr.find("form").hide();
                        attr.find(".disabled").show();
                    } else {
                        attr.find("form").show();
                        attr.find(".disabled").hide();
                    }
                    attr.find("[name=usertext]").attr("value", d.textsuggestion);
                    subjectEl = attr.find("[name=subject]");
                    subjectEl.unbind();
                    subjectEl.bind("click", function(e) {
                        r.cause("actionSubjectGuiChanged", {
                            name: name,
                            subject: e.target.value
                        });
                    });
                    subjectEl.children().remove();
                    objectEl = attr.find("[name=object]");
                    objectEl.children().remove();
                    for (i in d.actors) {
                        if (d.actors.hasOwnProperty(i)) {
                            firstchr = firstchr || i;
                            subjectEl.append("<option value='" + i + "'>" + d.actors[i].content.name + "</option>");
                            n = false;
                        }
                    }
                    if (n) {
                        subjectEl.append("<option value='none'>nobody</option>");
                        objectEl.append("<option value='none'>nobody</option>");
                    }
                    if (!n) {
                        n = true;
                        for (i in d.objects) {
                            if (d.objects.hasOwnProperty(i)) {
                                objectEl.append("<option value='" + i + "'>" + d.objects[i].name + "</option>");
                                n = false;
                            }
                        }
                    }
                    r.cause("actionSubjectGuiChanged", {
                        name: name,
                        subject: firstchr || "none"
                    });
                    attr.find("form").trigger("click");
                }
            }
        },
        disableSoundGui: function(e, d, r) {
            jQuery("#emotions").hide();
            jQuery("#tabNav li a[href=#emotions]").parent().hide();
            jQuery("#soundTrack").hide();
            jQuery("#playHeadHandle").css({
                "height": "34px",
                "background-position": "center -10px"
            });
            jQuery("#mute").hide();
        },
        actionSubjectGuiChanged: function(e, d, r) {
            var attr, objel, el, i;
            attr = findAttr(d.name);
            objel = attr.find("[name=object]");
            el = objel.get(0);
            if (el) {
                for (i = 0; i < el.options.length; i++) {
                    if (el.options[i].value === d.subject) {
                        el.options[i].disabled = true;
                        jQuery(el.options[i]).hide();
                        if (i + 1 < el.options.length) {
                            el.options[i + 1].selected = true;
                        } else {
                            el.options[0].selected = true;
                        }
                    } else {
                        el.options[i].disabled = false;
                        jQuery(el.options[i]).show();
                    }
                }
                attr.find("form").trigger("click");
            }
        },
        hideAttrGui: function(e, d, r) {
            var c = d.category;
            if (this.shownAttrGui && this.shownAttrGuiCategory) {
                if (c) {
                    if (c === this.shownAttrGuiCategory) {
                        findAttr(this.shownAttrGui).fadeOut("fast");
                    }
                } else {
                    findAttr(this.shownAttrGui).fadeOut("fast");
                }
            }
        },
        showActorAttributeButtons: function(e, d, r) {
            var name = d.name,
            chrs = d.actors,
            lin, lim, chr;
            if (chrs) {
                for (chr in chrs) {
                    if (this.charAttributesListElements[chr]) {
                        this.charAttributesListElements[chr].removeClass("minimumNav");
                    } else {
                        lin = jQuery(".menu a[href=#" + chr + "]");
                        lim = lin.parents("li.ngvactor");
                        lim.removeClass("minimumNav");
                        this.charAttributesListElements[chr] = lim;
                    }
                }
            }
            if (name) {
                lin = jQuery(".menu a[href=#" + name + "]");
                lim = lin.parents("li.ngvactor");
                lim.removeClass("minimumNav");
            }
        },
        hideActorAttributeButtons: function(e, d, r) {
            var i, ngvactors;
            ngvactors = this.charAttributesListElements;
            for (i in ngvactors) {
                if (ngvactors.hasOwnProperty(i)) {
                    ngvactors[i].addClass("minimumNav");
                }
            }
        },
        guiShowMuted: function(e, d, r) {
            jQuery("#mute a").addClass("active");
        },
        guiShowUnmuted: function(e, d, r) {
            jQuery("#mute a").removeClass("active");
        },
        guiDroppedToStage: function(e, d, r) {
            var sid, usertext, stagewidth, attr, subject, object, inst;
            stagewidth = 1000 / this.stageWidth;
            attr = findAttr(d.name); //r.replyTo(e,"showAttrGui",{name:d.name});
            //is it a actor or a background or an emotion or an action;
            switch (d.type) {
            case "background":
                r.replyTo(e, "guiBackgroundSelected", {
                    name: d.name
                });
                break;
            case "actor":
                r.replyTo(e, "actorGuiDroppedToStage", {
                    id: d.name,
                    left: (d.left * stagewidth),
                    top: (d.top + 435) * stagewidth,
                    type: d.type
                });
                break;
            case "stageactor":
                r.replyTo(e, "actorGuiDroppedToStage", {
                    id: d.name,
                    left: d.left * stagewidth,
                    top: d.top * stagewidth,
                    type: d.type
                });
                break;
            case "action":
                subject = attr.find("[name=subject]").attr("value");
                object = attr.find("[name=object]").attr("value");
                usertext = attr.find("[name=usertext]").attr("value");
                r.replyTo(e, "actionGuiDroppedToStage", {
                    id: d.name,
                    subject: subject,
                    object: object,
                    usertext: usertext
                });
                break;
            case "emotion":
                inst = attr.find("[name=instrument]:checked").attr("value") || "mix";
                sid = attr.find("[name=sid]").attr("value");
                r.replyTo(e, "emotionGuiDroppedToStage", {
                    id: d.name,
                    sid: sid,
                    instrument: inst
                });
                break;
            }
        },
        init: function(e, d, r) {
            var view, stagewidth, attrmenu;
            function attrmenuclick(e) {
                var t = jQuery(e.target);
                var h = t.attr("href") || t.siblings("a").attr("href");
                h = stripHash(h);
                if (t.parents(".menu").length && !t.parents(".magnify").length) {
                    r.cause("libItemClicked", {
                        name: h
                    });
                }
                if (t.parent(".close").length) {
                    r.cause("attrCloseClicked", {
                        name: h
                    });
                    return false;
                }
            }
            function bindActionToButton(selector, actionname) {
                return jQuery(selector).click(function(e) {
                    var name = jQuery(e.target).attr("href");
                    name = stripHash(name);
                    r.cause(actionname, {
                        name: name
                    });
                });
            }
            function actionFormChanged(e) {
                var form = e.currentTarget;
                var subject = form.subject.value;
                var object = form.object.value;
                var usertext = form.usertext.value;
                var id = form.actionid.value;
                r.cause("actionAttrChanged", {
                    subject: subject,
                    object: object,
                    usertext: usertext,
                    id: id
                });
            }
            view = this;
            stagewidth = this.stageWidth;
            view.editable = jQuery("#stage").hasClass("editable");
            
            r.cause('enableStageClick'); //make text selection difficult for anything that I don't want selected.
            
            jQuery("body").bind("mousedown", function(e) {
                return {
                    "INPUT": true,
                    "SELECT": true,
                    "TEXTAREA": true,
                    "A": true
                } [e.target.tagName] || [false, true, true][e.button] || false;
            }); //enable tabbing
            this.libtabsui = jQuery('#tabs').tabs(); //allow the display of attribute guis for lib items
            attrmenu = jQuery(".menu");
            if(view.editable){
                jQuery("#actions form").bind("click", actionFormChanged).bind("change", actionFormChanged);
                jQuery("#emotions form").bind("click", function(e) {
                    var form = jQuery(e.currentTarget);
                    var id = e.currentTarget.sid;
                    var instrument = form.find("[name=instrument][checked=true]").attr("value");
                    r.cause("emotionAttrChanged", {
                        id: id,
                        instrument: instrument
                    });
                });
                attrmenu.bind("keyup", function(e) {
                    var input = e.target;
                    var form = jQuery(input).parents("form")[0];
                    if(form && form.subject){
                        var subject = form.subject.value;
                        var object = form.object.value;
                        var usertext = form.usertext.value;
                        var id = form.actionid.value;
                        r.cause("actionAttrChanged", {
                            subject: subject,
                            object: object,
                            usertext: usertext,
                            id: id
                        });
                    }
                });
            
                /*export dialogue*/
            
                jQuery(".exportOptions input").bind("click", function(e) {
                    var t = e.target;
                    if(t.getAttribute("type").toLowerCase()=="text") {
                        t.value="";
                    }
                    jQuery(t).unbind("click");
                });
            
                jQuery(".savebutton").bind("submit", function(e) {
                    jQuery(".exportOptions").fadeIn(80);
                    return false;
                });
                jQuery("#cancelbutton").bind("mousedown", function(){
                    jQuery(".exportOptions").fadeOut(80);
                    return false;
                });
                var readScriptButton=jQuery(".readScript");
                jQuery(".exportOptions input#emailaddress").bind("keyup",function(e){
                    if(/@/.test(e.target.value)){
                        readScriptButton.attr("value","Save");
                    } else {
                        readScriptButton.attr("value","Save");
                    }
                });

            
               
                attrmenu.bind("click", attrmenuclick);
                jQuery("#playbackText").bind("click", function(e) {
                    if (e.target.className === "close") {
                        r.cause("guiRemoveFromStage", {
                            type: "action"
                        });
                    } else {
                        r.cause("actionGuiClicked", {
                            id: view.currentStageActionType
                        });
                    }
                    return false;
                });
            
            
                bindActionToButton(".soundpreview","guiSoundPreview");
                bindActionToButton(".turnAround", "guiActorFlip");
                bindActionToButton(".larger", "guiActorEnlarge");
                bindActionToButton(".smaller", "guiActorShrink");
                bindActionToButton(".defaultsize", "guiActorDefaultSize");
                var libitems = jQuery("#backgrounds>ul>li, #actors>ul>li, #emotions>ul>li, #actions>ul>li");
                libitems.draggable({
                    helper: "clone",
                    distance: 10,
                    drag: function() {
                        jQuery(this).css("opacity", "0.5");
                        //attrmenu.unbind("click", attrmenuclick);
                        jQuery("div.magnify").hide();
                        //console.log("libitem")
                    },
                    stop: function() {
                        jQuery(this).css("opacity", "");
                        attrmenu.bind("click", attrmenuclick);
                    }
                });
                //attrmenu draggable
                jQuery("div.magnify").draggable({distance:8}).bind("mouseover",function(){ libitems.draggable("disable"); }).bind("mouseout",function(){ libitems.draggable("enable");});
                
                jQuery("#stage").droppable({
                    accept: "li, .stageactor",
                    tolerance: "touch",
                    cursorAt: {
                        left: 25,
                        top: 28
                    },
                    drop: function(e, ui) {
                        var h = ui.draggable.attr("id").substr(1); //h = stripHash(h);
                        var type = ui.draggable.attr("class");
                        type = (/ngv([^ ]+)/.exec(type) || [])[1] || "";
                        if (ui.draggable.hasClass("stageactor")) {
                            type = "stageactor";
                        }
                        var wd = ui.draggable.width();
                        var ht = ui.draggable.height();
                        if (h && h !== "#") {
                            r.cause("guiDroppedToStage", {
                                name: h,
                                type: type,
                                left: ui.position.left + wd / 2,
                                top: ui.position.top + ht / 2
                            });
                        }
                    }
                });
                jQuery("#stage div.stageactor").live("mouseover", function(e) {
                    jQuery(e.target).css("opacity", 0.5).find("div").show();
                }).live("mouseout", function(e) {
                    if (e.relatedTarget && !jQuery(e.relatedTarget.parentNode).hasClass("stageactor")) {
                        jQuery(e.target).css("opacity", "").find("div").hide();
                    }
                });
                jQuery("#stage div.stageactor div").live("click", function(e) {
                    r.cause("guiRemoveFromStage", {
                        type: "actor",
                        name: e.target.parentNode.getAttribute("id").substr(1)
                    });
                });
            
            
            }
            jQuery("#timeline").click(function(e) {
                r.cause("timelineGuiClicked", {
                    element: e.target.id
                });
            }); //more stuff
            
            function makeControlHandler(evnt) {
                return function() {
                    r.cause(evnt);
                    return false;
                };
            }
            var controlmap = {
                "rewind": "guiRewind",
                "stepBack": "guiFrameBack",
                "play": "guiPlay",
                "pause": "guiPause",
                "stop": "guiStop",
                "stepForward": "guiFrameForward",
                "fastForward": "guiFastForward",
                "mute": "guiMute",
                "delete": "guiRemoveFrame",
                "addFrame": "guiAddFrame"
            }; //bind handlers to control buttons!
            for (var i in controlmap) {
                if (controlmap.hasOwnProperty(i)) {
                    jQuery("#" + i).click(makeControlHandler(controlmap[i]));
                }
            }
            jQuery("#sceneDescription").bind("keyup", function(e) {
                r.cause("sceneDescriptionGuiChanged", {
                    text: this.value
                });
            });
            jQuery("form").bind("submit", function(e) {
                if (e.target.id !== "makenewmovie") {
                    return false;
                }
            });
            jQuery("#hideTabs").click(function() {
                r.cause("hideLibraryGui", {
                    user: true
                });
                return false;
            });
            jQuery("#tabs ul a").click(function(e) {
                r.cause("showLibraryGui", {
                    user: true
                });
            });
            var ht = jQuery("html");
            ht.bind("keydown", function(e) {
                var ename = view.charmap[e.charCode || e.keyCode];
                if (ename && e.target.tagName !== "INPUT" && e.target.tagName !=="TEXTAREA") {
                    r.cause(ename);
                    e.stopPropagation();
                    e.preventDefault();
                    e.cancelBubble=true;
                    return false;
                    
                }
                
            });

            
            
        },
        backspace:function(e,d,r){
            
        },
        charmap: {
            32: "guiPlay",
            8: "backspace"
        },
        keymap: {}
    };
    timelineview = {
        frameDuration: 200,
        frameSize: 5,
        frameTrackLeftMargin: 54,
        frametrackLength: 1,
        soundtrackLength: 0,
        gidCounter: 1,
        playheadHandlePosition: 0,
        playheadHeld: false,
        playheadrangemax: 852,
        playheadLeftOffset: -48,
        playheadPosition: 0,
        playheadVisible: false,
        playheadDraggable: true,
        scrolltimer: 0,
        timelineViewPosition: 0,
        timelineWidth: 0,
        timelineBGAlignment: 6,
        _prevFrameTrack: {},
        _prevSoundTrack: {},
        init: function(e, d, r) {
            var view = this;
            var frameSize, frameDuration;
            view = this;
            this.timelineWidth = jQuery("#timeline").width();
            frameSize = this.frameSize;
            frameDuration = this.frameDuration;
            var ht = jQuery("html");
            var ft = jQuery("#frameTrack");
            var tl = jQuery("#timelineContainer");
            view.playheadrangemax=tl.width()-110;
            var ph = jQuery("#playHeadHandle");
            var buttonPressed;
            ht.bind("mousemove", function(e) {
                view.playheadMouseX = e.pageX - tl.offset().left - view.frameTrackLeftMargin;
                if (buttonPressed !== 0 && e.button === 0) {
                    ht.trigger("mouseup");
                }
                buttonPressed = e.button;
            });
            ph.bind("mousedown", function(e) {
                r.replyTo(e, "playheadHandleGuiDragged", {
                    left: view.playheadMouseX
                });
            });
            ht.bind("dragstart", function() {
                return false;
            }).bind("selectstart", function(e) {
                if (e.target.tagName !== "INPUT") {
                    return false;
                }
            });
            function dragstop() {
                r.replyTo(e, "playheadHandleGuiDragStop", {
                    left: view.playheadMouseX
                });
            }
            jQuery(window).bind("mouseup", dragstop);
            jQuery(document).bind("mouseup", dragstop);
            function setupFrameResize() {
                var ftintv = 0;
                var stel = null;
                var width;
                var pfts; //previous frametrack
                var ftel = jQuery(".editable #frameTrack");
                var next = [];
                function tdragstop(e) { 
                    if (pfts !== undefined) {
                        setTimeout(function() {
                            for (var j = 0; j < next.length; j++) {
                                (next[j][0].remove());
                                (next[j][2].show());
                            }
                            next = [];
                        },
                        40);
                        r.cause("guiFrameResized", {
                            width: width,
                            index: pfts
                        });
                        pfts = undefined;
                    }
                    clearInterval(ftintv);
                    stel = null;
                }
                if (ftel.length) {
                    ft.bind("mousedown", function(e) {
                        clearInterval(ftintv);
                        var pft = view._prevFrameTrack;
                        var i = Math.round((view.timelineViewPosition + view.playheadMouseX) / view.frameSize);
                        pfts = stel === null ? (stel = findPrevKey(i - 1, pft)) : stel;
                        var j, iwidth, fel, felc;
                        next = [];
                        for (iwidth = j = findNextKey(i, pft, i + 250) + 1; j < (i + 250); j = findNextKey(j, pft, i + 250) + 1) {
                            if (pft[j - 1]) {
                                fel = pft[j - 1];
                                felc = fel.clone();
                                felc.addClass("helper");
                                next.push([felc, fel.css("left"), fel]);
                                fel.hide();
                                ftel.append(felc);
                            }
                        }
                        iwidth = iwidth - pfts;
                        if (pft && pft[pfts] && view.frametrackLength - 1 !== pfts) {
                            ftintv = setInterval(function() {
                                var ci = Math.round((view.timelineViewPosition + view.playheadMouseX) / view.frameSize);
                                width = Math.max(1, iwidth - 1 + (ci - i));
                                if (pft[pfts]) {
                                    pft[pfts].css("width", width * view.frameSize - 1);
                                }
                                for (var j = 0; j < next.length; j++) {
                                    var o = next[j];
                                    o[0].css("left", parseInt(o[1], 10) + (width - iwidth + 1) * view.frameSize);
                                }
                            },
                            20);
                        }
                    });
                    jQuery(window).bind("mouseup", tdragstop);
                    jQuery(document.documentElement).bind("mouseup", tdragstop);
                }
            }
            setupFrameResize();
        },

        disablePlayheadDrag: function (e, d, r) {
            this.playheadDraggable=false;
        },
        enablePlayheadDrag: function (e, d, r) {
            this.playheadDraggable=true;
        },
        setFramesGui: function(e, d, r) {
            var ftlm = this.frameTrackLeftMargin;
            function equals(prev, curr) {
                return prev[0] === curr[0] && prev[1] === curr[1];
            }
            function updateElement(el, state, i) {
                var w = state[0] || 1;
                var act = state[1];
                el.css({
                    position: "absolute",
                    left: ftlm + i * timelineview.frameSize,
                    width: w * timelineview.frameSize - 1
                });
                if (act) {
                    el.attr("class", "active");
                } else {
                    el.attr("class", "");
                }
            }
            updateTrack("frameTrack", timelineview._prevFrameTrack, d, equals, updateElement);
            this.frametrackLength = d.length;
            r.replyTo(e, "setTimelineWidthGui", {
                width: (this.frametrackLength > this.soundtrackLength ? this.frametrackLength : this.soundtrackLength) * this.frameSize + 800
            });
        },
        setSoundsGui: function(e, d, r) {
            var ftlm = this.frameTrackLeftMargin;
            function updateElement(el, state, i) {
                var w = state.length || 0;
                var name = state.name || "";
                var type = {
                    mix: "Ensemble",
                    b: "Taiko",
                    a: "Shakuhachi",
                    c: "Shamisen"
                } [state.type] || "";
                var si = state.startindex;
                el.css({
                    position: "absolute",
                    left: ftlm + si * timelineview.frameSize,
                    width: w * timelineview.frameSize - 1
                });
                el.unbind("click");
                el.html(name + " " + type + " <div class=\"close\">close</div>");
                el.bind("click", function(e) {
                    if (e.target.className === "close") {
                        r.replyTo(e, "guiRemoveFromStage", {
                            type: "emotion",
                            sid: i,
                            startindex: si
                        });
                    } else {
                        r.replyTo(e, "stageEmotionClicked", {
                            type: "emotion",
                            sid: i,
                            startindex: si,
                            name: name
                        });
                    }
                });
            }
            function equals(a, b) {
                return a.startindex === b.startindex && a.type === b.type;
            }
            updateTrack("soundTrack", timelineview._prevSoundTrack, d, equals, updateElement);
            this.soundtrackLength = d.length;
            r.replyTo(e, "setTimelineWidthGui", {
                width: (this.frametrackLength > this.soundtrackLength ? this.frametrackLength : this.soundtrackLength) * this.frameSize + 300
            });
        },
        removeSoundGui: function(e, d, r) {
            r.replyTo(e, "guiRemoveFromStage", {
                type: "emotion",
                sid: d.sid
            });
        },
        startScroll: function(e, d, r) {
            if (!this.scrolltimer) {
                this.scrolltimer = r.causeEveryNFrame(1, "moveTimelineGui", {});
            }
        },
        stopScroll: function(e, d, r) {
            r.clearEvery(this.scrolltimer);
            this.scrolltimer = 0;
        },
        playheadHandleGuiDragged: function(e, d, r) { //r.replyTo(e, 'playheadHandleMoved');
            if (this.playheadDraggable) {
                this.playheadHeld = true;
                var left = d.left || this.playheadHandlePosition;
                this.playheadHandlePosition = left;
                if (this.timelineStable) {
                    this.timelineStable = false;
                    r.replyTo(e, 'startScroll');
                }
            }
        },
        playheadHandleGuiDragStop: function(e, d, r) { //r.replyTo(e, 'playheadHandleMoved');
            this.playheadHeld = false;
            var left = d.left || this.playheadHandlePosition;
            this.playheadHandlePosition = left;
            
        },
        playheadHandleEl: null,
        playheadHandleText: "1",
        setPlayheadHandleTextGui: function(e, d, r) {
            var phh, frameIndex;
            if (d.text !== this.playheadHandleText) {
                this.playheadHandleText = d.text;
                phh = this.playheadHandleEl || (this.playheadHandleEl = jQuery("#playHeadHandle"));
                frameIndex = d.text;
                phh.text(d.text.toString());
            }
        },
        moveTimelineGui: function(e, d, r) {
            var fs = this.frameSize;
            function calculatePlayheadViewX(px, txd, tw, mousex, uc) {
                
                var vpxd = uc ? mousex : px - txd;
                return vpxd > tw ? tw : vpxd < 0 ? 0 : isNaN(vpxd) ? 0 : vpxd;
            }
            function calculatePlayheadX(vpx, tx, uw, px, uc) {
                var pxd = uc ? vpx + tx : px;
                
                pxd = pxd > uw ? uw : pxd < 0 ? 0 : isNaN(pxd) ? 0 : pxd;
                pxd = (pxd % fs) !== 0 ? pxd - 1 : pxd;
                return pxd;
            }
            function calculateTimelineViewX(tx, px, tw, uw, g) {
                var txd = tx + (g * 10);
                return txd < 0 ? 0 : txd > (uw - tw) ? uw - tw : isNaN(txd) ? 0 : txd;
            }
            function calculateViewForce(tx, px, tw) {
                var twd = tw / 2;
                var pxd = (px - tx) / twd - 1;
                return pxd * 2;
            }
            function calculatePlayheadOutOfView(px, txd, tw) {
                return px + 1 < txd ? true : px > txd + tw ? true : false;
            }
            function calculateFrame(vpx, px, tx, tw, uw, uc, mousex) {
                var pxd = calculatePlayheadX(vpx, tx, uw, px, uc);
                var g = calculateViewForce(tx, px, tw);
                var txd = calculateTimelineViewX(tx, px, tw, uw, g);
                var vpxd = calculatePlayheadViewX(pxd, txd, tw, mousex, uc);
                var phc = calculatePlayheadOutOfView(pxd, txd, tw);
                return {
                    vpx: vpxd,
                    px: pxd,
                    tx: txd,
                    phc: phc
                };
            }
            var cleft = this.playheadLeftOffset;
            var vpx = this.playheadHandlePosition;
            var px = this.playheadPosition;
            var tx = this.timelineViewPosition;
            var tw = this.playheadrangemax;
            var uw = this.timelineWidth;
            var uc = this.playheadHeld;
            var mousex = this.playheadMouseX;
            var phc = this.playheadVisible;
            var out = calculateFrame(vpx, px, tx, tw, uw, uc, mousex);
            this.playheadHandlePosition = Math.round(out.vpx);
            var vpxd = this.playheadHandlePosition;
            this.playheadPosition = Math.round(out.px);
            this.timelineViewPosition = Math.round(out.tx);
            var txd = this.timelineViewPosition;
            this.playheadVisible = out.phc;
            var phcd = this.playheadVisible;
            if (uc) {
                r.replyTo(e, "guiPlayheadDragged", {
                    position: this.playheadPosition / this.frameSize * this.frameDuration
                });
            }
            if (tx !== txd) {
                jQuery("#timeline").css("left", -txd);
            }
            if (vpx !== vpxd || phc !== phcd) {
               
                jQuery("#playHead").css({
                    "left": vpxd - cleft,
                    opacity: phcd ? 0.5 : ""
                });
            }
        },
        movePlayheadGui: function(e, d, r) {
            var position = d.position === undefined ? d.frameIndex * this.frameDuration : d.position;
            if (!isNaN(position)) {
                this.playheadPosition = (position / this.frameDuration) * this.frameSize;
            }
            r.replyTo(e, 'startScroll');
        },
        setTimelineWidthGui: function(e, d, r) {
            var width = d.width > 5056 ? d.width : 5056;
            var bga = this.timelineBGAlignment;
            if (typeof width === "number" && width > 0) {
                var timelineEl = jQuery("#timeline");
                var soundtrackEl = jQuery("#soundTrack");
                var frametrackEl = jQuery("#frameTrack");
                timelineEl.css("width", width + bga);
                soundtrackEl.css("width", width + bga);
                frametrackEl.css("width", width + bga);
                this.timelineWidth = width;
            }
        }
    };
    ngv.ed.registerListener(view);
    ngv.ed.registerListener(timelineview);
    ngv.view = view;
})();

