///////////////////////////////////////////////////////////////////////////////
//
//  main.js
//
//
// © 2007 Microsoft Corporation. All Rights Reserved.
//
// This file is licensed as part of the Silverlight 1.0 SDK, for details look
// here: http://go.microsoft.com/fwlink/?LinkID=89144&clcid=0x409
//
///////////////////////////////////////////////////////////////////////////////
// Class encapsulating a XAML entry
function xamlEntry(name, xamlUrl, thumbnailUrl) {
    this.name = name;
    this.xamlUrl = xamlUrl;
    this.thumbnailUrl = thumbnailUrl;
}

function slPad(sender, eventArgs) { }

slPad.prototype.handleLoad = function(control, userContext, rootElement) {
    // Local variables
    this.plugIn = control;           // Store the host plug-in
    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
    
    // Silverlight Downloader with progress
    this.loader = this.plugIn.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");
    
    // Hook the plug-in's resize event
    this.resize(rootElement, null);
    this.setCallback(this.plugIn.content, "onResize", this.resize);
    
    // Show the textarea
    var textArea1 = document.getElementById("textArea1");
    textArea1.style.visibility = "visible";
    
    // Bind XAML buttons to javascript code
    var loadButton = rootElement.findName("loadButton");
    var loadButtonRect = rootElement.findName("loadButtonRect");
    new button(loadButton, loadButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onLoadXamlClicked));
    
    var zoomOutButton = rootElement.findName("zoomOutButton");
    var zoomOutButtonRect = rootElement.findName("zoomOutButtonRect");
    new button(zoomOutButton, zoomOutButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onZoomOutClicked));
    
    var zoomInButton = rootElement.findName("zoomInButton");
    var zoomInButtonRect = rootElement.findName("zoomInButtonRect");
    new button(zoomInButton, zoomInButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onZoomInClicked));
    
    var scrollDownButton = rootElement.findName("scrollDownButton");
    var scrollDownButtonRect = rootElement.findName("scrollDownButtonRect");
    new button(scrollDownButton, scrollDownButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onScrollDownClicked));
    
    var scrollUpButton = rootElement.findName("scrollUpButton");
    var scrollUpButtonRect = rootElement.findName("scrollUpButtonRect");
    new button(scrollUpButton, scrollUpButtonRect, "#FF6D6D6D", "#FF595959", delegate(this, this.onScrollUpClicked));
    
    var xamlTitleRect = rootElement.findName("xamlTitleRect");
    xamlTitleRect.addEventListener("mouseLeftButtonUp", delegate(this, this.splitterMouseUp));
    xamlTitleRect.addEventListener("mouseLeftButtonDown", delegate(this, this.splitterMouseDown));
    xamlTitleRect.addEventListener("mouseMove", delegate(this, this.splitterMouseMove));
    
    var scrollDown = rootElement.findName("scrollDown");   
    scrollDown.addEventListener("completed", delegate(this, this.scrollDownCompleted));
    
    var scrollUp = rootElement.findName("scrollUp");   
    scrollUp.addEventListener("completed", delegate(this, this.scrollUpCompleted));
    
    // Hook up the scene drag event handlers
    var sceneClipCanvas = rootElement.findName("sceneClipCanvas");
    
    sceneClipCanvas.addEventListener("mouseLeftButtonUp", delegate(this, this.sceneMouseUp));
    sceneClipCanvas.addEventListener("mouseLeftButtonDown", delegate(this, this.sceneMouseDown));
    sceneClipCanvas.addEventListener("mouseMove", delegate(this, this.sceneMouseMove));
    
    // Hook up mouse-wheel logic
    this.wheelHelper = new WheelHelper();
	this.wheelHelper.wheelScrolled = delegate(this, delegate(this, this.processMouseWheel));
    
    // Set the downloader's completed event to a local method and star the downloading
    this.loader.addEventListener("completed", delegate(this, this.downloadCopmleted));
    this.loader.addEventListener("downloadProgressChanged", delegate(this, this.downloadProgressChanged));
    
    // Start the first download
    this.loader.open("Get", this.xamlFiles[this.xamlIndex].xamlUrl, true);
  	this.loader.send();
}

slPad.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 templateInstance = this.plugIn.content.createFromXaml(templateInstanceString);
    return templateInstance;
}

