package org.tizen.gwt.sample.raphaelpaint.client;

import org.tizen.gwt.sample.raphaelpaint.client.Enums.Tool;
import org.tizen.gwt.sample.raphaelpaint.client.ShapeDrawer.Props;
import org.tizen.gwt.sample.raphaelpaint.client.overlay.RaphaelAttr;
import org.tizen.gwt.sample.raphaelpaint.client.overlay.JsBag;
import org.tizen.gwt.sample.raphaelpaint.client.overlay.RaphaelElement;
import org.tizen.gwt.sample.raphaelpaint.client.overlay.RaphaelElement.DragCallback;

import com.google.gwt.dom.client.NativeEvent;

public class Transforms implements DragCallback {
	
	private final RaphaelPaint mRaphaelPaint;
	
	//temporary for single drag 
	private float mDx;
	private float mDy;
	private float mSx;
	private float mSy;

	public Transforms(RaphaelPaint raphaelPaint) {
		mRaphaelPaint = raphaelPaint;
	}
	
	public void setRotation(RaphaelElement element, int rotation) {
		element.<JsBag> cast().put(Props.ROTATION, rotation);
		applyTransforms(element);
	}
	
	@Override
	public void onDragStart(RaphaelElement context, float x, float y,
			NativeEvent event) {
		if (mRaphaelPaint.getTool() == Tool.arrow) {
			mRaphaelPaint.setSelectedShape(context);

			// Animation for selected element
			context.animate(JsBag.create().put(RaphaelAttr.FILL_OPACITY, 0.4), 500);
			JsBag props = context.cast();
			switch (mRaphaelPaint.getOperation()) {
				case move:
					// read last translation
					mDx = props.getFloat(Props.TRANSLATE_X);
					mDy = props.getFloat(Props.TRANSLATE_Y);
					break;
	
				case scale:
					// read last scale
					mSx = props.getFloat(Props.SCALE_X);
					mSy = props.getFloat(Props.SCALE_Y);
					break;
				
				default:
					break;
			}
		}
		else if(mRaphaelPaint.getTool() == Tool.eraser) {
			mRaphaelPaint.removeShape(context);
		}
	}

	@Override
	public void onDragMove(RaphaelElement context, float x, float y, float dx,
			float dy, NativeEvent event) {
		if (mRaphaelPaint.getTool() == Tool.arrow) {
			switch (mRaphaelPaint.getOperation()) {
				case move:
					updateTranslationToDragPos(context, dx, dy);
					break;
				case scale:
					updateScaleToDragPos(context, dx, dy);
					break;
				default:
					return;
			}
			applyTransforms(context);
		}
	}

	@Override
	public void onDragEnd(RaphaelElement context, NativeEvent event) {
		if (mRaphaelPaint.getTool() == Tool.arrow) {
			// Animation for selected element
			context.animate(JsBag.create().put(RaphaelAttr.FILL_OPACITY, 1), 500);
		}
	}
	
	protected void updateTranslationToDragPos(RaphaelElement element, float dx, float dy) {
		element.<JsBag> cast()
				.put(Props.TRANSLATE_X, mDx + dx)
				.put(Props.TRANSLATE_Y, mDy + dy);
	}
	
	protected void updateScaleToDragPos(RaphaelElement element, float dx, float dy) {
		JsBag props = element.cast();
		Tool tool = Tool.valueOf(props.<String> get(Props.TOOL_NAME));
		double scaleX, scaleY;
		switch (tool) {
			case circle:
			case square:
				scaleX = scaleY = dx > dy ? 
						mSx + 10 * dx / Constants.CANVAS_WIDTH :
							mSy + 10 * dy / Constants.CANVAS_HEIGHT;
				break;
		
			case ellipse:
			case rectangle:
			case star:
			case triangle:
				scaleX = mSx + 10 * dx / Constants.CANVAS_WIDTH;
				scaleY = mSy + 10 * dy / Constants.CANVAS_HEIGHT;
				break;
				
			case arrow:
			case clear:
			case eraser:
			default:
				return;
		}
		props.put(Props.SCALE_X, scaleX);
		props.put(Props.SCALE_Y, scaleY);
	}
	
	private void applyTransforms(RaphaelElement element) {
		JsBag p = element.cast();
		String transformString = "t" + p.getFloat(Props.TRANSLATE_X) + 
				"," + p.getFloat(Props.TRANSLATE_Y) + 
				"r" + p.getInt(Props.ROTATION) + 
				"s" + p.getFloat(Props.SCALE_X) + 
				"," + p.getFloat(Props.SCALE_Y);
		element.transform(transformString);
		mRaphaelPaint.addBox();
	}
}
