/*
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCEloOR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


/**
 * This file contains definitions for the common functions used by all the home
 * configurator pages.
 */
o3djs.require('o3djs.util');
o3djs.require('o3djs.arcball');
o3djs.require('o3djs.dump');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.shape');
o3djs.require('o3djs.effect');
o3djs.require('o3djs.material');
o3djs.require('o3djs.pack');
o3djs.require('o3djs.picking');
o3djs.require('o3djs.scene');
o3djs.require('o3djs.math');
o3djs.require('o3djs.camera');
o3djs.require('o3djs.loader');

var g_root;
var g_o3d;
var g_math;
var g_client;
var g_thisRot;
var g_lastRot;
var g_pack = null;
var g_mainPack;
var g_viewInfo;
var g_lightPosParam;
var g_currentTool = null;
var g_xformRoot = null;
var TOOL_ORBIT = 3;
var TOOL_PAN = 4;
var TOOL_ZOOM = 5;
var TOOL_MOVE = 1;
var TOOL_ZOOM_EXTENTS = 6;
var g_o3dElement = null;
var g_loadInfo;
var g_downloadPercent = -1;
var g_path = '';
var g_texinfo;
var g_texLoaded = false;
var g_modelLoaded = false;
var g_switch = false;

var g_shaderSelection = 0;
var g_shaders = [
  {file: 'texture-only',          name: 'Texture Only'},
  {file: 'diffuse',               name: 'Diffuse'},
  {file: 'normal',                name: 'Normal'},
  {file: 'toon',                  name: 'Toon'}];
var g_effects = [];
var g_bumpTextureSampler;
var g_bumpBumpsSampler;
var g_colorRampSampler;

/**
 * Looks up a Param and if it exists sets its value.
 * @param {!o3d.ParamObject} object object to look for param on.
 * @param {string} name name of Param to look up.
 * @param {*} value Value to set param.
 */
function setParam(object, paramName, value) {
  var param = object.getParam(paramName);
  if (param) {
    param.value = value;
  }
}

/**
 * Apply the desired shader to our scene.
 * @param {!o3d.Pack} pack Variable referring to the scene's pack.
 * @param {number} shaderNumber Index into g_effects of which shader to use.
 */
function applyShader(pack, shaderNumber) {
  
  if(g_texLoaded == true && g_modelLoaded == true){
	  
	  //alert("Hi im going to apply shader number " + shaderNumber);
	  var materials = pack.getObjectsByClassName('o3d.Material');
	  // Make the change to each material. For our teapot, there is only one
	  // material.
	  for (var m = 0; m < materials.length; m++) {
		var material = materials[m];
		g_effects[shaderNumber].createUniformParameters(material);
		material.effect = g_effects[shaderNumber];
	
		// Set our shader values
		var colorParamValue = [0.8, 0.8, 0.8, 1];
		var lightPosParamValue = [600, 600, 1000];
	
		setParam(material, 'lightPos', lightPosParamValue);
		setParam(material, 'mylightWorldPos', lightPosParamValue);
		setParam(material, 'cameraEye', [197.58, -63.5702, 0]);
		setParam(material, 'color', colorParamValue);
		setParam(material, 'colorMult', [.75, .75, 75., 1]);
		// only use the texture input addition to bump mapping if on selection 3
		setParam(material, 'useTexture', (g_shaderSelection == 0) ? 1 : 0);
		setParam(material, 'lightIntensity', [0.8, 0.8, 0.8, 1]);
		setParam(material, 'ambientIntensity', [0.2, 0.2, 0.2, 1]);
		setParam(material, 'emissive', [0, 0, 0, 1]);
		setParam(material, 'ambient', [1, 1, 1, 1]);
		setParam(material, 'diffuse', colorParamValue);
		setParam(material, 'specular', [0.5, 0.5, 0.5, 1]);
		setParam(material, 'shininess', 50);
		setParam(material, 'BumpSampler', g_bumpBumpsSampler);
		setParam(material, 'AmbientSampler', g_bumpTextureSampler);
		setParam(material, 'DiffuseSampler', g_bumpTextureSampler);
		setParam(material, 'texSampler0', g_bumpTextureSampler);
		setParam(material, 'colorRamp', g_colorRampSampler);
	
		var timeParam = material.getParam('inputTime');
		if (timeParam) {
		  timeParam.bind(g_currentTimeParam);
		}
	  }
  }
  
}

