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

import jp.agentec.abook.abv.bl.common.Constant.EventType;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.ui.common.constant.IFPDFConst;
import jp.agentec.abook.abv.ui.common.vo.Size;
import jp.agentec.abook.abv.ui.home.activity.GuideViewActivity;
import jp.agentec.abook.abv.ui.viewer.activity.ContentViewActivity;
import android.content.Context;
import android.database.sqlite.SQLiteConstraintException;
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.widget.ImageView;
import android.widget.RelativeLayout;

public class PageView  extends ImageView {
	protected static final String TAG = "PageView";

	protected RelativeLayout pageLayout;
	protected int mPageNumber = 0;
	protected boolean isLandscape;
	protected int width;
	protected int height;
	protected int displayWidth;
	protected int displayHeight;
	protected Size pageSize = null;
	protected Size initSize = null;
	private float mLastTouchX;
	private float mLastTouchY;
	
	private static final int INVALID_POINTER_ID = -1;
	private int mActivePointerId = INVALID_POINTER_ID;
	protected static final int TOUCH_NONE = 0;
	private static final int TOUCH_MULTI = 2;
	protected int touchMode = TOUCH_NONE;
	
	protected float scaling0 = 0f;
	protected float pageScale;
	protected float mScaleFactor = 1.0f;
//	private int zoomLevel = 1000;
	
	protected Matrix initMatrix = null;
	private Matrix baseMatrix = new Matrix();
	protected Matrix imgMatrix = new Matrix();
	
	protected GestureDetector mGestureDetector;
	protected ScaleGestureDetector mScaleDetector = null;

	private boolean isOperationPdf;
	
	public PageView(Context context) {
		super(context);
	}

	public PageView(Context context, Size pageSize, int pageNumber, int displayWidth, int displayHeight, RelativeLayout pageLayout, boolean isOperationPdf) {
		super(context);
		Logger.i(TAG, "[PageView]: page=" + pageNumber);

		this.mPageNumber = pageNumber;
		this.pageLayout = pageLayout;
		this.displayWidth = displayWidth;
		this.displayHeight = displayHeight;
		this.pageSize = pageSize;

		setScaleType(ImageView.ScaleType.MATRIX);
		
		mGestureDetector = new GestureDetector(context, mGestureListener);

        this.isOperationPdf = isOperationPdf;
		// ABook Check : doubletap = 新作業指示
		if (!isOperationPdf) {
            mGestureDetector.setOnDoubleTapListener(mDoubleTapListener);
        }
		mScaleDetector = new ScaleGestureDetector(context, onScaleGestureListener);

		if (pageSize.width > pageSize.height) { // SCREEN_ORIENTATION_SENSOR_LANDSCAPE
			isLandscape = (float)pageSize.width / (float)pageSize.height > IFPDFConst.ASPECT_4_3_LAND;
		} else { // SCREEN_ORIENTATION_SENSOR_PORTRAIT
			isLandscape = (float) pageSize.height / (float) pageSize.width < IFPDFConst.ASPECT_4_3_PORT;
		}
		
		pageScale = Math.min((float) displayHeight / (float) pageSize.height, (float) displayWidth / (float) pageSize.width);
		width = (int) (pageSize.width * pageScale);
		height = (int) (pageSize.height * pageScale);
		
		Logger.i(TAG, "Init size: width=" + width + " heigth=" + height + " pdfWidth=" + pageSize.width + " pdfHeight=" + pageSize.height + " scale=" + pageScale + " displayWidth=" + displayWidth + " displayHeight=" + displayHeight);
		initSize = new Size(width, height);
	}

	protected SimpleOnGestureListener mGestureListener = new SimpleOnGestureListener() {};
	
	protected OnDoubleTapListener mDoubleTapListener = new OnDoubleTapListener() {
			
			@Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
				Logger.v(TAG, "[OnDoubleTapListener]:[onSingleTapConfirmed]");
				return false;
			}
			
			@Override
            public boolean onDoubleTapEvent(MotionEvent e) {
				Logger.v(TAG, "[OnDoubleTapListener]:[onDoubleTapEvent]");
				return true;
			}
			
