/*
 * Scratch Project Editor and Player
 * Copyright (C) 2014 Massachusetts Institute of Technology
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

// BlockArg.as
// John Maloney, August 2009
//
// A BlockArg represents a Block argument slot. Some BlockArgs, contain
// a text field that can be edited by the user. Others (e.g. booleans)
// are immutable. In either case, they be replaced by a reporter block
// of the right type. That is, dropping a reporter block onto a BlockArg
// inside a block causes the BlockArg to be replaced by the reporter.
// If a reporter is removed, a BlockArg is added to the block.
//
// To create a custom BlockArg widget such as a color picker, make a
// subclass of BlockArg for the widget. Your constructor is responsible
// for adding child display objects and setting its width and height.
// The widget must initialize argValue and update it as the user
// interacts with the widget. In some cases, the widget may need to
// override the setArgValue() method. If the widget can accept dropped
// arguments, it should set base to a BlockShape to support drag feedback.

package blocks {
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.FocusEvent;
	import flash.events.MouseEvent;
	import flash.filters.BevelFilter;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFieldType;
	import flash.utils.setTimeout;
	
	import cc.makeblock.mbot.uiwidgets.lightSetter.LightSensor;
	
	import scratch.BlockMenus;
	
	import translation.Translator;
	
	import util.Color;

public class BlockArg extends Sprite {

	static public const emotion:BitmapData = new BitmapData(16, 8, false, 0);
	public static const epsilon:Number = 1 / 4294967296;

	public var type:String;//变量类型
	public var base:BlockShape;
	public var argValue:* = '';//变量值
	public var isNumber:Boolean;
	private var _isEditable:Boolean;
	public var field:TextField;
	public var menuName:String;//变量菜单类型

	private var menuIcon:Shape;

	// BlockArg types:
	//	b - boolean (pointed)
	//	c - color selector
	//	d - number with menu (rounded w/ menu icon)
	//	m - string with menu (rectangular w/ menu icon)
	//	n - number (rounded)
	//	s - string (rectangular)
	//	none of the above - custom subclass of BlockArg
	public function BlockArg(type:String, color:int, editable:Boolean = false, menuName:String = '', fZero:Boolean = true) {
		this.type = type;
		//trace("BlockArg:["+type+"]["+color+"]["+editable+"]["+menuName+"]");
		if (color == -1) { // copy for clone; omit graphics
			if ((type == 'd') || (type == 'n')) isNumber = true;
			return;
		}
		var c:int = Color.scaleBrightness(color, 0.92);
		if (type == 'b') {
			base = new BlockShape(BlockShape.BooleanShape, c);
			//argValue = false;
		} else if (type == 'c') {
			base = new BlockShape(BlockShape.RectShape, c);
			this.menuName = 'colorPicker';
			addEventListener(MouseEvent.MOUSE_DOWN, invokeMenu);
		} else if (type == 'd') {
			base = new BlockShape(BlockShape.NumberShape, c);
			isNumber = true;
			this.menuName = menuName;
			addEventListener(MouseEvent.MOUSE_DOWN, invokeMenu);
		} else if (type == 'm') {
			base = new BlockShape(BlockShape.RectShape, c);
			this.menuName = menuName;
			addEventListener(MouseEvent.MOUSE_DOWN, invokeMenu);
		} else if (type == 'n') {
			base = new BlockShape(BlockShape.NumberShape, c);
			isNumber = true;
			//argValue = 0;
		} else if (type == 's') {
			base = new BlockShape(BlockShape.RectShape, c);
			isNumber = true;
		}else {
			// custom type; subclass is responsible for adding
			// the desired children, setting width and height,
			// and optionally defining the base shape
			return;
		}

		if (type == 'c') {
			base.setWidthAndTopHeight(13, 13);
			setArgValue(Color.random());
		} else if(menuName == "drawFace"){
			base.setWidthAndTopHeight(LightSensor.COUNT_W * LightSensor.BMP_ICON_SCALE, LightSensor.COUNT_H * LightSensor.BMP_ICON_SCALE, true);
		}else {
			base.setWidthAndTopHeight(30, Block.argTextFormat.size + 6); // 15 for normal arg font
		}
		base.filters = blockArgFilters();
		addChild(base);

		if ((type == 'd') || (type == 'm' && menuName != "drawFace")) { // add a menu icon
			menuIcon = new Shape();
			var g:Graphics = menuIcon.graphics;
			g.beginFill(0, 0.6); // darker version of base color
			g.lineTo(7, 0);
			g.lineTo(3.5, 4);
			g.lineTo(0, 0);
			g.endFill();
			menuIcon.y = 5;
			addChild(menuIcon);
		}
		
		if(menuName == "drawFace"){
			base.bmd = defaultBmd;
		}

		if (editable || isNumber || (type == 'm' && menuName != "drawFace")) { // add a string field
			field = makeTextField();
			if ((type == 'm') && !editable) field.textColor = 0xFFFFFF;
			else base.setWidthAndTopHeight(30, Block.argTextFormat.size + 5); // 14 for normal arg font
			//field.text = isNumber ? '10' : '';
			//if (isNumber) field.restrict = '0-9e.\\-'; // restrict to numeric characters
			if (isNumber)
			{
				//field.maxChars = 22;
				//if (fZero)
				//{
					field.restrict = '0-9.\\-'; // restrict to numeric characters
					
				//}				
				//else
				//{
					//field.restrict = '1-9.\\-'; // restrict to numeric characters
					
				//}

			}
			if (editable) {
				base.setColor(0xFFFFFF); // if editable, set color to white
				_isEditable = true;
			}
			field.addEventListener(FocusEvent.FOCUS_OUT, stopEditing);
			addChild(field);
			textChanged(null);
		}else {
			base.redraw();
		}
	}
	
	static private const defaultBmd:BitmapData = new BitmapData(16, 8, false, 0xFFFFFF);

	public function get isEditable():Boolean {
		return _isEditable;
	}
	public function set isEditable(v:Boolean):void{
		_isEditable = v;
	}
	public function labelOrNull():String { return field ? field.text : null }

	public function setArgValue(value:*, label:String = null):void {
		// if provided, label is displayed in field, rather than the value
		// this is used for sprite names and to support translation
		argValue = value;
		//trace("setArgValue:["+value+"]["+label+"]");
		if (field != null) {
			var s:String = (value == null) ? '' : value;
			field.text = (label) ? label : s;
			if(value is String)
			if(label==null&&menuName==null){
				value = s;
				field.text = type=="s"?Translator.map(value):value;
			}
			if (menuName && !label && (value is String) && (value != '')) {
				if (BlockMenus.shouldTranslateItemForMenu(value, menuName)) {
					// Translate menu value
					
					field.text = Translator.map(value);
				}
			}

			if (!label && (value is Number) && ((value - epsilon) is int)) {
				// Append '.0' to numeric values that are exactly epsilon
				// greather than an integer. See comment in textChanged().
				field.text = (value - epsilon) + '.0';
			}
			textChanged(null);
			argValue = value; // set argValue after textChanged()
			return;
		}
		if (type == 'c') base.setColor(int(argValue) & 0xFFFFFF);
		base.redraw();
	}

	public function startEditing():void {
		if (isEditable) {
			field.type = TextFieldType.INPUT;
			field.selectable = true;
			if (field.text.length == 0) field.text = '  ';
			field.setSelection(0, field.text.length);
			root.stage.focus = field;
		}
	}

	private function stopEditing(ignore:*):void {
		field.type = TextFieldType.DYNAMIC;
		field.selectable = false;
	}
	
	// filters for BlockArg outlines
	static private var _blockArgFilters:Array;
	private function blockArgFilters():Array {
		if(null == _blockArgFilters){
			var f:BevelFilter = new BevelFilter(1);
			f.blurX = f.blurY = 2;
			f.highlightAlpha = 0.3;
			f.shadowAlpha = 0.6;
			f.angle = 240;  // change light angle to show indentation
			_blockArgFilters = [f];
		}
		return _blockArgFilters;
	}

	private function makeTextField():TextField {
		var tf:TextField = new TextField();
		offsetTextField(tf);
		tf.autoSize = TextFieldAutoSize.LEFT;
		Block.argTextFormat.bold = false;
		tf.defaultTextFormat = Block.argTextFormat;
		tf.selectable = false;
		tf.addEventListener(Event.CHANGE, textChanged);
		return tf;
	}

	private function offsetTextField(tf:TextField):void {
		if("b" == type){
			tf.x = 5;
			tf.y = 0;
		}else if(isNumber){
			tf.x = 3;
			tf.y = 0;
		}else{
			tf.x = 2;
			tf.y = -1;
		}
	}
	
	static private var isShowingCode:Boolean;

	private function textChanged(evt:*):void {
		
		if (isNumber) {
			// optimization: coerce to a number if possible
			var n:Number = Number(field.text);
			var last_value:* = argValue;
			if (!isNaN(n) && n < 1000000000 ) {
				//argValue = n;
				if ((field.text.indexOf('.') >= 0) && (n is int)) {
					// if text includes a decimal point, make value a float as a signal to
					// primitives (e.g. random) to use real numbers rather than integers.
					// Note: Flash does not appear to distinguish between a floating point
					// value with no fractional part and an int of that value. We mark
					// arguments like 1.0 by adding a tiny epsilon to force them to be a
					// floating point number. Certain primitives, such as random, use
					// this to decide whether to work in integers or real numbers.
					argValue += epsilon;
				}
				
				{
				    var i:int = field.text.indexOf('.');
					if (i > 0 )
					{
   				      //trace("textChanged1:["+(field.length - (i+1))+"]"+field.text);
					  if ((field.length - (i+1)) > 6)
					  {
						  field.text = last_value;
						  argValue = field.text;
						 // trace("textChanged2:["+(field.length - (i+1))+"]"+field.text);
						 
					  }
					  else
					  {
						  argValue = field.text;
						  //trace("textChanged3:"+argValue);
					  }
					}
					else
					{
					  argValue = n;
					  if (field.text != "")
					  {
						field.text = argValue;
					  }

					//trace("textChanged4:"+argValue);
					}
					
					//trace("textChanged5:"+argValue);
				}
				
			}
			else
			{
			  if (field.text != '-')
			  field.text = argValue;
			  //trace("textChanged6:"+argValue);
			}
		
		}
		else
		{
			argValue = field.text;
		}
		// fix layout:
		var padding:int = (type == 'n') ? 3 : 0;
		if (type == 'b') padding = 8;
		if (menuIcon != null) padding = (type == 'd') ? 10 : 13;
		var w:int = Math.max(14, field.textWidth + 6 + padding);
		if (menuIcon) menuIcon.x = w - menuIcon.width - 3;
		base.setWidth(w);
		base.redraw();
		if (parent is Block) Block(parent).fixExpressionLayout();

		if (evt && IQCar.app) IQCar.app.setSaveNeeded();
		if(IQCar.app.stageIsArduino && !isShowingCode){
			isShowingCode = true;
			setTimeout(onShowArduinoCode, 0);
		}
	}
	
	private function onShowArduinoCode():void // show arduino code stdio
	{
		IQCar.app.scriptsPart.showArduinoCode();
		isShowingCode = false;
	}

	private function invokeMenu(evt:MouseEvent):void {
		if ((menuIcon != null) && (evt.localX <= menuIcon.x)) return;
		if (Block.MenuHandlerFunction != null) {
			Block.MenuHandlerFunction(evt, parent, this, menuName);
			trace("invokeMenu:"+menuName);
			evt.stopImmediatePropagation();
		}
	}

}}
