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

import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.common.util.ContentFileUtil;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.vo.Size;
import jp.agentec.abook.abv.ui.viewer.view.PageView;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Process;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;

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

	// PDFレンダリング関連
	private static final int PROGRESS_DIALOG_DELAY = 200;
	private AsyncTask<Void,Void,Bitmap> mDrawEntireTask;
	private AsyncTask<Void,Void,Void> mDrawPatchTask;
	private Rect mPrevPatchArea = null;
	private Bitmap mPatchBm;
	private ProgressBar mBusyIndicator;
	private final Handler mHandler = new Handler();
	
	private FoxitPdfCore mFoxitPdfCore = null;

	private long contentId;
	private boolean isOutputFile;
	private PdfImageProvider pdfImageProvider;
	private Size mDisplaySize;

	public PDFPageView(Context context, FoxitPdfCore foxitPdfCore, PdfImageProvider pdfImageProvider, Size pdfSize, int pageNumber, Size displaySize, RelativeLayout pageLayout, long contentId) {
		super(context, pdfSize, pageNumber, displaySize.width, displaySize.height, pageLayout, false);
		Logger.i(TAG, "[PDFPageView]: page=" + pageNumber);
		this.mFoxitPdfCore = foxitPdfCore;
		this.pdfImageProvider = pdfImageProvider;
		this.contentId = contentId;
		this.mDisplaySize = displaySize;
		this.isOutputFile = context.getResources().getBoolean(R.bool.pdf_image_output);

		if (isHardwareAccelerated() == false) {
			// HW acceleration は無効にする
			setLayerType(View.LAYER_TYPE_SOFTWARE, null);
		}
	}

	public PDFPageView(Context context, FoxitPdfCore foxitPdfCore, PdfImageProvider pdfImageProvider, Size pdfSize, int pageNumber, Size displaySize, RelativeLayout pageLayout, long contentId, boolean isOperationPdf) {
		super(context, pdfSize, pageNumber, displaySize.width, displaySize.height, pageLayout, isOperationPdf);
		Logger.i(TAG, "[PDFPageView]: page=" + pageNumber);
		this.mFoxitPdfCore = foxitPdfCore;
		this.pdfImageProvider = pdfImageProvider;
		this.contentId = contentId;
		this.mDisplaySize = displaySize;
		this.isOutputFile = context.getResources().getBoolean(R.bool.pdf_image_output);

		if (isHardwareAccelerated() == false) {
			// HW acceleration は無効にする
			setLayerType(View.LAYER_TYPE_SOFTWARE, null);
		}
	}
	
	@Override
	public boolean isOpaque() {
		return true;
	}


	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