// The target camera has its z and y flipped because that's the way Scott
// Lininger thinks.

//initial cam eye and target position
function TargetCamera() {
  this.eye = {
      //rotZ: -2,
      //rotH: 1.6,
	  rotZ: -2.1,
      rotH: 1.8,
      //distanceFromTarget: 85 };
	  distanceFromTarget: 72 };
  this.target = { x: -1.2, y: 0, z: -1.1 };
}

TargetCamera.prototype.update = function() {
  var target = [this.target.x, this.target.y, this.target.z];

  this.eye.x = this.target.x + Math.cos(this.eye.rotZ) * this.eye.distanceFromTarget * Math.sin(this.eye.rotH);
  this.eye.y = this.target.y + Math.sin(this.eye.rotZ) * this.eye.distanceFromTarget * Math.sin(this.eye.rotH);
  this.eye.z = this.target.z + Math.cos(this.eye.rotH) * this.eye.distanceFromTarget;

  var eye = [this.eye.x, this.eye.y, this.eye.z];
  var up = [0, 0, 1];
  g_viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);
  g_lightPosParam.value = eye;
};

var g_camera = new TargetCamera();

function peg(value, lower, upper) {
  if (value < lower) {
    return lower;
  } else if (value > upper) {
    return upper;
  } else {
    return value;
  }
}

/**
 * Keyboard constants.
 */
var BACKSPACE = 8;
var F = 102;
var TAB = 9;
var ENTER = 13;
var SHIFT = 16;
var CTRL = 17;
var ALT = 18;
var ESCAPE = 27;
var PAGEUP = 33;
var PAGEDOWN = 34;
var END = 35;
var HOME = 36;
var LEFT = 37;
var UP = 38;
var RIGHT = 39;
var DOWN = 40;
var DELETE = 46;
var SPACE = 32;

/**
 * Create some global key capturing. Keys that are pressed will be stored in
 * this global array.
 */
g_keyIsDown = [];

document.onkeydown = function(e) {
  var keycode;
  if (window.event) {
    keycode = window.event.keyCode;
  } else if (e) {
    keycode = e.which;
  }
  g_keyIsDown[keycode] = true;
  
  //Reset view code for F
  if(keycode==70){
	g_camera.eye = {
      //rotZ: -2,
      //rotH: 1.6,
      rotZ: -2.1,
      rotH: 1.8,
	  distanceFromTarget: 72 };
 	g_camera.target = { x: -1.2, y: 0, z: -1.1 };
	//alert("UpdateView");
	g_camera.update();
  }
  
  //key to change Shade is c
  if(keycode==67){
	g_shaderSelection += 1;
	if (g_shaderSelection == 4){
		g_shaderSelection = 0;
		applyShader(g_pack, g_shaderSelection);
	}else{
		applyShader(g_pack, g_shaderSelection);
	}
  }
  
};

document.onkeyup = function(e) {
  var keycode;
  if (window.event) {
    keycode = window.event.keyCode;
  } else if (e) {
    keycode = e.which;
  }
  g_keyIsDown[keycode] = false;  
};

document.onmouseup = function(e) {
  if (g_currentTool != null) {
    g_currentTool.handleMouseUp(e);
  } else {
    cancelInsertDrag();
  }
};

function mouseDown(e) {
  // If the middle mouse button is used, then switch into the orbit tool,
  // Sketchup-style.
  if (e.button == g_o3d.Event.BUTTON_LEFT) {
    selectTool(null, TOOL_ORBIT);
  }
  
  if (e.button == g_o3d.Event.BUTTON_MIDDLE) {
    selectTool(null, TOOL_PAN);
  }
  
  if (e.button == g_o3d.Event.BUTTON_RIGHT) {
    selectTool(null, TOOL_ZOOM);
  }

  if (g_currentTool != null) {
    g_currentTool.handleMouseDown(e);
  }
}

// This function handles mouse move events inside the o3d area.  It simply
// forwards them down to the currently selected tool. All event handling is actually
// done by the selected tool... so make sure to handle each even in each respective
// tool!!!
function mouseMove(e) {
  if (g_currentTool != null) {
    g_currentTool.handleMouseMove(e);
  }
}

// This function handles mouse up events that take place in the o3d area.
function mouseUp(e) {
  if (g_currentTool != null) {
    g_currentTool.handleMouseUp(e);
  }
}

