// Handles the root element's loaded event
// The current CTP fo WPF/E requires this to be global 
function rootLoaded(sender, eventArgs) {
    new wpfePad(sender);
}

// Handles the second hidden WPF/E plug-in's error event
// This is used as a wokaround to catch errors since createFromXaml doesn't raise the error event
// The current CTP fo WPF/E requires this to be global 
function handleParseError(line, col, hr, string)
{
	var str = string + " at: " + line + ", " + col;
	
	var wpfeControl = document.getElementById("wpfeControl1");
	var errorTextBlock = wpfeControl.findName("errorTextBlock");
	errorTextBlock.foreground = "red";
	errorTextBlock.text = str;
}

// Class encapsulating a XAML entry
function xamlEntry(name, xamlUrl, thumbnailUrl) {
    this.name = name;
    this.xamlUrl = xamlUrl;
    this.thumbnailUrl = thumbnailUrl;
}

function wpfePad(sender) {
    // Local variables
    this.mouseDownX = -1;           // Used for dragging the scene canvas
    this.mouseDownY = -1;           // Used for dragging the scene canvas
    this.splitterY = -1;            // Used for dragging the splitter
    this.scenePanePercentage = .5;  // Used for calculating size of the scene pane during resizes
    this.items = new Array();       // The xamlItems loaded into the toolbox
    this.xamlIndex = 0;             // Index used for loading XAML files
    
    // WPF/E Downloader with progress downloader
    this.loader = sender.getHost().createObject("Downloader");     
    
    // The xaml template that will be used by createFromXaml
    this.itemTemplate = 
        "<Canvas xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'" + 
	    "      xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'" + 
        "      x:Name='item$0' " + 
        "      Width='106' " + 
        "      Height='106' Canvas.Left='7.5' Canvas.Top='7.5'>" + 
        "   <Rectangle x:Name='itemBackground$0' Fill='#FF424242' StrokeThickness='0' RadiusX='5' RadiusY='5' Width='106' Height='106'/>" + 
        "   <TextBlock x:Name='itemText$0'  Width='110' Height='15' Foreground='#FFC2C2C2' Text='$1' TextWrapping='Wrap' FontFamily='Tahoma' FontSize='11' Canvas.Left='5' Canvas.Top='4'/>" + 
        "   <TextBlock x:Name='itemXaml$0'  Text='$2' Foreground='#00000000'/>" + 
        "   <Image x:Name='itemImage$0' Width='95' Height='75' Canvas.Left='5' Canvas.Top='25' Source='$3' Stretch='Uniform'/>" + 
        "</Canvas>";
    
    // XAML files loaded into the toolbox
    // Each of these will be turned a xamlItem instance
    this.xamlFiles = new Array();
        this.xamlFiles[0] = new xamlEntry("BugsLogo", "XAML/scenes/BugsLogo.xaml", "Images/BugsLogo.png");
        this.xamlFiles[1] = new xamlEntry("Blocks", "XAML/scenes/Blocks.xaml", "Images/Blocks.png");
        this.xamlFiles[2] = new xamlEntry("Coffee", "XAML/scenes/Coffee.xaml", "Images/Coffee.png");
        this.xamlFiles[3] = new xamlEntry("Media", "XAML/scenes/Media.xaml", "Images/Media.png");
        this.xamlFiles[4] = new xamlEntry("Camera", "XAML/scenes/Camera.xaml", "Images/Camera.png");
        this.xamlFiles[5] = new xamlEntry("Cowboy", "XAML/scenes/Cowboy.xaml", "Images/Cowboy.png");
        this.xamlFiles[6] = new xamlEntry("RedScanlineGloss", "XAML/scenes/RedScanlineGloss.xaml", "Images/RedScanlineGloss.png");
        this.xamlFiles[7] = new xamlEntry("WelcomeScreen", "XAML/scenes/WelcomeScreen.xaml", "Images/WelcomeScreen.png");
        this.xamlFiles[8] = new xamlEntry("Glasses", "XAML/scenes/Glasses.xaml", "Images/Glasses.png");
        this.xamlFiles[9] = new xamlEntry("Landscape", "XAML/scenes/Landscape.xaml", "Images/Landscape.png");
        this.xamlFiles[10] = new xamlEntry("Techy", "XAML/scenes/Techy.xaml", "Images/Techy.png");
        this.xamlFiles[11] = new xamlEntry("Popcan", "XAML/scenes/Popcan.xaml", "Images/Popcan.png");
        this.xamlFiles[12] = new xamlEntry("Tile", "XAML/scenes/Tile.xaml", "Images/Tile.png");
        this.xamlFiles[13] = new xamlEntry("Suit", "XAML/scenes/Suit.xaml", "Images/Suit.png");

    window.onresize = delegate(this, this.resize);
    this.resize();
    
    var textArea1 = document.getElementById("textArea1");
    textArea1.style.visibility = "visible";
    
    // Bind XAML buttons to javascript code
    var loadButton = sender.findName("loadButton");
    var loadButtonRect = sender.findName("loadButtonRect");
    new button(loadButton, loadButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onLoadXamlClicked));
    
    var zoomOutButton = sender.findName("zoomOutButton");
    var zoomOutButtonRect = sender.findName("zoomOutButtonRect");
    new button(zoomOutButton, zoomOutButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onZoomOutClicked));
    
    var zoomInButton = sender.findName("zoomInButton");
    var zoomInButtonRect = sender.findName("zoomInButtonRect");
    new button(zoomInButton, zoomInButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onZoomInClicked));
    
    var scrollDownButton = sender.findName("scrollDownButton");
    var scrollDownButtonRect = sender.findName("scrollDownButtonRect");
    new button(scrollDownButton, scrollDownButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onScrollDownClicked));
    
    var scrollUpButton = sender.findName("scrollUpButton");
    scrollUpButtonRect = sender.findName("scrollUpButtonRect");
    new button(scrollUpButton, scrollUpButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onScrollUpClicked));
    
    var xamlTitleRect = sender.findName("xamlTitleRect");
    this.setCallback(xamlTitleRect, "mouseLeftButtonUp", this.splitterMouseUp);
    this.setCallback(xamlTitleRect, "mouseLeftButtonDown", this.splitterMouseDown);
    this.setCallback(xamlTitleRect, "mouseMove", this.splitterMouseMove);
    
    var scrollDown = sender.findName("scrollDown");   
    this.setCallback(scrollDown, "completed", this.scrollDownCompleted);
    
    var scrollUp = sender.findName("scrollUp");   
    this.setCallback(scrollUp, "completed", this.scrollUpCompleted);
    
    // Hook up the scene drag event handlers
    var sceneClipCanvas = sender.findName("sceneClipCanvas");
    
    this.setCallback(sceneClipCanvas, "mouseLeftButtonUp", this.sceneMouseUp);
    this.setCallback(sceneClipCanvas, "mouseLeftButtonDown", this.sceneMouseDown);
    this.setCallback(sceneClipCanvas, "mouseMove", this.sceneMouseMove);
    
    // Hook up mouse-wheel logic
    this.wheelHelper = new WheelHelper();
	this.wheelHelper.wheelScrolled = delegate(this, this.processMouseWheel);
    
    // Set the downloader's completed event to a local method and star the downloading
    this.setCallback(this.loader, "completed", this.downloadCopmleted);
    this.setCallback(this.loader, "downloadProgressChanged", this.downloadProgressChanged);
    
    // Start the first download
    this.loader.open("Get", this.xamlFiles[this.xamlIndex].xamlUrl, true);
  	this.loader.send();
}

