package jp.agentec.abook.abv.ui.viewer.view;

import jp.agentec.abook.abv.ui.common.constant.IFPDFConst;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;

public class ActionZoomLayout extends RelativeLayout {
	private float mScaleFactor = 1.0f;
	private float mLastTouchX;
	private float mLastTouchY;
	private static final int INVALID_POINTER_ID = -1;
	private int mActivePointerId = INVALID_POINTER_ID;
	private GestureDetector mGestureDetector;
	private ScaleGestureDetector mScaleDetector;
	private Matrix initMatrix = new Matrix();
	private Matrix baseMatrix = new Matrix(); // タッチダウン時の画像保存用
	private Matrix imgMatrix = new Matrix();
	
	public ActionZoomLayout(Context context) {
		super(context);
		mGestureDetector = new GestureDetector(context, mGestureListener);
		mGestureDetector.setOnDoubleTapListener(mDoubleTapListener);
		mScaleDetector = new ScaleGestureDetector(context, onScaleGestureListener);
	}
	
	@Override
	protected void dispatchDraw(Canvas canvas) {
		canvas.save();
		canvas.concat(imgMatrix);
		super.dispatchDraw(canvas);
		canvas.restore();
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		super.onTouchEvent(ev);

		ViewGroup parenGroup = (ViewGroup) getParent();
		for (int i = 0; i < parenGroup.getChildCount(); i++) {
			View view = parenGroup.getChildAt(i);
			if (!view.equals(this)) {
				view.onTouchEvent(ev);
			}
		}
		
		mScaleDetector.onTouchEvent(ev);
		if (mScaleDetector.isInProgress()) {
			return true;
		}
		
		mGestureDetector.onTouchEvent(ev);
		if (mScaleFactor > 1.0f) {
			final int action = ev.getAction();
		    switch (action & MotionEvent.ACTION_MASK) {
		       
 			    case MotionEvent.ACTION_DOWN:
 			   case MotionEvent.ACTION_POINTER_DOWN: {
			        final float x = ev.getX();
			        final float y = ev.getY();
			        
			        mLastTouchX = x;
			        mLastTouchY = y;
			        mActivePointerId = ev.getPointerId(0);
			        baseMatrix.set(imgMatrix);
			        break;
			    } 
 			    
			    case MotionEvent.ACTION_MOVE: {
			    	
			    	if(mActivePointerId != INVALID_POINTER_ID) {
			    		final int pointerIndex = ev.findPointerIndex(mActivePointerId);
				    	final float x = ev.getX(pointerIndex);
				    	final float y = ev.getY(pointerIndex);

				    	if (!mScaleDetector.isInProgress()) {
				    		final float dx = x - mLastTouchX;
				    		final float dy = y - mLastTouchY;
				    		Matrix checkMatrix = new Matrix();
				    		checkMatrix.set(imgMatrix);
				    		checkMatrix.postTranslate(dx, dy);

				    		imgMatrix.set(checkMatrix);
				    		invalidate();
				    	}
				    	mLastTouchX = x;
				    	mLastTouchY = y;
			    	}
			        break;
			    }
			        
			    case MotionEvent.ACTION_UP: {
			        mActivePointerId = INVALID_POINTER_ID;
			        break;
			    }
			        
			    case MotionEvent.ACTION_CANCEL: {
			        mActivePointerId = INVALID_POINTER_ID;
			        break;
			    }
			    
			    case MotionEvent.ACTION_POINTER_UP: {
			        final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) 
			                >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
			        final int pointerId = ev.getPointerId(pointerIndex);
			        if (pointerId == mActivePointerId) {
			            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
			            mLastTouchX = ev.getX(newPointerIndex);
			            mLastTouchY = ev.getY(newPointerIndex);
			            mActivePointerId = ev.getPointerId(newPointerIndex);
			        }
			        break;
			    }
		    }
	    }
		return true;
	}
	
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
	}
	
	private float[] getMatrixValue(Matrix matrix) {
		float[] value = new float[9];
		if (matrix != null) {
			matrix.getValues(value);
		}
		return value;
	}
	
	public float[] getMatrixValue() {
		return getMatrixValue(imgMatrix);
	}
	
	private SimpleOnScaleGestureListener onScaleGestureListener = new SimpleOnScaleGestureListener() {
		@Override
		public boolean onScale(ScaleGestureDetector detector) {
			Matrix checkMatrix = new Matrix();
			checkMatrix.set(baseMatrix);
			checkMatrix.postScale(detector.getScaleFactor(),
					detector.getScaleFactor(), detector.getFocusX(),
					detector.getFocusY());
			float matrixScale = getMatrixValue(checkMatrix)[Matrix.MSCALE_X];
			if (IFPDFConst.MIN_SCALE < matrixScale && matrixScale < IFPDFConst.MAX_SCALE) {
				imgMatrix.set(baseMatrix);
					imgMatrix.postScale(detector.getScaleFactor(),
							detector.getScaleFactor(), detector.getFocusX(),
							detector.getFocusY());
					mScaleFactor = matrixScale;
				postInvalidate();
			}
			return super.onScale(detector);
		}

        @Override
		public boolean onScaleBegin(ScaleGestureDetector detector) {
			baseMatrix.set(imgMatrix);
			return super.onScaleBegin(detector);
		}
		
		@Override
		public void onScaleEnd(ScaleGestureDetector detector) {
			float[] matrixValue = getMatrixValue(imgMatrix);
			final float matrixScale = matrixValue[Matrix.MSCALE_X];
			if (matrixScale < 1.0f) {
				// 初期サイズへ変更
				imgMatrix.set(initMatrix);
				postInvalidate();
			}
			mScaleFactor = matrixScale;
		}
	};
	
	private SimpleOnGestureListener mGestureListener = new SimpleOnGestureListener() {		
		@Override
		public boolean onDown(MotionEvent e) {
			return super.onDown(e);
		}
	};
	
	private OnDoubleTapListener mDoubleTapListener = new OnDoubleTapListener() {
		@Override
		public boolean onSingleTapConfirmed(MotionEvent e) {
			return false;
		}
		
		@Override
		public boolean onDoubleTapEvent(MotionEvent e) {
			return true;
		}
		
		@Override
		public boolean onDoubleTap(MotionEvent e) {
			// 通常、1回のみの処理であればこのメソッドで実装する		
			float[] matrixValue = getMatrixValue(imgMatrix);
			float nowScale = matrixValue[Matrix.MSCALE_X];
			float nextMatrixScale;
			if (nowScale < IFPDFConst.MAX_SCALE / 1.5f) { // MAX_SCALEの3分の２以下の場合
				nextMatrixScale = IFPDFConst.MAX_SCALE / 1.5f / nowScale;
			} else {
				if (nowScale == IFPDFConst.MAX_SCALE) {
					nextMatrixScale = 1.5f;// ただMax値を超える値にさせるため
				} else {
					nextMatrixScale = IFPDFConst.MAX_SCALE / nowScale;
				}
			}
			
			Matrix checkMatrix = new Matrix(imgMatrix);
			checkMatrix.postScale(nextMatrixScale, nextMatrixScale, e.getX(), e.getY());
			matrixValue = getMatrixValue(checkMatrix);
			float matrixScale = matrixValue[Matrix.MSCALE_X];
			if (matrixScale > IFPDFConst.MAX_SCALE) {
				// 拡大の設定解除
				imgMatrix.set(initMatrix);
				mScaleFactor = 1.0f;
			} else {
				// 拡大の設定
				imgMatrix.set(checkMatrix);
				mScaleFactor = matrixScale;
			}
			postInvalidate();
			return true;
		}
	};
	
	public boolean isZooming() {
		return mScaleFactor > 1.0f;
	}
}