/* <copyright>
This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/>
(c) Copyright 2011 Motorola Mobility, Inc.  All Rights Reserved.
</copyright> */

var GeomObj =           require("js/lib/geom/geom-obj").GeomObj;
var ShapePrimitive =    require("js/lib/geom/shape-primitive").ShapePrimitive;
var MaterialsModel = require("js/models/materials-model").MaterialsModel;
///////////////////////////////////////////////////////////////////////
// Class GLLine
//      GL representation of a line.
//      Derived from class GeomObj
///////////////////////////////////////////////////////////////////////
var Line = function GLLine( world, xOffset, yOffset, width, height, slope, strokeSize, strokeColor, strokeMaterial, strokeStyle, xAdj, yAdj) {
	///////////////////////////////////////////////////////////////////////
	// Instance variables
	///////////////////////////////////////////////////////////////////////
	this._width = 2.0;
	this._height = 2.0;
	this._xOffset = 0;
	this._yOffset = 0;

	// If line doesn't fit in canvas world, we had to grow the canvas by this much on either side
	this._xAdj = 0;
	this._yAdj = 0;
	
	this._slope = 0;

	this._strokeWidth = 0.25;

	this._strokeStyle = "Solid";
	this._scaleX = 1.0;
	this._scaleY = 1.0;

	if (arguments.length > 0) {
		this._width = width;
		this._height = height;
		this._xOffset = xOffset;
		this._yOffset = yOffset;

		this._xAdj = xAdj;
		this._yAdj = yAdj;

		this._slope = slope;
		this._strokeWidth = strokeSize;
		if (strokeColor)  this._strokeColor = strokeColor.slice();

		this._strokeStyle = strokeStyle;
		this._scaleX = (world.getViewportWidth())/(world.getViewportHeight());
	}

	this._strokeVerticesLen = 0;

	this.m_world = world;

	this._materialAmbient  = [0.2, 0.2, 0.2,  1.0];
	this._materialDiffuse  = [0.4, 0.4, 0.4,  1.0];
	this._materialSpecular = [0.4, 0.4, 0.4,  1.0];

	if(strokeMaterial) {
		this._strokeMaterial = strokeMaterial;
	}

	///////////////////////////////////////////////////////////////////////
	// Property Accessors
	///////////////////////////////////////////////////////////////////////
	this.getStrokeWidth		= function()		{  return this._strokeWidth;	}
	this.setStrokeWidth		= function(w)		{  this._strokeWidth = w;		}

	this.getStrokeMaterial	= function()		{  return this._strokeMaterial;	}
	this.setStrokeMaterial	= function(m)		{  this._strokeMaterial = m;	}

	this.getStrokeColor		= function()		{  return this._strokeColor;	}
	//this.setStrokeColor	= function(c)		{  this._strokeColor = c;		}

	this.getStrokeStyle		= function()		{  return this._strokeStyle;	}
	this.setStrokeStyle		= function(s)		{  this._strokeStyle = s;		}

	this.getFillMaterial	= function()		{  return null;		}
	
	this.setStrokeMaterial  = function(m)		 {  this._strokeMaterial = m;		 }
	this.getStrokeMaterial	= function()		{  return this._strokeMaterial;		}

	this.getWidth			= function()		{  return this._width;				}
	this.setWidth			= function(w)		{  this._width = w;					}

	this.getHeight			= function()		{  return this._height;				}
	this.setHeight			= function(h)		{  this._height = h;				}

	this.getXAdj			= function()		{  return this._xAdj;			}
	this.setXAdj            = function(x)		{  this._xAdj = x;				}

	this.getYAdj			= function()		{  return this._yAdj;			}
	this.setYAdj            = function(y)		{  this._yAdj = y;				}

	this.getSlope			= function()		{  return this._slope;			}
	this.setSlope            = function(m)		{  this._slope = m;				}

	this.geomType	= function()				{  return this.GEOM_TYPE_LINE;	}

	///////////////////////////////////////////////////////////////////////
	// Methods
	///////////////////////////////////////////////////////////////////////
	this.exportJSON = function()
	{
		var jObj = 
		{
			'type'			: this.geomType(),
			'xoff'			: this._xOffset,
			'yoff'			: this._yOffset,
			'width'			: this._width,
			'height'		: this._height,
			'xAdj'		    : this._xAdj,
			'yAdj'		    : this._yAdj,
			'slope'	        : this._slope,
			'strokeWidth'	: this._strokeWidth,
			'strokeColor'	: this._strokeColor,
			'strokeStyle'	: this._strokeStyle,
			'strokeMat'		: this._strokeMaterial ? this._strokeMaterial.getName() : MaterialsModel.getDefaultMaterialName(),
			'materials'		: this.exportMaterialsJSON()
		};

		return jObj;
	};

	this.importJSON = function( jObj )
	{
		this._xOffset			= jObj.xoff;
		this._yOffset			= jObj.yoff;
		this._width				= jObj.width;
		this._height			= jObj.height;
		this._xAdj			    = jObj.xAdj;
		this._yAdj			    = jObj.yAdj;
		this._strokeWidth		= jObj.strokeWidth;
		this._slope 		    = jObj.slope;
		this._strokeStyle		= jObj.strokeStyle;
		this._strokeColor		= jObj.strokeColor;
		var strokeMaterialName	= jObj.strokeMat;
		this.importMaterialsJSON( jObj.materials );
	};

	this.export = function() {
		var rtnStr = "type: " + this.geomType() + "\n";

		rtnStr += "xoff: "			+ this._xOffset		+ "\n";
		rtnStr += "yoff: "			+ this._yOffset		+ "\n";
		rtnStr += "width: "			+ this._width		+ "\n";
		rtnStr += "height: "		+ this._height		+ "\n";
		rtnStr += "xAdj: "		    + this._xAdj		+ "\n";
		rtnStr += "yAdj: "		    + this._yAdj		+ "\n";
		rtnStr += "strokeWidth: "	+ this._strokeWidth	+ "\n";

        if(this._strokeColor.gradientMode) {
            rtnStr += "strokeGradientMode: "	+ this._strokeColor.gradientMode	+ "\n";
            rtnStr += "strokeColor: " + this.gradientToString(this._strokeColor.color) + "\n";
        } else {
            rtnStr += "strokeColor: "	+ String(this._strokeColor)  + "\n";
        }

		rtnStr += "strokeStyle: "	+ this._strokeStyle	+ "\n";
		rtnStr += "slope: "	        + String(this._slope)	+ "\n";

		rtnStr += "strokeMat: ";
		if (this._strokeMaterial) {
			rtnStr += this._strokeMaterial.getName();
        } else {
			rtnStr +=  MaterialsModel.getDefaultMaterialName();
        }

		rtnStr += "\n";
		return rtnStr;
	};

	this.import = function( importStr ) {
		this._xOffset			= Number( this.getPropertyFromString( "xoff: ",			importStr )  );
		this._yOffset			= Number( this.getPropertyFromString( "yoff: ",			importStr )  );
		this._width				= Number( this.getPropertyFromString( "width: ",		importStr )  );
		this._height			= Number( this.getPropertyFromString( "height: ",		importStr )  );
		this._xAdj			    = Number( this.getPropertyFromString( "xAdj: ",			importStr )  );
		this._yAdj			    = Number( this.getPropertyFromString( "yAdj: ",			importStr )  );
		this._strokeWidth		= Number( this.getPropertyFromString( "strokeWidth: ",	importStr )  );
		var slope 		        = this.getPropertyFromString( "slope: ",	importStr );

		if(isNaN(Number(slope))) {
			this._slope		    = slope;
        } else {
			this._slope         = Number(slope);
        }

		var strokeMaterialName	= this.getPropertyFromString( "strokeMat: ",	importStr );
		this._strokeStyle		= this.getPropertyFromString( "strokeStyle: ",	importStr );

        if(importStr.indexOf("strokeGradientMode: ") < 0)
        {
            this._strokeColor		=  eval( "[" + this.getPropertyFromString( "strokeColor: ",	importStr ) + "]" );
        } else {
            this._strokeColor = {};
            this._strokeColor.gradientMode = this.getPropertyFromString( "strokeGradientMode: ",	importStr );
            this._strokeColor.color = this.stringToGradient(this.getPropertyFromString( "strokeColor: ",	importStr ));
        }

		var strokeMat = MaterialsModel.getMaterial( strokeMaterialName );
		if (!strokeMat) {
			console.log( "object material not found in library: " + strokeMaterialName );
			strokeMat = MaterialsModel.getMaterial( MaterialsModel.getDefaultMaterialName() );
		}

		this._strokeMaterial = strokeMat;

	};

	///////////////////////////////////////////////////////////////////////
	// Methods
	///////////////////////////////////////////////////////////////////////
	this.buildBuffers = function()  {
		// get the world
		var world = this.getWorld();
		if (!world)  throw( "null world in buildBuffers" );
		if (!world._useWebGL)  return;
		
		// make sure RDGE has the correct context
		g_Engine.setContext( world.getCanvas().rdgeid );

		 // create the gl buffer
		var gl = world.getGLContext();

		this._strokeVerticesLen = 0;

		var strokeVertices = [];
		var strokeTextures = [];
		var strokeNormals = [];
		var strokeColors = [];

//        var scaleMat = Matrix.I(3);
//        scaleMat.elements[0][0] = this._scaleX;
//        scaleMat.elements[1][1] = this._scaleY;


        // get the normalized device coordinates (NDC) for
		// all position and dimensions.
		var	vpw = world.getViewportWidth(),  vph = world.getViewportHeight();
		var	xNDC = 2*this._xOffset/vpw,  yNDC = 2*this._yOffset/vph,
			xFillNDC = this._width/vpw,  yFillNDC = this._height/vph,
			xAdjNDC = this._xAdj/vpw,  yAdjNDC = this._yAdj/vph,
			xStrokeNDC = this._strokeWidth/vpw,  yStrokeNDC = this._strokeWidth/vph;

		var aspect = world.getAspect();
		var zn = world.getZNear(),  zf = world.getZFar();
		var	t = zn * Math.tan(world.getFOV() * Math.PI / 360.0),
			b = -t,
			r = aspect*t,
			l = -r;

		// calculate the object coordinates from their NDC coordinates
		var z = -world.getViewDistance();

		// get the position of the origin
		var x = -z*(r-l)/(2.0*zn)*xNDC,
			y = -z*(t-b)/(2.0*zn)*yNDC;

		// get the x and y fill
		var xFill = -z*(r-l)/(2.0*zn)*xFillNDC,
			yFill = -z*(t-b)/(2.0*zn)*yFillNDC;

		// get the x & y stroke size
		var xStroke = -z*(r-l)/(2.0*zn)*xStrokeNDC,
			yStroke = -z*(t-b)/(2.0*zn)*yStrokeNDC;

		// get the x & y adjustments size
		var xAdj = -z*(r-l)/(2.0*zn)*xAdjNDC*2,
			yAdj = -z*(t-b)/(2.0*zn)*yAdjNDC*2;


		this._primArray = [];
		this._materialArray = [];
		this._materialTypeArray = [];
		this._materialNodeArray = [];
		
		this._scaleX = (world._viewportWidth)/(world._viewportHeight);
		
		var innerX = xFill-xStroke;
		var innerY = yFill-yStroke;

		if(this._slope === "vertical") {
			strokeVertices = [
				-xFill+x, yFill+y,  0.0,
				xFill+x, yFill+y,  0.0,
				-xFill+x, -yFill+y,  0.0,

				xFill+x, -yFill+y,  0.0,
				-xFill+x, -yFill+y,  0.0,
				xFill+x, yFill+y,  0.0
			];

			strokeTextures = [
				0, 1,
				1, 1,
				0, 0,

				1, 0,
				0, 0,
				1, 1
			];
		} else if(this._slope === "horizontal") {
			// right now, this is the same as vertical line because,
			// our canvas is the same size as us.
			// But, we will need to use this when drawing in an existing GLWorld with other shapes
			strokeVertices = [
				-xFill+x, yFill+y,  0.0,
				xFill+x, yFill+y,  0.0,
				-xFill+x, -yFill+y,  0.0,

					xFill+x, -yFill+y,  0.0,
				-xFill+x, -yFill+y,  0.0,
				xFill+x, yFill+y,  0.0
			];

			strokeTextures = [
				0, 1,
				1, 1,
				0, 0,

				1, 0,
				0, 0,
				1, 1
			];
		} else if(this._slope > 0) {
			// if slope is positive, draw a line from top-left to bottom-right
			strokeVertices = [
				-xFill+x, yFill-2*yAdj+y,  0.0,
				-xFill+2*xAdj+x, yFill+y,  0.0,
				xFill-2*xAdj+x, -yFill+y,  0.0,

				xFill+x, -yFill+2*yAdj+y,  0.0,
				xFill-2*xAdj+x, -yFill+y,  0.0,
				-xFill+2*xAdj+x, yFill+y,  0.0
			];

			strokeTextures = [
				0, 0,
				0, 1,
				1, 0,

				1, 1,
				1, 0,
				0, 1
			];
		} else {
			// else slope is negative, draw a line from bottom-left to top-right
			strokeVertices = [
				xFill-2*xAdj+x, yFill+y,  0.0,
				-xFill+2*xAdj+x, -yFill+y,  0.0,
				-xFill+x, -yFill+2*yAdj+y,  0.0,

				-xFill+2*xAdj+x, -yFill+y,  0.0,
				xFill-2*xAdj+x, yFill+y,  0.0,
				xFill+x, yFill-2*yAdj+y,  0.0
			];
	
			strokeTextures = [
				1, 1,
				0, 0,
				0, 1,

				0, 0,
				1, 1,
				1, 0
			];
		}
		
		var z = 0;
		var indices = [];
		var nVerts = strokeVertices.length/3;

		// stroke normals
		var index = 0;
		for (var i=0;  i<nVerts;  i++) {
			// push a normal for each vertex in the stroke
			strokeNormals.push(0.0);  strokeNormals.push(0.0);  strokeNormals.push(1);
			indices.push( index );  index++;
		}

		var prim = ShapePrimitive.create(strokeVertices, strokeNormals, strokeTextures, indices, g_Engine.getContext().renderer.TRIANGLES, indices.length);

		var strokeMaterial = this.makeStrokeMaterial();

		this._primArray.push( prim );
		this._materialNodeArray.push( strokeMaterial.getMaterialNode() );

		world.updateObject(this);
	};

	this.render = function() {
		// get the world
		var world = this.getWorld();
		if (!world)  throw( "null world in rectangle render" );

		 // get the context
		var ctx = world.get2DContext();
		if (!ctx)  return;
	
		// set up the stroke style
		var lineWidth = this._strokeWidth,
            w = this._width,
            h = this._height;

        var c,
            gradient,
            colors,
            len,
            n,
            position,
            cs;

		ctx.beginPath();
		ctx.lineWidth	= lineWidth;
		if (this._strokeColor) {
            if(this._strokeColor.gradientMode) {
                if(this._strokeColor.gradientMode === "radial") {
                    gradient = ctx.createRadialGradient(w/2, h/2, 0, w/2, h/2, Math.max(w/2, h/2));
                } else {
                    gradient = ctx.createLinearGradient(0, h/2, w, h/2);
                }
                colors = this._strokeColor.color;

                len = colors.length;

                for(n=0; n<len; n++) {
                    position = colors[n].position/100;
                    cs = colors[n].value;
                    gradient.addColorStop(position, "rgba(" + cs.r + "," + cs.g + "," + cs.b + "," + cs.a + ")");
                }

                ctx.strokeStyle = gradient;

            } else {
                c = "rgba(" + 255*this._strokeColor[0] + "," + 255*this._strokeColor[1] + "," + 255*this._strokeColor[2] + "," + this._strokeColor[3] + ")";
                ctx.strokeStyle = c;
            }

			// get the points
			var p0,  p1;
			if(this._slope === "vertical") {
				p0 = [0.5*w, 0];
				p1 = [0.5*w, h];
			} else if(this._slope === "horizontal") {
				p0 = [0, 0.5*h];
				p1 = [w, 0.5*h];
			} else if(this._slope > 0) {
				p0 = [this._xAdj, this._yAdj];
				p1 = [w - this._xAdj,  h - this._yAdj];
			} else {
				p0 = [this._xAdj, h - this._yAdj];
				p1 = [w - this._xAdj,  this._yAdj];
			}
			
			// draw the line
			ctx.moveTo( p0[0],  p0[1] );
			ctx.lineTo( p1[0],  p1[1] );
			ctx.stroke();
		}
	};

	/*
	// type is Dotted or Dashed
	this.createStippledStrokes = function(strokeVertices, strokeSize, slope, type, innerX, innerY)
	{
		var stippleLength = strokeSize;

		if (type === "Dotted")
		{
			stippleLength = this.DOT_LENGTH;
		}
		else if(type === "Dashed")
		{
			stippleLength = this.DASH_LENGTH;
		}

		var numStrokes = 0;
		var startPos = -innerX;
		var endPos = innerX;

		var gap = this.GAP_LENGTH;
		if(slope === "horizontal")
		{
			gap *= this._scaleX;
			stippleLength *= this._scaleX;
		}
		else if(slope === "vertical")
		{
			startPos = -innerY;
			endPos = innerY;
		}
		else
		{
			if(this._scaleX < 1)
			{
				startPos = -innerY;
				endPos = innerY;
				strokeSize = (strokeSize*this._scaleX)/2;
//                strokeSize *= this._scaleX;
				stippleLength *= this._scaleX;
			}
			else
			{
				strokeSize = strokeSize/2;
				gap *= this._scaleX;
				stippleLength *= this._scaleX;
			}

		}

		while(startPos + stippleLength < endPos)
		{
			if(slope === "horizontal")
			{
				strokeVertices.push(startPos); strokeVertices.push(-strokeSize); strokeVertices.push(0);
				strokeVertices.push(startPos); strokeVertices.push(strokeSize); strokeVertices.push(0);
				strokeVertices.push(startPos+stippleLength); strokeVertices.push(-strokeSize); strokeVertices.push(0);

				strokeVertices.push(startPos); strokeVertices.push(strokeSize); strokeVertices.push(0);
				strokeVertices.push(startPos+stippleLength); strokeVertices.push(-strokeSize); strokeVertices.push(0);
				strokeVertices.push(startPos+stippleLength); strokeVertices.push(strokeSize); strokeVertices.push(0);
			}
			else if(slope === "vertical")
			{
				strokeVertices.push(-strokeSize); strokeVertices.push(startPos); strokeVertices.push(0);
				strokeVertices.push(strokeSize); strokeVertices.push(startPos); strokeVertices.push(0);
				strokeVertices.push(-strokeSize); strokeVertices.push(startPos+stippleLength); strokeVertices.push(0);

				strokeVertices.push(strokeSize); strokeVertices.push(startPos); strokeVertices.push(0);
				strokeVertices.push(-strokeSize); strokeVertices.push(startPos+stippleLength); strokeVertices.push(0);
				strokeVertices.push(strokeSize); strokeVertices.push(startPos+stippleLength); strokeVertices.push(0);
			}
			else
			{
				strokeVertices.push(startPos); strokeVertices.push(startPos*-slope-strokeSize); strokeVertices.push(0);
				strokeVertices.push(startPos); strokeVertices.push(startPos*-slope+strokeSize); strokeVertices.push(0);
				strokeVertices.push(startPos+stippleLength); strokeVertices.push((startPos+stippleLength)*-slope-strokeSize); strokeVertices.push(0);

				strokeVertices.push(startPos); strokeVertices.push(startPos*-slope+strokeSize); strokeVertices.push(0);
				strokeVertices.push(startPos+stippleLength); strokeVertices.push((startPos+stippleLength)*-slope-strokeSize); strokeVertices.push(0);
				strokeVertices.push(startPos+stippleLength); strokeVertices.push((startPos+stippleLength)*-slope+strokeSize); strokeVertices.push(0);
			}

			numStrokes += 6;

			startPos += (stippleLength+gap);
		}
//
		return numStrokes;
	}
	*/

	this.collidesWithPoint = function( x, y ) {
		if(x < this._xOffset) return false;
		if(x > (this._xOffset + this._width)) return false;
		if(y < this._yOffset) return false;
		if(y > (this._yOffset + this._height)) return false;

		return true;
	}
 };

Line.prototype = new GeomObj();

if (typeof exports === "object") {
    exports.Line = Line;
}