wpfePad.prototype.instanceTemplate = function(index, templateData) {
    // Databind the template by replacing the placeholders with real data
    var templateInstanceString = this.itemTemplate.replaceAll("$0", index + 1);
 
    // Iterate over the proeprties in the data item and fill out the template   
    var i = 0;
    for (var propName in templateData) {
        templateInstanceString = templateInstanceString.replaceAll("$" + (i + 1), templateData[propName]);
        i++;
    }

    // Use createFromXaml to create an element from the string
    var wpfeControl = document.getElementById("wpfeControl1");
    var templateInstance = wpfeControl.createFromXaml(templateInstanceString);
    return templateInstance;
}

wpfePad.prototype.downloadProgressChanged = function(sender, args) {
    // Set the width of the progress bar
    var loadingRect = sender.findName("loadingRect");
    loadingRect.width = sender.downloadProgress * 96;
}

wpfePad.prototype.downloadCopmleted = function(sender, args) {
    var wpfeControl = document.getElementById("wpfeControl1");
    var itemsCanvas = wpfeControl.findName("itemsCanvas");
    var templateInstance;
    
    // Fill out the template and create a WPF/E element
    templateInstance = this.instanceTemplate(this.xamlIndex, this.xamlFiles[this.xamlIndex]);
    
    // Position the item in items canvas
    templateInstance["Canvas.Top"] = 2 + (this.xamlIndex * (templateInstance.Height + 5));
    itemsCanvas.children.insert(0, templateInstance);
            
    // Attach items to eventhandlers
    this.items[this.xamlIndex] = new xamlItem(templateInstance, this.xamlIndex + 1);
    
    // Attach a click handler to the item
    this.items[this.xamlIndex].clickHandler = delegate(this, this.handleXamlItemClick);

    // Store the XAML
    this.items[this.xamlIndex].xaml = sender.responseText;
	
	if (this.xamlIndex == 0) {
	    this.handleXamlItemClick(this.items[0]);
	}
	
	this.xamlIndex++;
	if (this.xamlIndex >= this.xamlFiles.length) {
	    // Hide the downlaod status text
	    var loadingTextCanvas = wpfeControl.findName("loadingTextCanvas");
	    loadingTextCanvas.opacity = 0;
	
	    // Set the height of the canvas containing the items
        itemsCanvas.height = this.items.length * (this.items[0].target.height + 5);
    }
    else {
        var loadingText = wpfeControl.findName("loadingText");
        loadingText.text = "Loading: " + this.xamlFiles[this.xamlIndex].name + ".xaml";
        
        // Kick off the next download
        this.loader.open("Get", this.xamlFiles[this.xamlIndex].xamlUrl, true);
  	    this.loader.send();
    }
}