//		Logger.v(TAG, "[onDraw]:" + " page: " + mPageNumber + " w=" + width + " h=" + height + " pdfW=" + pdfSize.width + " pdfH=" + pdfSize.height + " mScaleFactor=" + mScaleFactor  + " MTRANS_X=" + getMatrixValue(imgMatrix)[Matrix.MTRANS_X]);
		createPatch(canvas);
	}
	
	
	public void setPage() {
		setPage(width, height);
	}

	/**
	 * ページ全体ビットマップ生成
	 * 
	 * @param targetWidth
	 * @param targetHeight
	 */
	public void setPage(final int targetWidth, final int targetHeight) {
		Logger.d(TAG, "[setPage]:" + mPageNumber + " : " + targetWidth + "x" + targetHeight);
		
		if (getDrawable() != null) {
			return;
		}

		// Cancel pending render task
		if (mDrawEntireTask != null) {
			mDrawEntireTask.cancel(true);
			mDrawEntireTask = null;
		}

		// Render the page in the background
		mDrawEntireTask = new AsyncTask<Void,Void,Bitmap>(Process.THREAD_PRIORITY_BACKGROUND) {
			@Override
            protected Bitmap doInBackground(Void... v) {
				try {
					Thread.sleep(10); // 他のスレッドに実行権を渡す
				} catch (InterruptedException e) {}
				
				Bitmap bm = PdfImageProvider.readFromFile(mDisplaySize, contentId, mPageNumber); // キャッシュがある場合
				if (bm != null) {
					return bm;
				}
				
				if (!pdfImageProvider.cancelTask(mPageNumber, false)) {// 別途イメージ作成を行っている場合はキャンセルする ⇒ 全体がキャンセルされる可能性がある
					Logger.e(TAG, "pdfImageProvider.cancelTask failed. pageNumber=" + mPageNumber);
					try {
						Thread.sleep(1000); // 1秒待つ
					} catch (InterruptedException e) {}
					
					bm = PdfImageProvider.readFromFile(mDisplaySize, contentId, mPageNumber); // 再度キャッシュをチェック
					if (bm != null) {
						return bm;
					}
				}

				Logger.d(TAG, "[drawPage]: pageNumber=" + mPageNumber + " sizeX=" + 0 + " sizeY="  + 0 + " patchX=" + targetWidth + " patchY=" + targetHeight + " patchWidth=" + targetWidth + " patchHeight=" + targetHeight);
				
				pdfImageProvider.setPauseTask(true);
				String imagePath = ContentFileUtil.getPdfImagePath(contentId, mPageNumber);
				bm = pdfImageProvider.createPdfImage(contentId, mDisplaySize, mPageNumber, imagePath, isOutputFile, false, false);
				pdfImageProvider.setPauseTask(false);
				return bm;
			}

			@Override
            protected void onPreExecute() {
				setImageDrawable(null);

				if (mBusyIndicator == null) {
					mBusyIndicator = new ProgressBar(getContext());
					mBusyIndicator.setIndeterminate(true);
					mBusyIndicator.setBackgroundResource(R.drawable.busy);
					RelativeLayout.LayoutParams layoutParam = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
					layoutParam.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.CENTER_IN_PARENT);
					pageLayout.addView(mBusyIndicator, layoutParam);
					mBusyIndicator.setVisibility(INVISIBLE);
					mHandler.postDelayed(new Runnable() {
						@Override
                        public void run() {
							if (mBusyIndicator != null) {
                                mBusyIndicator.setVisibility(VISIBLE);
                            }
						}
					}, PROGRESS_DIALOG_DELAY);
				}
			}

			@Override
            protected void onPostExecute(Bitmap bm) {
				try {
					pageLayout.removeView(mBusyIndicator);
					mBusyIndicator = null;
					Bitmap resizedBm = Bitmap.createScaledBitmap(bm, targetWidth, targetHeight, true);
					if (!resizedBm.equals(bm)) { // 同じ場合がある
						bm.recycle();
					}
					setImageBitmap(resizedBm);
					Logger.v(TAG, "[mEntire.invalidate]: page=%d, width=%d, height=%d", mPageNumber, targetWidth, targetHeight);
					invalidate();
					
					// ZoomRelativeLayoutを表示
					if (pageLayout.getChildAt(1) != null) {
						pageLayout.getChildAt(1).setVisibility(View.VISIBLE);
					}
				} catch (Exception e) {
					Logger.e(TAG, "onPostExecute error. page=" + mPageNumber, e); // ignore
				}
			}
		};

		mDrawEntireTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[])null);
	}
	
	/**
	 * パッチ領域ビットマップ生成
	 *
	 * @param canvas
	 */
	public void createPatch(final Canvas canvas) {
		if (touchMode != TOUCH_NONE) {
			return;
		}
		
		float scale = getMatrixValue(imgMatrix)[Matrix.MSCALE_X];
		if (isLandscape && scale <= displayWidth / initSize.width // 画面サイズを超えない限り再描画はしない
				|| !isLandscape && scale <= displayHeight / initSize.height) {
			return;
		}

//		Logger.v(TAG, "[createPatch]: page: " + mPageNumber);

		float left = getMatrixValue(imgMatrix)[Matrix.MTRANS_X];
		float top = getMatrixValue(imgMatrix)[Matrix.MTRANS_Y];
		
		final Point patchViewSize = new Point((int)(initSize.width * scale), (int)(initSize.height * scale));
		Rect pdfWholeArea = new Rect((int)left,(int)top,(int)left+patchViewSize.x,(int)top+patchViewSize.y);
		// If the viewArea's size matches the unzoomed size, there is no need for an hq patch
		final Rect patchArea = new Rect(0, 0, getWidth(), getHeight());
		// Intersect and test that there is an intersection
		if (!patchArea.intersect(pdfWholeArea)) {
            return;
        }

		final Rect viewArea = new Rect(patchArea); // 実際の表示座標

		// Offset patch area to be relative to the view top left
		patchArea.offset(-pdfWholeArea.left, -pdfWholeArea.top);
		if (mPrevPatchArea != null && mPrevPatchArea.equals(patchArea) && mPatchBm != null) { // 変化がない場合
			Bitmap bm = mPatchBm;
			Rect src = new Rect(0, 0, bm.getWidth(), bm.getHeight());
			canvas.drawBitmap(bm, src, viewArea, null);
			postInvalidate();
			return;
		}

		if (mDrawPatchTask != null) {
			mDrawPatchTask.cancel(true);
			mDrawPatchTask = null;
		}

		mDrawPatchTask = new AsyncTask<Void,Void,Void>(Process.THREAD_PRIORITY_BACKGROUND) {
			@Override
            protected Void doInBackground(Void... v) {
				Logger.d(TAG, "[drawPatch]: pageNumber=" + mPageNumber + " sizeX=" + patchViewSize.x + " sizeY="  + patchViewSize.y
						+ " patchX=" + patchArea.left + " patchY=" + patchArea.top + " patchWidth=" + patchArea.width() + " patchHeight=" + patchArea.height());
				try {
					if (pdfImageProvider != null) {
						pdfImageProvider.setPauseTask(true);
					}
					mPatchBm = mFoxitPdfCore.drawPage(mPageNumber, patchViewSize.x, patchViewSize.y, patchArea.left, patchArea.top, patchArea.width(), patchArea.height());
				} catch (OutOfMemoryError e) {
					Logger.e(TAG, "mDrawPatchTask.doInBackground() " + e.toString());
				} finally {
					if (pdfImageProvider != null) {
						pdfImageProvider.setPauseTask(false);
					}
				}
				return null;
			}

			@Override
            protected void onPostExecute(Void v) {
				if (mPatchBm != null) {
					Rect src = new Rect(0, 0, mPatchBm.getWidth(), mPatchBm.getHeight());
					// HW acceleration は無効にしないとここで落ちる 要検証
					canvas.drawBitmap(mPatchBm, src, viewArea, null);

					postInvalidate();

					mPrevPatchArea = new Rect(patchArea);
				}
			}
		};
		Logger.v(TAG, "[createPatch]: page: " + mPageNumber);
		mDrawPatchTask.execute();
	}
	
	public void releaseResources() {
		Logger.d(TAG, "[releaseResources]:" + " page: " + mPageNumber);

		// Cancel pending render task
		if (mDrawEntireTask != null) {
			mDrawEntireTask.cancel(true);
			mDrawEntireTask = null;
		}

		if (mDrawPatchTask != null) {
			mDrawPatchTask.cancel(true);
			mDrawPatchTask = null;
		}
		
		if (mBusyIndicator != null) {
			pageLayout.removeView(mBusyIndicator);
			mBusyIndicator = null;
		}
		
		Drawable d = getDrawable();
		if (d instanceof BitmapDrawable) {
			Bitmap b = ((BitmapDrawable)d).getBitmap();
			if (b!=null) {
				b.recycle();
			}
		}
		if (d!=null) {
			d.setCallback(null);
		}
		setImageDrawable(null);
		
		if (mPatchBm != null) {
			mPatchBm.recycle();
			mPatchBm = null;
		}
	}
}
