/******************************************
 * Websanova.com
 *
 * Resources for web entrepreneurs
 *
 * @author          Websanova
 * @copyright       Copyright (c) 2012 Websanova.
 * @license         This wScratchPad jQuery plug-in is dual licensed under the MIT and GPL licenses.
 * @link            http://www.websanova.com
 * @github			http://github.com/websanova/wScratchPad
 * @version         Version 1.4.4
 *
 ******************************************/
(function($)
{
	$.fn.wScratchPad = function(option, settings)
	{
		if(typeof option === 'object')
		{
			settings = option;
		}
		else if(typeof option == 'string')
		{
			var values = [];

			var elements = this.each(function()
			{
				var data = $(this).data('_wScratchPad');

				if(data)
				{
					if(option === 'reset') { data.reset(); }
					else if(option === 'clear') { data.clear(); }
					else if(option === 'enabled') { data.enabled = settings === true; }
					else if($.fn.wScratchPad.defaultSettings[option] !== undefined)
					{
						if(settings !== undefined) { data.settings[option] = settings; }
						else { values.push(data.settings[option]); }
					}
				}
			});

			if(values.length === 1) { return values[0]; }
			if(values.length > 0) { return values; }
			else { return elements; }
		}
		
		settings = $.extend({}, $.fn.wScratchPad.defaultSettings, settings || {});

		return this.each(function()
		{
			var elem = $(this);
			var $settings = jQuery.extend(true, {}, settings);

			//test for HTML5 canvas
			var test = document.createElement('canvas');
			if(!test.getContext)
			{
				elem.html("Browser does not support HTML5 canvas, please upgrade to a more modern browser.");
				return false;	
			}

			var sp = new ScratchPad($settings, elem);
			
			elem.append(sp.generate());
			
			//get number of pixels of canvas for percent calculations 
			sp.pixels = sp.canvas.width * sp.canvas.height;
			
			elem.data('_wScratchPad', sp);
			
			sp.init();
		});
	};

	$.fn.wScratchPad.defaultSettings =
	{
		width			: 210,					// set width - best to match image width
		height			: 100,					// set height - best to match image height
		image			: null,		// set image path
		image2			: null,					// set overlay image path - if set color is not used
		color			: '#336699',			// set scratch color - if image2 is not set uses color
		overlay			: 'none',				// set the type of overlay effect 'none', 'lighter' - only used with color
		size			: 10,					// set size of scratcher
		realtimePercent : false,              	// Update scratch percent only on the mouseup/touchend (for better performances on mobile device)
		scratchDown		: null,					// scratchDown callback
		scratchUp		: null,					// scratchUp callback
		scratchMove		: null,					// scratcMove callback
		cursor			: null					// Set path to custom cursor
	};
	
	function ScratchPad(settings, elem)
	{
		this.sp = null;
		this.settings = settings;
		this.$elem = elem;
		
		this.enabled = true;
		this.scratch = false;
		
		this.canvas = null;
		this.ctx = null;
		
		return this;
	}
	
	ScratchPad.prototype = 
	{
		generate: function()
		{
			var $this = this;
			
			this.canvas = document.createElement('canvas');
			this.ctx = this.canvas.getContext('2d');
			
			this.sp =
			$('<div></div>')
			.css({position: 'relative'})
			.append(
				$(this.canvas)
				.attr('width', this.settings.width + 'px')
				.attr('height', this.settings.height + 'px')
			)
			
			$(this.canvas)
			.mousedown(function(e)
			{
				if(!$this.enabled) return true;

				e.preventDefault();
				e.stopPropagation();
				
				//reset canvas offset in case it has moved
				$this.canvas_offset = $($this.canvas).offset();
				
				$this.scratch = true;
				$this.scratchFunc(e, $this, 'Down');
			})
			.mousemove(function(e)
			{
				e.preventDefault();
				e.stopPropagation();
				
				if($this.scratch) $this.scratchFunc(e, $this, 'Move');
			})
			.mouseup(function(e)
			{
				e.preventDefault();
				e.stopPropagation();
				
				//make sure we are in draw mode otherwise this will fire on any mouse up.
				if($this.scratch)
				{
					$this.scratch = false;
					$this.scratchFunc(e, $this, 'Up');
				}
			});

			this.bindMobile(this.sp);
			
			return this.sp;
		},
		
		bindMobile: function($el)
		{
			$el.bind('touchstart touchmove touchend touchcancel', function ()
			{
				var touches = event.changedTouches, first = touches[0], type = ""; 

				switch (event.type)
				{
					case "touchstart": type = "mousedown"; break; 
					case "touchmove": type = "mousemove"; break; 
					case "touchend": type = "mouseup"; break; 
					default: return;
				}

				var simulatedEvent = document.createEvent("MouseEvent"); 

				simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
				first.target.dispatchEvent(simulatedEvent);
				event.preventDefault();
			});
		},

		init: function()
		{
			this.sp.css('width', this.settings.width);
			this.sp.css('height', this.settings.height);
			this.sp.css('cursor', (this.settings.cursor ? 'url("' + this.settings.cursor + '"), default' : 'default'));

			$(this.canvas).css({cursor: (this.settings.cursor ? 'url("' + this.settings.cursor + '"), default' : 'default')});
			
			this.canvas.width = this.settings.width;
			this.canvas.height = this.settings.height;
			
			this.pixels = this.canvas.width * this.canvas.height;
			
			if(this.settings.image2)
			{
				this.drawImage(this.settings.image2);
			}
			else
			{
				if(this.settings.overlay != 'none')
				{
					if(this.settings.image)
					{
						this.drawImage(this.settings.image);
					}
					this.ctx.globalCompositeOperation = this.settings.overlay;
				}
				else
				{
					this.setBgImage();
				}
				
				this.ctx.fillStyle = this.settings.color;
				this.ctx.beginPath();
				this.ctx.rect(0, 0, this.settings.width, this.settings.height)
				this.ctx.fill();
			}
		},

		reset: function()
		{
			this.ctx.globalCompositeOperation = 'source-over';
			this.init();
		},

		clear: function()
		{
			this.ctx.clearRect(0, 0, this.settings.width, this.settings.height);
		},

		setBgImage: function()
		{
			if(this.settings.image)
			{
				this.sp.css({backgroundImage: 'url('+this.settings.image+')'});
			}
		},

		drawImage: function(imagePath)
		{
			var $this = this;
			var img = new Image();
  			img.src = imagePath;
  			$(img).load(function(){
  				$this.ctx.drawImage(img, 0, 0);
  				$this.setBgImage();
  			})
		},

		scratchFunc: function(e, $this, event)
		{
			e.pageX = Math.floor(e.pageX - $this.canvas_offset.left);
			e.pageY = Math.floor(e.pageY - $this.canvas_offset.top);
			
			$this['scratch' + event](e, $this);
			
			if(this.settings.realtimePercent || event == "Up") {
				if($this.settings['scratch' + event]) $this.settings['scratch' + event].apply($this, [e, $this.scratchPercentage($this)]);
			}
		},

		scratchPercentage: function($this)
		{
			var hits = 0;
			var imageData = $this.ctx.getImageData(0,0,$this.canvas.width,$this.canvas.height)
			
			for(var i=0, ii=imageData.data.length; i<ii; i=i+4)
			{
				if(imageData.data[i] == 0 && imageData.data[i+1] == 0 && imageData.data[i+2] == 0 && imageData.data[i+3] == 0) hits++;
			}
			
			return (hits / $this.pixels) * 100;
		},

		scratchDown: function(e, $this)
		{
			$this.ctx.globalCompositeOperation = 'destination-out';
			$this.ctx.lineJoin = "round";
			$this.ctx.lineCap = "round";
			$this.ctx.strokeStyle = $this.settings.color;
			$this.ctx.lineWidth = $this.settings.size;
			
			//draw single dot in case of a click without a move
			$this.ctx.beginPath();
			$this.ctx.arc(e.pageX, e.pageY, $this.settings.size/2, 0, Math.PI*2, true);
			$this.ctx.closePath();
			$this.ctx.fill();
			
			//start the path for a drag
			$this.ctx.beginPath();
			$this.ctx.moveTo(e.pageX, e.pageY);
		},
		
		scratchMove: function(e, $this)
		{
			$this.ctx.lineTo(e.pageX, e.pageY);
			$this.ctx.stroke();
		},
		
		scratchUp: function(e, $this)
		{
			$this.ctx.closePath();
		},
	}
})(jQuery);