wpfePad.prototype.handleXamlItemClick = function(xamlItem) {
    var textArea1 = document.getElementById("textArea1");
    textArea1.value = xamlItem.xaml;
    
    // Reset the translation
    var wpfeControl = document.getElementById("wpfeControl1");
    var sceneTranslate = wpfeControl.findName("sceneTranslate");
    sceneTranslate.X = 0;
    sceneTranslate.Y = 0;
    
    var sceneScale = wpfeControl.findName("sceneScale");
    sceneScale.scaleX = 1;
    sceneScale.scaleY = 1;
    
    var zoomText = wpfeControl.findName("zoomText");
    zoomText.text = "Zoom: " + Math.round(sceneScale.scaleX * 100) + "%";
    
    this.updateScene();
}

wpfePad.prototype.updateScene = function() {
    var textArea1 = document.getElementById("textArea1");
    if (textArea1.value == "") {
        return;
    }
    
    var wpfeControl = document.getElementById("wpfeControl1");
    
    try {
        var scene = wpfeControl.createFromXaml(textArea1.value);
    }
    catch (exception) {
        this.checkError();
        return;
    }
    
    var sceneHostCanvas = wpfeControl.findName("sceneHostCanvas");
    
    if(sceneHostCanvas.children.count > 0) {
        sceneHostCanvas.children.removeAt(0);
    }
    
    try {
        sceneHostCanvas.children.add(scene);
    }
    catch (exception) {
        alert("error adding the XAML to the main canvas.  This is likely an error in an animation targetting a non-existant object.");
        window.close();
    }
    
    var errorTextBlock = wpfeControl.findName("errorTextBlock");
	errorTextBlock.foreground = "#FFC5C5C5";
	errorTextBlock.text = "Parse successful";
}

wpfePad.prototype.checkError = function() {
    if((navigator.appVersion.indexOf('MSIE') != -1)) {   
        var textArea1 = document.getElementById("textArea1");
        var wpfeControl = document.getElementById("wpfeParseTest");
        
        wpfeControl.sourceString = textArea1.value;
        wpfeControl.reload();
    }
    else {
        handleParseError(0, 0, "", "Parse error")
    }
}

wpfePad.prototype.sceneMouseMove = function(sender, eventArgs) {
    if (this.mouseDownX != -1 && this.mouseDownY != -1) {
        var sceneTranslate = sender.findName("sceneTranslate");
        sceneTranslate.X = eventArgs.X - this.mouseDownX;
        sceneTranslate.Y = eventArgs.Y - this.mouseDownY;
    }
}