// An array of tool objects that will get populated when our base model loads.
var g_tools = [];
function selectTool(e, opt_toolNumber) {
  var toolNumber = opt_toolNumber;
  g_currentTool = g_tools[toolNumber];
}

//INITAL CAMERA PAREMETERS   FOV and Clipping
function setClientSize() {
  // Create a perspective projection matrix
  g_viewInfo.drawContext.projection = g_math.matrix4.perspective(
    3.14 * 10 / 180, g_client.width / g_client.height, 2, 500000);
}

function resize() {
  setClientSize();
}

function init() {
  o3djs.util.makeClients(initStep2,"LargeGeometry");
}

function initStep2(clientElements) {
  g_o3dElement = clientElements[0];

  g_path = window.location.href;
  var index = g_path.lastIndexOf('/');
  if (my_page == 0) {
  	g_path = g_path.substring(0, index + 1) + 'assets/model/' +g_model+ '.o3dtgz';
  } else {
  	g_path = g_path.substring(0, index + 1) + 'shapeshot/assets/model/' +g_model+ '.o3dtgz';
  }
  var myurl = document.getElementById('url').value = g_path; // changed to myurl from url

  g_o3d = g_o3dElement.o3d;
  g_math = o3djs.math;
  g_client = g_o3dElement.client;

  g_mainPack = g_client.createPack();
  g_mainPack.name = 'di viewer pack';

  // Create the render graph for a view.
  
  if (my_page == 0) {
  	g_viewInfo = o3djs.rendergraph.createBasicView(
      g_mainPack,
      g_client.root,
      g_client.renderGraphRoot,
      [1, 1, 1, 1]);
  	} else {
  	g_viewInfo = o3djs.rendergraph.createBasicView(
      g_mainPack,
      g_client.root,
      g_client.renderGraphRoot,
      [0.941, 0.941, 0.941, 1]);
  	}
  
  /*g_viewInfo = o3djs.rendergraph.createBasicView(
      g_mainPack,
      g_client.root,
      g_client.renderGraphRoot,
      [1, 1, 1, 1]);*/

  g_lastRot = g_math.identity(3);
  g_thisRot = g_math.identity(3);

  var root = g_client.root;

  var target = [0, 0, 0];
  var eye = [0, 0, 5];
  var up = [0, 0, 1];
  g_viewInfo.drawContext.view = g_math.matrix4.lookAt(eye, target, up);

  setClientSize();

  var paramObject = g_mainPack.createObject('ParamObject');
  // Set the light at the same position as the camera to create a headlight
  // that illuminates the object straight on.
  g_lightPosParam = paramObject.createParam('lightWorldPos', 'ParamFloat3');
  g_lightPosParam.value = eye;

  g_root = loadFile(g_viewInfo.drawContext, myurl); // changed to myurl from url

  o3djs.event.addEventListener(g_o3dElement, 'mousedown', mouseDown);
  o3djs.event.addEventListener(g_o3dElement, 'mousemove', mouseMove);
  o3djs.event.addEventListener(g_o3dElement, 'mouseup', mouseUp);
}

