function $(id) {
    return document.getElementById(id);
}

if (!window.cytv) {
    cytv = {};
}

cytv.XHR = function() {
    var self = this;
    
    self.performRequest = function(url, handler) {
        var xhr;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else if (window.ActiveXObject) {
            try {
                xhr = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
            }
        } else {
            throw new Error("Cannot create an XMLHttpRequest object");
        }
        xhr.onreadystatechange = function() {
            if (xhr.readyState == 4) {
                if (xhr.status < 300) {
                    var result;
                    var handlerCalled = false;
                    try {
                        result = eval("(" + xhr.responseText + ")");
                    } catch (e) {
                        handler(null, e);
                        handlerCalled = true;
                    }
                    if (!handlerCalled) {
                        handler(result, null);
                    }
                } else {
                    handler(null, new Error("HTTP code " + xhr.status + ": " + xhr.statusText));
                }
            }
        }
        xhr.open("GET", url, true);
        xhr.send("");
    }
};
cytv.XHR = new cytv.XHR();

cytv.gui = {};
cytv.gui.PositionBar = function() {
    var self = this;
    
    self.init = function(delegate) {
        self.delegate = delegate;
        var positionBarElement = $("positionBar");
        var positionIndicatorElement = $("positionIndicator");
        var barWidth = parseInt(positionBarElement.style.width);
        var barBorderWidth = parseInt(positionBarElement.style.borderLeftWidth);
        var barLeft = parseInt(positionBarElement.style.left);
        var indicatorWidth = parseInt(positionIndicatorElement.style.width);
        var indicatorLeft = parseInt(positionIndicatorElement.style.left);
        self.width = barWidth;
        self.maxPos = barWidth - indicatorWidth;
        self.mouseOffset = barLeft + barBorderWidth + Math.round(indicatorWidth * 1.5);
        self.displayOffset = indicatorLeft;
        self.pos = 0;
        self.positionBarElement = positionBarElement;
        self.positionIndicatorElement = positionIndicatorElement;
        self.mouseIsDown = false;
        positionBarElement.onmousedown = positionIndicatorElement.onmousedown = function(evt) {
            self.mouseIsDown = true;
            var requestedPos = evt.pageX - self.mouseOffset;
            if (requestedPos != self.pos) {
                var relativePos = requestedPos / self.maxPos;
                if (relativePos < 0) {
                    relativePos = 0;
                } else if (relativePos > 1) {
                    relativePos = 1;
                }
                delegate.positionRequested(relativePos);
            }
        };
        positionBarElement.onmouseup = positionIndicatorElement.onmouseup = function(evt) {
            self.mouseIsDown = false;
            var requestedPos = evt.pageX - self.mouseOffset;
            if (requestedPos != self.pos) {
                var relativePos = requestedPos / self.maxPos;
                if (relativePos < 0) {
                    relativePos = 0;
                } else if (relativePos > 1) {
                    relativePos = 1;
                }
                delegate.positionRequested(relativePos);
            }
        };
        positionBarElement.onmousemove = function(evt) {
            if (self.mouseIsDown) {
                var requestedPos = evt.pageX - self.mouseOffset;
                if (requestedPos != self.pos) {
                    var relativePos = requestedPos / self.maxPos;
                    if (relativePos < 0) {
                        relativePos = 0;
                    } else if (relativePos > 1) {
                        relativePos = 1;
                    }
                    delegate.positionRequested(relativePos);
                }
            }
        };
    };

    self.destroy = function() {
        // TODO
    }

    self.setPosition = function(relativePos) {
        self.pos = Math.round(relativePos * self.maxPos);
        self.positionIndicatorElement.style.left = self.displayOffset + self.pos + "px";
    };
    
    self.updateSize = function(delta) {
        var relativePos = self.pos / self.maxPos;
        self.width += delta;
        self.maxPos += delta;
        self.positionBarElement.style.width = self.width + "px";
        self.setPosition(relativePos);
    };
};