slPad.prototype.downloadProgressChanged = function(sender, args) {
    // Set the width of the progress bar
    var loadingRect = sender.findName("loadingRect");
    loadingRect.width = sender.downloadProgress * 96;
}

slPad.prototype.downloadCopmleted = function(sender, args) {
    var itemsCanvas = this.plugIn.content.findName("itemsCanvas");
    var templateInstance;
    
    // Fill out the template and create a new 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 = this.plugIn.content.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 = this.plugIn.content.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();
    }
}

slPad.prototype.handleXamlItemClick = function(xamlItem) {
    var textArea1 = document.getElementById("textArea1");
    textArea1.value = xamlItem.xaml;
    
    // Reset the translation
    var sceneTranslate = this.plugIn.content.findName("sceneTranslate");
    sceneTranslate.X = 0;
    sceneTranslate.Y = 0;
    
    var sceneScale = this.plugIn.content.findName("sceneScale");
    sceneScale.scaleX = 1;
    sceneScale.scaleY = 1;
    
    var zoomText = this.plugIn.content.findName("zoomText");
    zoomText.text = "Zoom: " + Math.round(sceneScale.scaleX * 100) + "%";
    
    this.updateScene();
}

slPad.prototype.updateScene = function() {
    var textArea1 = document.getElementById("textArea1");
    if (textArea1.value == "") {
        return;
    }
    
    var errorTextBlock = this.plugIn.content.findName("errorTextBlock");
    
    try {
        var scene = this.plugIn.content.createFromXaml(textArea1.value);
    }
    catch (ex) {
        errorTextBlock.foreground = "red";
	    errorTextBlock.text = "Parse error";
        return;
    }
    
    var sceneHostCanvas = this.plugIn.content.findName("sceneHostCanvas");
    
    if(sceneHostCanvas.children.count > 0) {
        sceneHostCanvas.children.removeAt(0);
    }
    
    try {
        sceneHostCanvas.children.add(scene);
        sceneHostCanvas.Width = scene.width;
        sceneHostCanvas.Height = scene.height;
    }
    catch (exception) {
        errorTextBlock.foreground = "red";
	    errorTextBlock.text = "error adding the XAML to the main canvas.  This is likely an error in an animation targetting a non-existant object.";
	    return;
    }
    
	errorTextBlock.foreground = "#FFC5C5C5";
	errorTextBlock.text = "Parse successful";
}

slPad.prototype.sceneMouseMove = function(sender, eventArgs) {
    if (this.mouseDownX != -1 && this.mouseDownY != -1) {
        var sceneTranslate = sender.findName("sceneTranslate");
        sceneTranslate.X = eventArgs.getPosition(null).X - this.mouseDownX;
        sceneTranslate.Y = eventArgs.getPosition(null).Y - this.mouseDownY;
    }
}

slPad.prototype.sceneMouseDown = function(sender, eventArgs) {
    var sceneTranslate = sender.findName("sceneTranslate");
    this.mouseDownX = eventArgs.getPosition(null).X - sceneTranslate.X;
    this.mouseDownY = eventArgs.getPosition(null).Y - sceneTranslate.Y;
}

slPad.prototype.sceneMouseUp = function(sender, eventArgs) {
    this.mouseDownX = -1;
    this.mouseDownY = -1;
}

slPad.prototype.onLoadXamlClicked = function(sender, eventArgs) {
   this.updateScene();
}

slPad.prototype.scrollUpCompleted = function(sender, eventArgs) {
    var itemsCanvas = sender.findName("itemsCanvas");
    
    sender.stop();
    itemsCanvas["canvas.top"] += 111;
}

slPad.prototype.scrollDownCompleted = function(sender, eventArgs) {
    var itemsCanvas = sender.findName("itemsCanvas");
    
    sender.stop();
    itemsCanvas["canvas.top"] -= 111;
}

slPad.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();
    }
}

slPad.prototype.onScrollUpClicked = function(sender, eventArgs) {
    var itemsCanvas = sender.findName("itemsCanvas");
    var scrollUp = sender.findName("scrollUp");
    
    if (itemsCanvas["canvas.top"] + 111 <= 0) {
        scrollUp.begin();
    }
}