// LOAD in files. Currently our model loads in fine.... likely other stuff due to loading of assets from original
function loadFile(context, g_path) {
  g_pack = g_client.createPack();
  g_pack.name = 'load pack';

  g_new_object_root = null;
  if (g_xformRoot == null) {
    // Assign as the floorplan

	g_xformRoot = g_pack.createObject('o3d.Transform');
    g_xformRoot.name = 'floorplan';
    g_xformRoot.parent = g_client.root;
	
	//g_xformRoot = 
	//g_xformRoot.rotateX (Math.PI/2);
   
	// Put the object we're loading on the floorplan.
    g_new_object_root = g_xformRoot;

    // Create our set of tools that can be activated.
    // Note: Currently only the Delete, Move, Rotate, Orbit, Pan and Zoom
    // tools are implemented.  The last four icons in the toolbar are unused.
    g_tools = [
      null,
      null,
      null,
      new OrbitTool(g_camera),
      new PanTool(g_camera),
      new ZoomTool(g_camera),
      null,
      null,
      null,
      null
    ]
    selectTool(null, TOOL_ORBIT);
  } 
  
  for(var s = 0; s < g_shaders.length; s++) {
    g_effects[s] = g_pack.createObject('Effect');
    if (my_page == 0) {
  	var shaderString = 'shaders/' + g_shaders[s].file + '.shader';
  	} else {
  	var shaderString = 'shapeshot/shaders/' + g_shaders[s].file + '.shader';
  	}
	//var shaderString = 'shaders/' + g_shaders[s].file + '.shader';
    o3djs.effect.loadEffect(g_effects[s], shaderString);
  }
  var rampWidth = 64;
  var texture = g_pack.createTexture2D(
      rampWidth, 1, g_o3d.Texture.XRGB8, 1, false);
  var pixels = [];
  for (var ii = 0; ii < rampWidth; ++ii) {
    var level = ii > rampWidth * 0.5 ? 1 : 0.3;
    pixels.push(level, level, level);
  }
  texture.set(0, pixels);
  g_colorRampSampler = g_pack.createObject('Sampler');
  g_colorRampSampler.texture = texture;
  g_colorRampSampler.addressModeU = g_o3d.Sampler.CLAMP;
  
  ///////
  
  var loader = o3djs.loader.createLoader(initStep3);
  
  if (my_page == 0) {
  	 g_texinfo = loader.loadTexture(g_pack, 'assets/texture/'+g_model+'.jpg',
                     function(texture, exception) {
                       if (exception) {
                         alert(exception);
                       } else {
                         g_bumpTextureSampler = g_pack.createObject('Sampler');
                         g_bumpTextureSampler.texture = texture;
                         g_bumpTextureSampler.mipFilter = g_o3d.Sampler.LINEAR;
                       }
                     });
  } else {
  	 g_texinfo = loader.loadTexture(g_pack, 'shapeshot/assets/texture/'+g_model+'.jpg',
                     function(texture, exception) {
                       if (exception) {
                         alert(exception);
                       } else {
                         g_bumpTextureSampler = g_pack.createObject('Sampler');
                         g_bumpTextureSampler.texture = texture;
                         g_bumpTextureSampler.mipFilter = g_o3d.Sampler.LINEAR;
                       }
                     });
  }
 
  loader.finish();
  
  if (g_path != null) {	
	g_loadInfo = o3djs.scene.loadScene(g_client, g_pack, g_new_object_root, g_path, callback);
	//alert("Going to pause");
	//pause(20000);
	g_client.setRenderCallback(onRender);
  }
  
  function callback(pack, start_move_tool_root, exception) {
    //alert("done loading");
	
	g_loadInfo = null;
	g_modelLoaded = true;
	if (exception) {
      alert('Could not load: ' + g_path + '\n' + exception);
    } else {
      // Generate draw elements and setup material draw lists.
      o3djs.pack.preparePack(g_pack, g_viewInfo);
	
	  // Start with diffuse as default
      applyShader(g_pack, 0);
	  // Manually connect all the materials' lightWorldPos params to the context
      var materials = g_pack.getObjectsByClassName('o3d.Material');
      for (var m = 0; m < materials.length; ++m) {
        var material = materials[m];
        var param = material.getParam('lightWorldPos');
        if (param) {
          param.bind(g_lightPosParam);
        }
      }
      
    }
	g_camera.update();
  }
  return g_new_object_root;
}

function pause(milliseconds) {
	var dt = new Date();
	while ((new Date()) - dt <= milliseconds) { /* Do nothing */ }
}

function initStep3() {
  g_texLoaded = true;
}

function onRender(){
	if (g_loadInfo) {
		var progressInfo = g_loadInfo.getKnownProgressInfoSoFar();
		if (progressInfo.percent != g_downloadPercent) {
		  g_downloadPercent = progressInfo.percent;
		  setStatus('Loading... ' + progressInfo.percent + '%' +
					' (' + progressInfo.downloaded +
					' of ' + progressInfo.totalBytes + progressInfo.suffix + ')');
		} else {
			document.getElementById('status').style.display = 'none';
		}
    } /*else {
		if(g_switch == false){
			setStatus('Applying Textures');
		}
	}*/
	
	if(g_texLoaded == true && g_modelLoaded == true && g_switch == false){
	  g_switch = true;
	  // Start with diffuse as default
      applyShader(g_pack, 0);
	  setStatus('');
	}
}

function setStatus(msg) {
  var element = document.getElementById('status');
  if (element) {
	element.innerHTML = msg;
  }
}