wpfePad.prototype.sceneMouseDown = function(sender, eventArgs) {
    var sceneTranslate = sender.findName("sceneTranslate");
    this.mouseDownX = eventArgs.X - sceneTranslate.X;
    this.mouseDownY = eventArgs.Y - sceneTranslate.Y;
}

wpfePad.prototype.sceneMouseUp = function(sender, eventArgs) {
    this.mouseDownX = -1;
    this.mouseDownY = -1;
}

wpfePad.prototype.onLoadXamlClicked = function(sender, eventArgs) {
   this.updateScene();
}

wpfePad.prototype.scrollUpCompleted = function(sender, eventArgs) {
    var itemsCanvas = sender.findName("itemsCanvas");
    
    sender.stop();
    itemsCanvas["canvas.top"] += 111;
}

wpfePad.prototype.scrollDownCompleted = function(sender, eventArgs) {
    var itemsCanvas = sender.findName("itemsCanvas");
    
    sender.stop();
    itemsCanvas["canvas.top"] -= 111;
}

wpfePad.prototype.onScrollDownClicked = function(sender, eventArgs) {
    var itemsCanvas = sender.findName("itemsCanvas");
    var itemsClippingCanvas = sender.findName("itemsClippingCanvas"); 
    var scrollDown = sender.findName("scrollDown");   
    
    if (itemsClippingCanvas.height - itemsCanvas["canvas.top"] < itemsCanvas.height) {
        scrollDown.begin();
    }
}

wpfePad.prototype.onScrollUpClicked = function(sender, eventArgs) {
    var itemsCanvas = sender.findName("itemsCanvas");
    var scrollUp = sender.findName("scrollUp");
    
    if (itemsCanvas["canvas.top"] + 111 <= 0) {
        scrollUp.begin();
    }
}

wpfePad.prototype.splitterMouseDown = function(sender, eventArgs) {
    var xamlTitleRect = sender.findName("xamlTitleRect");
    xamlTitleRect.captureMouse();
    var xamlCanvas = sender.findName("xamlCanvas");
    this.splitterY = eventArgs.Y - xamlCanvas["canvas.top"];
}

wpfePad.prototype.splitterMouseUp = function(sender, eventArgs) {
    this.splitterY = -1;
    var xamlTitleRect = sender.findName("xamlTitleRect");
    xamlTitleRect.releaseMouseCapture();
}

wpfePad.prototype.splitterMouseMove = function(sender, eventArgs) {
    if (this.splitterY != -1) {
        // Find the new height percentage and resize the scene
        var wpfeControl1 = document.getElementById("wpfeControl1");
        var xamlCanvas = sender.findName("xamlCanvas");
        var targetHeight = wpfeControl1.clientHeight;
        
        this.scenePanePercentage = (eventArgs.Y - this.splitterY) / targetHeight;
        this.resize();
    }
}

wpfePad.prototype.onZoomInClicked = function(sender, eventArgs) {
    this.processZoomIn(sender);
}
wpfePad.prototype.onZoomOutClicked = function(sender, eventArgs) {
    this.processZoomOut(sender);
}

wpfePad.prototype.processZoomOut = function(sender) {
    var sceneScale = sender.findName("sceneScale");
    
    // Handle any rounding errors
    var scale = Math.round(sceneScale.scaleX * 100);
    scale = scale / 100;
    
    if (scale <= .01) {
        return;
    }
    
    if (scale <= .1) {
        sceneScale.scaleX -= .01;
        sceneScale.scaleY -= .01;
    }  
    else if (scale <= 1) {
        sceneScale.scaleX -= .1;
        sceneScale.scaleY -= .1;
    }
    else {
        sceneScale.scaleX -= .25;
        sceneScale.scaleY -= .25;
    }
    
    var zoomText = sender.findName("zoomText");
    zoomText.text = "Zoom: " + Math.round(sceneScale.scaleX * 100) + "%";
}