slPad.prototype.splitterMouseDown = function(sender, eventArgs) {
    var xamlTitleRect = sender.findName("xamlTitleRect");
    xamlTitleRect.captureMouse();
    var xamlCanvas = sender.findName("xamlCanvas");
    this.splitterY = eventArgs.getPosition(null).Y - xamlCanvas["canvas.top"];
}

slPad.prototype.splitterMouseUp = function(sender, eventArgs) {
    this.splitterY = -1;
    var xamlTitleRect = sender.findName("xamlTitleRect");
    xamlTitleRect.releaseMouseCapture();
}

slPad.prototype.splitterMouseMove = function(sender, eventArgs) {
    if (this.splitterY != -1) {
        // Find the new height percentage and resize the scene
        var xamlCanvas = sender.findName("xamlCanvas");
        var targetHeight = this.plugIn.content.actualHeight;
        
        this.scenePanePercentage = (eventArgs.getPosition(null).Y - this.splitterY) / targetHeight;
        this.resize(sender, null);
    }
}

slPad.prototype.onZoomInClicked = function(sender, eventArgs) {
    this.processZoomIn(sender);
}
slPad.prototype.onZoomOutClicked = function(sender, eventArgs) {
    this.processZoomOut(sender);
}

slPad.prototype.processZoomOut = function() {
    var sceneScale = this.plugIn.content.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 = this.plugIn.content.findName("zoomText");
    zoomText.text = "Zoom: " + Math.round(sceneScale.scaleX * 100) + "%";
}

slPad.prototype.processZoomIn = function() {
    var sceneScale = this.plugIn.content.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 = this.plugIn.content.findName("zoomText");
    zoomText.text = "Zoom: " + Math.round(sceneScale.scaleX * 100) + "%";
}

slPad.prototype.processMouseWheel = function(delta) {
    if (delta < 0) {
        this.processZoomOut();
    }
    else {
        this.processZoomIn();
    }
}

// Resizes the XAML scene to fill the browser
slPad.prototype.resize = function(sender, eventArgs) {
   // Find the new size of the control
   var targetWidth = this.plugIn.content.actualWidth;
   var targetHeight = this.plugIn.content.actualHeight;
   
   // Calculate height based on percentage
   var sceneHeight = targetHeight / (1 / this.scenePanePercentage);
   
   // Layout XAML items toolbar
   var borderRect = sender.findName("borderRect");
   borderRect.height = targetHeight - 27;
   
   var itemsClippingCanvas = sender.findName("itemsClippingCanvas");
   itemsClippingCanvas.clip = "M3,3L115,3 115," + (borderRect.height - 41) + " 3," + (borderRect.height - 41) + "z";
   itemsClippingCanvas.height = borderRect.height - 41;
   
   var scrollDownButton = sender.findName("scrollDownButton");
   scrollDownButton["canvas.top"] = targetHeight - 20;
   
   // Layout scene pane
   var sceneTitleRect = sender.findName("sceneTitleRect");
   sceneTitleRect.width = targetWidth - 130;
   
   var sceneHatch = sender.findName("sceneHatch");
   sceneHatch.height = sceneHeight - 27;
   sceneHatch.width = targetWidth - 131;
   
   var sceneClipCanvas = sender.findName("sceneClipCanvas");
   sceneClipCanvas.clip = "M0,0L" + sceneHatch.width + ",0 " + sceneHatch.width + "," + sceneHatch.height + " 0," + sceneHatch.height + "z";
   
   var zoomUI = sender.findName("zoomUI");
   zoomUI["canvas.left"] = targetWidth - 240;
   
   // Layout XAML pane
   var xamlCanvas = sender.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 = sender.findName("xamlTitleRect");
   xamlTitleRect.width = targetWidth - 130;
   
   var loadButton = sender.findName("loadButton");
   loadButton["canvas.left"] = targetWidth - 77;
   loadButton["canvas.top"] = targetHeight - 25;
   
   var errorTextBlock = sender.findName("errorTextBlock");
   errorTextBlock["canvas.top"] = targetHeight - 22;
   
   var loadingTextCanvas = sender.findName("loadingTextCanvas");
   loadingTextCanvas["canvas.top"] = targetHeight - 110;
}

slPad.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] = callbackName;
}