			@Override
            public boolean onDoubleTap(MotionEvent e) {
				// 通常、1回のみの処理であればこのメソッドで実装する
				Logger.v(TAG, "[OnDoubleTapListener]:[onDoubleTap]");
				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) {
					// 拡大の設定解除
					setDefaultScale();
					// touchEventではscaleが1.0の場合は処理しないため
					callZoomAction(imgMatrix);
				} else {
					imgMatrix.set(checkMatrix);
					mScaleFactor = matrixScale;
				}
				
				Logger.v(TAG, "[onDoubleTap:setImageMatrix] %s", getMatrixValue(imgMatrix)[Matrix.MSCALE_X]);
				setImageMatrix(imgMatrix);
				if (nowScale < mScaleFactor) {
					callZoomLog(e.getX(), e.getY(), EventType.DOUBLE_TAP);
				}
				return true;
			}
		};
		
		
	
		
	protected final SimpleOnScaleGestureListener onScaleGestureListener = new SimpleOnScaleGestureListener() {
			private float startScale;

			@Override
			public boolean onScale(ScaleGestureDetector detector) {
				Matrix checkMatrix = new Matrix();
				checkMatrix.set(baseMatrix);
				checkMatrix.postScale(detector.getScaleFactor(),
						detector.getScaleFactor(), detector.getFocusX(),
						detector.getFocusY());
				float[] matrixValue = getMatrixValue(checkMatrix);
				float matrixScale = matrixValue[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;
					Logger.v(TAG, "[onScale:setImageMatrix] %s", getMatrixValue(imgMatrix)[Matrix.MSCALE_X]);
					setImageMatrix(imgMatrix);
				}
				Logger.v(TAG, "SimpleOnScaleGestureListener:[onScale]:scaleFactor=%s", mScaleFactor);
				return super.onScale(detector);
			}
	
			@Override
			public boolean onScaleBegin(ScaleGestureDetector detector) {
				startScale = mScaleFactor;

				Logger.v(TAG, "SimpleOnScaleGestureListener:[onScaleBegin]");
				baseMatrix.set(imgMatrix);
				Logger.v(TAG, "[onScaleBegin:setImageMatrix] %s", getMatrixValue(imgMatrix)[Matrix.MSCALE_X]);
				setImageMatrix(imgMatrix);
				touchMode = TOUCH_MULTI; // onDraw（）処理を制御
				return super.onScaleBegin(detector);
			}
	
			@Override
			public void onScaleEnd(ScaleGestureDetector detector) {
				fixPosition();

				if (startScale < mScaleFactor) {
					callZoomLog(detector.getFocusX(), detector.getFocusY(), EventType.PINCH_OUT);
				}

				if (mScaleFactor == 1.0f) {
					// 通常はonTouchUpのMotionEvent.ACTION_UPからCallする
					callZoomAction(imgMatrix);
				}
				touchMode = TOUCH_NONE;
			}
		};

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		Logger.d(TAG, "[onTouchEvent]:action=" + ev.getAction() + ", count=" + ev.getPointerCount());
		super.onTouchEvent(ev);
		
		mGestureDetector.onTouchEvent(ev);
		mScaleDetector.onTouchEvent(ev);
		if (mScaleDetector.isInProgress()) {
			Logger.d(TAG, "[onTouchEvent]:mScaleGestureDetector");
			return true;
		}
		
		if (mScaleFactor > 1.0f) {
			final int action = ev.getAction();
			int actionMode = action & MotionEvent.ACTION_MASK;
			switch (actionMode) {
			case MotionEvent.ACTION_DOWN: {
				Logger.v(TAG, "[onTouchEvent]:ACTION_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) {
					break;
				}
				final int pointerIndex = ev.findPointerIndex(mActivePointerId);
				final float x = ev.getX(pointerIndex);
				final float y = ev.getY(pointerIndex);

				// Only move if the ScaleGestureDetector isn't processing a gesture.
				if (!mScaleDetector.isInProgress()) {
					Logger.v(TAG, "[onTouchEvent]:ACTION_MOVE");
					final float dx = x - mLastTouchX;
					final float dy = y - mLastTouchY;

					// 移動する前に移動可能エリアをチェックするためのMatrix
					Matrix checkMatrix = new Matrix();
					checkMatrix.set(imgMatrix);
					checkMatrix.postTranslate(dx, dy);
					imgMatrix.set(checkMatrix);
				}

				mLastTouchX = x;
				mLastTouchY = y;

				break;
			}

			case MotionEvent.ACTION_UP: {
				mActivePointerId = INVALID_POINTER_ID;
				fixPosition();
				callZoomAction(imgMatrix);
				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) {
							// This was our active pointer going up. Choose a new
							// active pointer and adjust accordingly.
							final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
							mLastTouchX = ev.getX(newPointerIndex);
							mLastTouchY = ev.getY(newPointerIndex);
							mActivePointerId = ev.getPointerId(newPointerIndex);
						}
						break;
			}
			}
			Logger.v(TAG, "actionMode=%s", actionMode);
			if (actionMode == MotionEvent.ACTION_MOVE) {
				Logger.v(TAG, "[onTouchEvent:setImageMatrix] %s", getMatrixValue(imgMatrix)[Matrix.MSCALE_X]);
				setImageMatrix(imgMatrix);
			} else {
				postInvalidate();
			}
		}
		
		return true;
	}

	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		Logger.v(TAG, "[onSizeChanged]:width=%s height=%s oldw=%s, oldh=%s, pageNumber=%s", w, h, oldw, oldh, mPageNumber);
		if ((float)w / h > 0.9 && (float)w / h < 1.1) { // 不正な値の場合無視
			return;
		}
		
		super.onSizeChanged(w, h, oldw, oldh);
		width = w;
		height = h;
		if (scaling0 == 0f) {
			scaling0 = Math.min(width / (float)pageSize.width, height / (float)pageSize.height);
			Logger.v(TAG, "[onSizeChanged]:scaling0=%s", scaling0);
		}
		float marginX = (width - getCurrentPageWidth()) / 2;
		float marginY = (height - getCurrentPageHeight()) / 2;
		Logger.v(TAG, "marginX=%s marginY=%s", marginX, marginY);
		if (imgMatrix == null) {
			imgMatrix = new Matrix();
		}
		imgMatrix.postTranslate(marginX, marginY);
		Logger.v(TAG, "[onSizeChanged:setImageMatrix] %s", getMatrixValue(imgMatrix)[Matrix.MSCALE_X]);
		setImageMatrix(imgMatrix);
		if (initMatrix == null) {
			// 初期値保存
			initMatrix = new Matrix();
			initMatrix.set(imgMatrix);
		}
	}
	
	protected float getCurrentPageWidth() {
		float currentpagewidth = pageSize.width * scaling0 * mScaleFactor;// * (zoomLevel * 0.001f);
		Logger.v(TAG, "currentpagewidth=%s", currentpagewidth);
		return currentpagewidth;
	}

	protected float getCurrentPageHeight() {
		float currentpageheight = pageSize.height * scaling0 * mScaleFactor;// * (zoomLevel * 0.001f);
		Logger.v(TAG, "currentpageheight=%s", currentpageheight);
		return currentpageheight;
	}

	protected float[] getMatrixValue(Matrix matrix) {
		float[] value = new float[9];
		if (matrix != null) {
			matrix.getValues(value);
		}
		return value;
	}

	public Size getInitSize() {
		return initSize;
	}
	
	public void setDefaultScale() {
		imgMatrix.set(initMatrix);
		mScaleFactor = 1.0f;
	}
	
	public void setZoomMatrix(Matrix matrix) {
		float[] m = getMatrixValue(matrix);
		imgMatrix.setValues(m);
		setImageMatrix(imgMatrix);
		invalidate();
		fixPosition();
		Logger.d(TAG, "[onScaleEnd]:x=%s", m[Matrix.MTRANS_X]);
	}
	
	private void fixPosition() {
		float[] matrixValue = getMatrixValue(imgMatrix);
		final float matrixScale = matrixValue[Matrix.MSCALE_X];
		Logger.d(TAG, "[fixPosition][start]:(matrix)scale=%s, (View)width=%s, height=%s", matrixScale, getWidth(), getHeight());
		if (matrixScale < 1.0f) {
			Logger.v(TAG, "[fixPosition]:init");
			// 初期サイズへ変更
			imgMatrix.set(initMatrix);
			setImageMatrix(imgMatrix);
			mScaleFactor = 1.0f;
		} else {
			mScaleFactor = matrixScale;
			
			// 拡大サイズが画面サイズより小さい
			Logger.v(TAG, "[fixPosition][before]x=%s, y=%s", matrixValue[Matrix.MTRANS_X], matrixValue[Matrix.MTRANS_Y]);
			
			// PDFの左上座標
			float minX = matrixValue[Matrix.MTRANS_X];
			float minY = matrixValue[Matrix.MTRANS_Y];
						
			// 横幅の拡大サイズが画面サイズより小さい
			if (getCurrentPageWidth() < getWidth()) {
				minX = getWidth() / 2 - getCurrentPageWidth() / 2;
				Logger.v(TAG, "[fixPosition]center horizontal");
			}
			// 縦幅の拡大サイズが画面サイズより小さい
			if (getCurrentPageHeight() < getHeight()) {
				minY = getHeight() / 2 - getCurrentPageHeight() / 2;
				Logger.v(TAG, "[fixPosition]center vectical");
			}
			
			// PDFの右上座標
			float maxX = minX + getCurrentPageWidth();
			float maxY = minY + getCurrentPageHeight();
			
			if (minX < 0 && maxX < getWidth()) {
				// 左側によっていて、右側があいている状況
				minX += getWidth() - maxX; // 右側へ寄せる
				Logger.v(TAG, "[fixPosition]:fittingToRight:x=%s", minX);
			} else if (minX > 0 && maxX > getWidth()) {                                                                                                                                                                                                                                                                                                                                                                                                
				// 右側によっていて、左側があいている状況
				minX = 0; // 左側へ寄せる
				Logger.v(TAG, "[fixPosition]:fittingToLeft:x=%s", minX);
			}
			if (minY < 0 && maxY < getHeight()) {
				// 上側によっていて、下側があいている状況
				minY += getHeight() - maxY; // 下側に寄せる
				Logger.v(TAG, "[fixPosition]:fittingToBottom:y=%s", minY);
			} else if (minY > 0 && maxY - minY > getHeight()) {
				// 下側ｊによっていて、上側があいている状況
				minY = 0; // 上側へ寄せる
				Logger.v(TAG, "[fixPosition]:fittingToTop:y=%s", minY);
			}
			
			if (minX != matrixValue[Matrix.MTRANS_X] || minY != matrixValue[Matrix.MTRANS_Y]) {
				matrixValue[Matrix.MTRANS_X] = minX;
				matrixValue[Matrix.MTRANS_Y] = minY;
				imgMatrix.setValues(matrixValue);
				setImageMatrix(imgMatrix);
				Logger.v(TAG, "[fixPosition]setImageMatrix");
			}
			Logger.v(TAG, "[fixPosition][after]x=%s, y=%s", matrixValue[Matrix.MTRANS_X], matrixValue[Matrix.MTRANS_Y]);
		}
		Logger.d(TAG, "[fixPosition]:[end]");
	}                            
	
	private void callZoomAction(Matrix matrix) {
		float[] matrixValue = getMatrixValue(matrix);
		if (getContext() instanceof ContentViewActivity) {
			((ContentViewActivity) getContext()).callZoomAction(matrixValue[Matrix.MTRANS_X], matrixValue[Matrix.MTRANS_Y], matrixValue[Matrix.MSCALE_X], getCurrentPageWidth(), getCurrentPageHeight());
		} else {
			((GuideViewActivity) getContext()).callZoomAction(matrixValue[Matrix.MTRANS_X], matrixValue[Matrix.MTRANS_Y], matrixValue[Matrix.MSCALE_X], getCurrentPageWidth(), getCurrentPageHeight());
		}

	}

	private void callZoomLog(float x, float y, String eventType) {
		float[] matrixValue = getMatrixValue(imgMatrix);

		// 表示している位置、拡大率をもとに座標を求める
		double dx = (x - matrixValue[Matrix.MTRANS_X]) / mScaleFactor;
		double dy = (y - matrixValue[Matrix.MTRANS_Y]) / mScaleFactor;

        try {
        	if (getContext() instanceof ContentViewActivity) {
				((ContentViewActivity) getContext()).callZoomLog(dx, dy, eventType);
			} else {
				((GuideViewActivity) getContext()).callZoomLog(dx, dy, eventType);
			}

        } catch (SQLiteConstraintException e) { // ignore
            Logger.e(TAG, "callZoomLog failed. ignored.", e);
        }
	}
}