wpfePad.prototype.processZoomIn = function(sender) {
    var sceneScale = sender.findName("sceneScale");
  
    // Handle any rounding errors
    var scale = Math.round(sceneScale.scaleX * 100);
    scale = scale / 100;
  
    if (scale >= 1) {
        sceneScale.scaleX += .25;
        sceneScale.scaleY += .25;
    }
    else if (scale >= .1) {
        sceneScale.scaleX += .1;
        sceneScale.scaleY += .1;
    }
    else {
        sceneScale.scaleX += .01;
        sceneScale.scaleY += .01;
    }  
    
    var zoomText = sender.findName("zoomText");
    zoomText.text = "Zoom: " + Math.round(sceneScale.scaleX * 100) + "%";
}

wpfePad.prototype.processMouseWheel = function(delta) {
    var wpfeControl = document.getElementById("wpfeControl1");
    
    if (delta < 0) {
        this.processZoomIn(wpfeControl);
    }
    else {
        this.processZoomOut(wpfeControl);
    }
}

// Resizes the XAML scene to fill the browser
wpfePad.prototype.resize = function() {
   var wpfeControl1 = document.getElementById("wpfeControl1");
   
   // Find the new size of the control
   var targetWidth = wpfeControl1.clientWidth;
   var targetHeight = wpfeControl1.clientHeight;
   
   // Calculate height based on percentage
   var sceneHeight = targetHeight / (1 / this.scenePanePercentage);
   
   // Layout XAML items toolbar
   var borderRect = wpfeControl1.findName("borderRect");
   borderRect.height = targetHeight - 27;
   
   var itemsClippingCanvas = wpfeControl1.findName("itemsClippingCanvas");
   itemsClippingCanvas.clip = "M3,3L115,3 115," + (borderRect.height - 41) + " 3," + (borderRect.height - 41) + "z";
   itemsClippingCanvas.height = borderRect.height - 41;
   
   var scrollDownButton = wpfeControl1.findName("scrollDownButton");
   scrollDownButton["canvas.top"] = targetHeight - 20;
   
   // Layout scene pane
   var sceneTitleRect = wpfeControl1.findName("sceneTitleRect");
   sceneTitleRect.width = targetWidth - 130;
   
   var sceneHatch = wpfeControl1.findName("sceneHatch");
   sceneHatch.height = sceneHeight - 27;
   sceneHatch.width = targetWidth - 131;
   
   var sceneClipCanvas = wpfeControl1.findName("sceneClipCanvas");
   sceneClipCanvas.clip = "M0,0L" + sceneHatch.width + ",0 " + sceneHatch.width + "," + sceneHatch.height + " 0," + sceneHatch.height + "z";
   
   var zoomUI = wpfeControl1.findName("zoomUI");
   zoomUI["canvas.left"] = targetWidth - 240;
   
   // Layout XAML pane
   var xamlCanvas = wpfeControl1.findName("xamlCanvas");
   xamlCanvas["canvas.top"] = sceneHatch.height + 27;
   
   var textArea1 = document.getElementById("textArea1");
   textArea1.style.top = sceneHeight + 25;
   textArea1.style.width = targetWidth - 131;
   if ((targetHeight - sceneHeight) - 52 < 1) {
        textArea1.style.visibility = "hidden";
   }
   else {
        textArea1.style.visibility = "visible";
        textArea1.style.height = (targetHeight - sceneHeight) - 52;
   }
   
   var xamlTitleRect = wpfeControl1.findName("xamlTitleRect");
   xamlTitleRect.width = targetWidth - 130;
   
   var loadButton = wpfeControl1.findName("loadButton");
   loadButton["canvas.left"] = targetWidth - 77;
   loadButton["canvas.top"] = targetHeight - 25;
   
   var errorTextBlock = wpfeControl1.findName("errorTextBlock");
   errorTextBlock["canvas.top"] = targetHeight - 22;
   
   var loadingTextCanvas = wpfeControl1.findName("loadingTextCanvas");
   loadingTextCanvas["canvas.top"] = targetHeight - 110;
}

wpfePad.prototype.setCallback = function(target, eventName, callback) {
	if (!window.methodIndex)
		window.methodIndex = 0;
	
	var callbackName = "uniqueCallback" + (window.methodIndex++);
	var controller = this;
	var func = function() {
		callback.apply(controller, arguments);
	}
	
	eval(callbackName + " = func;");
	target[eventName] = "javascript:" + callbackName;
}