cytv.gui.Core = function() {
    var self = this;
    var mDeinterlace = false;
    var mFirstPlayFunction = null;
    var mCurrentPlayFunction = null;
    var mSeekTo = null;
    var mLastPlaybackPosition = null;
    var mPATReceived = false;
    var mTranscodeSettings;
    var mCurrentTimeout = null;

    var mTranscodePresets = [ { transcodePresetName:"Slow broadband (256 kbps upstream)",
                                transcodeWidth:"160", transcodeHeight:"128",
                                transcodeAudioSync:"true",
                                transcodeVideoCodec:"h263p", transcodeAudioCodec:"mp2",
                                transcodeContainerFormat:"avi",
                                transcodeVideoBitrate:"160", transcodeAudioBitrate:"32",
                                transcodeMono:"true", transcodeAudioSampleRate:"22050",
                                transcodeFrameRate:"12.5", transcodeDeinterlace:"false" },
                              { transcodePresetName:"Medium broadband (512 kbps upstream)",
                                transcodeWidth:"160", transcodeHeight:"128",
                                transcodeAudioSync:"true",
                                transcodeVideoCodec:"h263p", transcodeAudioCodec:"mp2",
                                transcodeContainerFormat:"avi",
                                transcodeVideoBitrate:"256", transcodeAudioBitrate:"128",
                                transcodeMono:"false", transcodeAudioSampleRate:"44100",
                                transcodeFrameRate:"original", transcodeDeinterlace:"false" },
                              { transcodePresetName:"Fast broadband (1024 kbps upstream)",
                                transcodeWidth:"320", transcodeHeight:"256",
                                transcodeAudioSync:"true",
                                transcodeVideoCodec:"h263p", transcodeAudioCodec:"mp2",
                                transcodeContainerFormat:"avi",
                                transcodeVideoBitrate:"640", transcodeAudioBitrate:"128",
                                transcodeMono:"false", transcodeAudioSampleRate:"44100",
                                transcodeFrameRate:"original", transcodeDeinterlace:"false" },
                              { transcodePresetName:"WLAN (802.11b, theoretical 11 Mbit/s)",
                                transcodeWidth:"640", transcodeHeight:"512",
                                transcodeAudioSync:"true",
                                transcodeVideoCodec:"h263p", transcodeAudioCodec:"mp2",
                                transcodeContainerFormat:"avi",
                                transcodeVideoBitrate:"1280", transcodeAudioBitrate:"128",
                                transcodeMono:"false", transcodeAudioSampleRate:"44100",
                                transcodeFrameRate:"original", transcodeDeinterlace:"false" },
                              { transcodePresetName:"WLAN (802.11g, theoretical 54 Mbit/s)",
                                transcodeWidth:"640", transcodeHeight:"512",
                                transcodeAudioSync:"true",
                                transcodeVideoCodec:"h263p", transcodeAudioCodec:"mp2",
                                transcodeContainerFormat:"avi",
                                transcodeVideoBitrate:"2560", transcodeAudioBitrate:"192",
                                transcodeMono:"false", transcodeAudioSampleRate:"44100",
                                transcodeFrameRate:"original", transcodeDeinterlace:"false" }];

    var copyTranscodeSettings = function(settings) {
        mTranscodeSettings = {};
        for (var key in settings) {
            mTranscodeSettings[key] = settings[key];
        }
    };

    var displayTranscodeSettings = function(settings) {
        for (var key in settings) {
            var element = $(key);
            if (element.type == "checkbox") {
                element.checked = (settings[key] == "true");
            } else {
                $(key).value = settings[key];
            }
        }
    };

    var applyTranscodeSettings = function() {
        var transcodeSuffix = "";
        for (var key in mTranscodeSettings) {
            var element = $(key);
            if (element.type == "checkbox") {
                if (element.checked) {
                    mTranscodeSettings[key] = "true";
                } else {
                    mTranscodeSettings[key] = "false";
                }
            } else {
                mTranscodeSettings[key] = element.value;
            }
            if (key != "transcodePresetName") {
                transcodeSuffix += "&" + key + "=" + encodeURIComponent(mTranscodeSettings[key]);
            }
        }
        var transcodeLinks = document.getElementsByTagName("a");
        for (var i = 0; i < transcodeLinks.length; ++i) {
            var transcodeIndex = transcodeLinks[i].href.indexOf("transcode=true");
            if (transcodeIndex != -1) {
                transcodeLinks[i].href = transcodeLinks[i].href.substring(0, transcodeIndex + 14) + transcodeSuffix;
            }
        }
    };

    self.restartVideo = function() {
        if (vlc.playlist.isPlaying) {
            var currentPos = vlc.input.position;
            mSeekTo = currentPos;
            mCurrentPlayFunction();
        }
    };

    self.init = function() {
        self.positionBar = new cytv.gui.PositionBar();
        self.positionBar.init(self);
        self.videoWidth = vlc.width;
        self.showingPlayButton = true;
        vlc.log.verbosity = 2;
        var pauseButton = $("pauseButton");
        pauseButton.onclick = function() {
            if (vlc.playlist.itemCount > 0) {
                vlc.playlist.togglePause();
            } else if (mFirstPlayFunction != null) {
                mFirstPlayFunction();
            }
        };
        var videoOptionsElement = $("videoOptionsContainer");
        var transcodeOptionsElement = document.createElement("div");
        //transcodeOptionsElement.style.width = "100px";
        //transcodeOptionsElement.style.height = "100px";
        transcodeOptionsElement.style.display = "none";
        transcodeOptionsElement.className = "transcodeOptionsContainer";
        transcodeOptionsElement.style.position = "absolute";
        transcodeOptionsElement.innerHTML = "Width: <select id='transcodeWidth'>" +
            "<option value='original'>Original</option>" +
            "<option value='160'>160</option>" +
            "<option value='176'>760</option>" +
            "<option value='320'>320</option>" +
            "<option value='352'>352</option>" +
            "<option value='640'>640</option>" +
            "<option value='704'>704</option>" +
            "</select> Height: <select id='transcodeHeight'>" +
            "<option value='original'>Original</option>" +
            "<option value='128'>128</option>" +
            "<option value='144'>144</option>" +
            "<option value='256'>256</option>" +
            "<option value='288'>288</option>" +
            "<option value='512'>512</option>" +
            "<option value='576'>576</option>" +
            "</select> Frame rate: <select id='transcodeFrameRate'>" +
            "<option value='original'>Original</option>" +
            "<option value='10'>10</option>" +
            "<option value='12.5'>12.5</option>" +
            "<option value='14.985'>14.985</option>" +
            "<option value='15'>15</option>" +
            "<option value='20'>20</option>" +
            "<option value='25'>25</option>" +
            "<option value='29.97'>29.97</option>" +
            "<option value='30'>30</option>" +
            "</select><p></p>Video bitrate: <select id='transcodeVideoBitrate'>" +
            "<option value='128'>128</option>" +
            "<option value='160'>160</option>" +
            "<option value='256'>256</option>" +
            "<option value='320'>320</option>" +
            "<option value='512'>512</option>" +
            "<option value='640'>640</option>" +
            "<option value='1024'>1024</option>" +
            "<option value='1280'>1280</option>" +
            "<option value='2048'>2048</option>" +
            "<option value='2560'>2560</option>" +
            "<option value='4096'>4096</option>" +
            "<option value='5120'>5120</option>" +
            "</select> Audio bitrate: <select id='transcodeAudioBitrate'>" +
            "<option value='32'>32</option>" +
            "<option value='64'>64</option>" +
            "<option value='96'>96</option>" +
            "<option value='128'>128</option>" +
            "<option value='160'>160</option>" +
            "<option value='192'>192</option>" +
            "<option value='256'>256</option>" +
            "<option value='320'>320</option>" +
            "</select><p></p>Audio sample rate: <select id='transcodeAudioSampleRate'>" +
            "<option value='22050'>22050</option>" +
            "<option value='44100'>44100</option>" +
            "<option value='48000'>48000</option>" +
            "</select> <input type='checkbox' id='transcodeMono'></input>Mono " +
            "<input type='checkbox' id='transcodeAudioSync'></input>Sync audio<p></p>" +
            "Video codec: <input type='text' size='8' id='transcodeVideoCodec'></input> " +
            "Audio codec: <input type='text' size='8' id='transcodeAudioCodec'></input><p></p>" +
            "Container format: <input type='text' size='8' id='transcodeContainerFormat'></input> " +
            "<input type='checkbox' id='transcodeDeinterlace'></input>Deinterlace<p></p>" +
            "Preset: <select id='transcodePresetName'></select> " +
            "<input type='button' id='loadPresetButton' value='Load preset'></input><p></p>" +
            "<input type='button' id='transcodeOptionsOKButton' value='OK'></input> " +
            "<input type='button' id='transcodeOptionsApplyButton' value='Apply'></input> " +
            "<input type='button' id='transcodeOptionsCancelButton' value='Cancel'></input>"
        videoOptionsElement.appendChild(transcodeOptionsElement);
        var deinterlaceElement = document.createElement("input");
        deinterlaceElement.type = "checkbox";
        deinterlaceElement.onchange = function() {
            var oldValue = mDeinterlace;
            mDeinterlace = deinterlaceElement.checked;
            if (mDeinterlace != oldValue) {
                self.restartVideo();
            }
        }
        videoOptionsElement.appendChild(deinterlaceElement);
        videoOptionsElement.appendChild(document.createTextNode("Deinterlace"));
        videoOptionsElement.appendChild(document.createElement("p"));
        var transcodeOptionsButtonElement = document.createElement("input");
        transcodeOptionsButtonElement.type = "button";
        transcodeOptionsButtonElement.value = "Transcode options ...";
        transcodeOptionsButtonElement.onclick = function() {
            transcodeOptionsElement.style.display = "";
        };
        videoOptionsElement.appendChild(transcodeOptionsButtonElement);

        for (var i = 0; i < mTranscodePresets.length; ++i) {
            var presetOption = document.createElement("option");
            presetOption.value = mTranscodePresets[i].transcodePresetName;
            presetOption.appendChild(document.createTextNode(mTranscodePresets[i].transcodePresetName));
            $("transcodePresetName").appendChild(presetOption);
        }
        $("loadPresetButton").onclick = function() {
            var selectedPreset = $("transcodePresetName").value;
            for (var i = 0; i < mTranscodePresets.length; ++i) {
                if (mTranscodePresets[i].transcodePresetName == selectedPreset) {
                    displayTranscodeSettings(mTranscodePresets[i]);
                    break;
                }
            }
        };
        $("transcodeOptionsOKButton").onclick = function() {
            applyTranscodeSettings();
            transcodeOptionsElement.style.display = "none";
        };
        $("transcodeOptionsApplyButton").onclick = function() {
            applyTranscodeSettings();
        };
        $("transcodeOptionsCancelButton").onclick = function() {
            transcodeOptionsElement.style.display = "none";
            displayTranscodeSettings(mTranscodeSettings);
        };
        copyTranscodeSettings(mTranscodePresets[0]);
        displayTranscodeSettings(mTranscodeSettings);

        self.pollerInterval = window.setInterval(self.vlcPoller, 1000);
    };
    
    self.positionRequested = function(relativePos) {
        if (relativePos < 0.000001) {
            relativePos = 0.000001;
        } else if (relativePos > 0.999999) {
            relativePos = 0.999999;
        }
        try {
            vlc.input.position = relativePos;
        } catch (e) {
            // never mind
        }
    };
    
    self.setPlaybackPosition = function(relativePos) {
        if (relativePos < 0) {
            relativePos = 0;
        } else if (relativePos > 1) {
            relativePos = 1;
        }
        self.positionBar.setPosition(relativePos);
    };
    
    self.setVideoWidth = function(newWidth) {
        newWidth = Math.round(newWidth);
        if (newWidth < 16) {
            newWidth = 16;
        } else if (newWidth > 4096) {
            newWidth = 4096;
        }
        if (newWidth != self.videoWidth) {
            self.positionBar.updateSize(newWidth - self.videoWidth);
            self.videoWidth = newWidth;
            vlc.width = newWidth;
            $("videoOptionsContainer").style.marginLeft = newWidth + "px";
        }
    };

    self.destroy = function() {
        // TODO (unhook pause handler and clear stream click handlers + clear poller interval)
    };
    
    self.displayControls = function(controls) {
        $("controlsContainer").appendChild(controls);
    };
    
    self.displayStreams = function(categories) {
        mFirstPlayFunction = null;
        var container = $("sourcesContainer");
        container.style.display = "none";
        var oldLinks = container.getElementsByTagName("a");
        var oldLinkCount = oldLinks.length;
        for (var i = 0; i < oldLinkCount; ++i) {
            oldLinks[i].onclick = null;     // prevent IE mem leaks
        }
        container.innerHTML = "";

        var categoryCount = categories.length;
        for (var i = 0; i < categoryCount; ++i) {
            var category = categories[i];
            var categoryContainerElement = document.createElement("div");
            categoryContainerElement.className = "categoryContainer";
            var categoryTitleElement = document.createElement("div");
            categoryTitleElement.className = "categoryTitle";
            categoryTitleElement.appendChild(document.createTextNode(category.title));
            categoryContainerElement.appendChild(categoryTitleElement);
            var categoryStreamsElement = document.createElement("div");
            categoryStreamsElement.className = "categoryStreamsContainer";
            
            var streams = category.streams;
            var streamCount = streams.length;
            for (var j = 0; j < streamCount; ++j) {
                var streamElement = document.createElement("div");
                streamElement.className = "streamContainer";
                
                var previewPictureElement = document.createElement("div");
                previewPictureElement.className = "streamPreviewPictureContainer";
                var previewPictureImageElement = document.createElement("img");
                previewPictureImageElement.className = "streamPreviewPictureImage";
                var previewPictureURL = streams[j].previewPictureURL;
                previewPictureImageElement.src = previewPictureURL;
                previewPictureElement.appendChild(previewPictureImageElement);
                streamElement.appendChild(previewPictureElement);
                
                //var streamInfoElement = document.createElement("div");
                //streamInfoElement.className = "streamInfoContainer";
                
                var titleElement = document.createElement("div");
                titleElement.className = "streamTitle";
                var title = streams[j].title;
                if (title == null) {
                    title = "\xa0";
                }
                titleElement.appendChild(document.createTextNode(title));
                streamElement.appendChild(titleElement);

                var episodeElement = document.createElement("div");
                episodeElement.className = "streamEpisode";
                var episode = streams[j].episode;
                if (episode == null) {
                    episode = "\xa0";
                }
                episodeElement.appendChild(document.createTextNode(episode));
                streamElement.appendChild(episodeElement);
                
                var streamActionsElement = document.createElement("div");
                streamActionsElement.className = "streamActionsContainer";
                var playLink = document.createElement("a");
                playLink.className = "streamPlayLink";
                playLink.href = "javascript:void(0)";
                playLink.onclick = self.generatePlayFunction(streamElement, 2, false);
                playLink.appendChild(document.createTextNode("[Play]"));
                streamActionsElement.appendChild(playLink);
                var playTranscodedLink = document.createElement("a");
                playTranscodedLink.className = "streamPlayTranscodedLink";
                playTranscodedLink.href = "javascript:void(0)";
                playTranscodedLink.onclick = self.generatePlayFunction(streamElement, 3, true);
                playTranscodedLink.appendChild(document.createTextNode("[Play transcoded]"));
                streamActionsElement.appendChild(playTranscodedLink);
                var downloadLink = document.createElement("a");
                downloadLink.className = "streamDownloadLink";
                downloadLink.href = streams[j].url;
                downloadLink.appendChild(document.createTextNode("[Download]"));
                streamActionsElement.appendChild(downloadLink);
                var downloadTranscodedLink = document.createElement("a");
                downloadTranscodedLink.className = "streamDownloadTranscodedLink";
                downloadTranscodedLink.href = streams[j].url;
                if (downloadTranscodedLink.href.indexOf("?") == -1) {
                    downloadTranscodedLink.href += "?transcode=true";
                } else {
                    downloadTranscodedLink.href += "&transcode=true";
                }
                downloadTranscodedLink.appendChild(document.createTextNode("[Download transcoded]"));
                streamActionsElement.appendChild(downloadTranscodedLink);
                streamElement.appendChild(streamActionsElement);

                var streamDummyElementForStyling = document.createElement("div");
                streamDummyElementForStyling.className = "streamDummyForStyling";
                streamElement.appendChild(streamDummyElementForStyling);

                categoryStreamsElement.appendChild(streamElement);
            }

            categoryContainerElement.appendChild(categoryStreamsElement);
            container.appendChild(categoryContainerElement);
        }

        applyTranscodeSettings();
        container.style.display = "";
    };

    self.generatePlayFunction = function(element, linkIndex, transcoded) {
        var playFunction = function() {
            if (transcoded) {
                mSeekTo = null;
            }
            if (mCurrentTimeout != null) {
                window.clearTimeout(mCurrentTimeout);
            }
            //vlc.parentNode.innerHTML = vlc.parentNode.innerHTML;
            //vlc = $("vlc");
            if (vlc.playlist.itemCount > 0) {
                vlc.playlist.clear();
            }
            mCurrentTimeout = window.setTimeout(function() {
                // do this in a timeout - otherwise, changes to the video filter don't stick
                vlc.playlist.add(element.getElementsByTagName("a")[linkIndex].href,
                                 null, (mDeinterlace ? ":vout-filter=deinterlace" : ""));
                vlc.playlist.play();
                try {
                    vlc.playlist.next();
                } catch (e) {
                    // never mind
                }
                mCurrentPlayFunction = playFunction;
            }, 500);
            //vlc.playlist.next();    // workaround for VLC plugin bug
            //throw new Error("Count: " + vlc.playlist.itemCount);
            //vlc.playlist.next();    // workaround for VLC plugin bug
            //vlc.playlist.prev();    // workaround for VLC plugin bug
        };
        if (mFirstPlayFunction == null) {
            mFirstPlayFunction = playFunction;
        }
        return playFunction;
    };
    
    var logMsg = function(msg) {
        if (msg.message.indexOf("    -") != 0 &&
            msg.message.indexOf("PSINewTableCallBack") != 0 &&
            msg.message.indexOf("  *" != 0)) {
            window.setTimeout(function() {
                throw new Error(msg.message);
            }, 0);
        }
    };

    self.vlcPoller = function(url) {
        if (vlc.playlist.isPlaying) {
            var isPlaying = true;
            if (self.showingPlayButton) {
                self.showingPlayButton = false;
                $("pauseButton").style.backgroundImage = "url(pause.png)";
            }
        } else {
            isPlaying = false;
            if (!self.showingPlayButton) {
                self.showingPlayButton = true;
                $("pauseButton").style.backgroundImage = "url(play.png)";
            }
        }
        try {
            try {
                var debugWidth = vlc.video.width;
            } catch (e) {}
            try {
                var debugHasVout = vlc.input.hasVout;
            } catch (e) {}
            try {
                var debugFps = vlc.input.fps;
            } catch (e) {}
            try {
                var debugState = vlc.input.state;
            } catch (e) {}
            try {
                var debugPosition = vlc.input.position;
            } catch (e) {}
            try {
                //var msgIterator = vlc.log.messages.iterator();
                //while (msgIterator.hasNext) {
                //    var msg = msgIterator.next();
                //    var message = msg.message;
                //    var error = false;
                //    if (message.indexOf("PATCallBack") == 0) {
                //        if (mPATReceived) {
                //            mPATReceived = false;
                //            error = true;
                //        } else {
                //            mPATReceived = true;
                //        }
                //    } else if (message.indexOf("pmt error") == 0) {
                //        error = true;
                //    }
                //    if (error) {
                //        window.setTimeout(function() {
                //            self.restartVideo();
                //            throw new Error("Video restarted");
                //        }, 0);
                //        break;
                //    }
                //    //logMsg(msg);
                //}
                //vlc.log.messages.clear();
            } catch (e) {
                alert(e);
            }
            var videoWidth = vlc.video.width;
            var videoHeight = vlc.video.height;
            var aspectRatio = videoWidth / videoHeight;
            if (!isNaN(aspectRatio)) {
                var newWidth = Math.round(vlc.height * aspectRatio);
                if (newWidth != vlc.width) {
                    self.setVideoWidth(newWidth);
                    $("noVideoOverlay").style.width = newWidth + "px";
                }
                if (isPlaying) {
                    $("noVideoOverlay").style.display = "none";
                    if (mSeekTo != null) {
                        try {
                            vlc.input.position = mSeekTo;
                        } catch (e) {
                            // never mind
                        }
                        mSeekTo = null;
                    }
                }
            } else {
                throw new Error();
            }
            try {
                self.setPlaybackPosition(vlc.input.position);
            } catch (ex) {
                // never mind
            }
        } catch (e) {
            $("noVideoOverlay").style.display = ""; 
        }
        //throw new Error("debugWidth: " + debugWidth + ", debugHasVout: " + debugHasVout +
        //                ", debugFps: " + debugFps + ", debugState: " + debugState);
        //throw new Error("debugPosition: " + debugPosition);
    };
};

