package com.tryandroid.media;

import java.io.File;
import java.util.List;

import com.kurukurupapa.tryandroid.fw.util.LogUtil;

import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

public class CameraRecViewHelper implements SurfaceHolder.Callback,
		PictureCallback, View.OnTouchListener {

	private Camera camera;
	private Context context;
	private SurfaceView cameraRecView;

	public CameraRecViewHelper(Context context, SurfaceView cameraRecView) {
		this.context = context;
		this.cameraRecView = cameraRecView;

		// カメラのプレビュー設定
		SurfaceHolder surfaceHolder = cameraRecView.getHolder();
		surfaceHolder.addCallback(this);
		// カメラ映像をGPUに直接送り込むための設定
		surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

		// タッチ時に画像保存するため、ビューへのタッチイベントを紐付け。
		cameraRecView.setOnTouchListener(this);
	}

	@Override
	public void surfaceCreated(SurfaceHolder surfaceHolder) {
		LogUtil.called();

		try {
			// カメラの準備
			camera = Camera.open();
			camera.setPreviewDisplay(surfaceHolder);

		} catch (Exception e) {
			String msg = "カメラの準備に失敗しました。";
			LogUtil.e(msg, e);
			Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
			releaseCamera();
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder surfaceHolder, int format,
			int width, int height) {
		LogUtil.called();

		if (camera == null) {
			return;
		}

		try {
			// プレビュー停止
			camera.stopPreview();

			// プレビュー設定
			Parameters params = camera.getParameters();
			setPreviewSize(params, width, height);
			camera.setParameters(params);

			// プレビュー開始
			camera.startPreview();

		} catch (Exception e) {
			String msg = "プレビュー開始に失敗しました。";
			LogUtil.e(msg, e);
			Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
			releaseCamera();
		}
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
		LogUtil.called();

		if (camera == null) {
			return;
		}

		try {
			// プレビュー終了
			camera.stopPreview();

		} catch (Exception e) {
			String msg = "プレビュー終了に失敗しました。";
			LogUtil.e(msg, e);
			Toast.makeText(context, msg, Toast.LENGTH_LONG).show();

		} finally {
			releaseCamera();
		}
	}

	/**
	 * Viewをタッチされた時に、撮影を行う。
	 */
	@Override
	public boolean onTouch(View view, MotionEvent motionEvent) {
		LogUtil.called();

		if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
			try {
				// 撮影要求
				camera.takePicture(null, null, this);

				// このタイミングでプレビューを再開すると、アプリが無応答になるので、コメントアウト。
				// onPictureTakenメソッドへ移動する。
				// // プレビュー再開
				// camera.startPreview();

			} catch (Exception e) {
				String msg = "カメラ撮影に失敗しました。";
				LogUtil.e(msg, e);
				Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
				releaseCamera();
			}
			return false;
		}

		return true;
	}

	/**
	 * カメラオブジェクトからの撮影結果を受け取り、ファイルへ保存する。
	 */
	@Override
	public void onPictureTaken(byte[] data, Camera camera) {
		LogUtil.called();

		String path = null;

		try {
			// ファイルパス名取得
			File baseDir = CameraUtil.getBaseDir(context);
			path = new File(baseDir, CameraUtil.getFileName())
					.getAbsolutePath();

			// ファイル保存
			CameraUtil.save(baseDir, path, data);

			// 結果をトースト表示
			Toast.makeText(context, "ファイルを保存しました。\n" + path, Toast.LENGTH_LONG)
					.show();

		} catch (Exception e) {
			String msg = "ファイル保存に失敗しました。";
			LogUtil.e(msg, e);
			Toast.makeText(context, msg + "\n" + path, Toast.LENGTH_LONG)
					.show();
		}

		try {
			// プレビュー開始
			camera.startPreview();

		} catch (Exception e) {
			String msg = "プレビュー再開に失敗しました。";
			LogUtil.e(msg, e);
			Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
			releaseCamera();
		}
	}

	/**
	 * カメラオブジェクトを解放する。
	 */
	private void releaseCamera() {
		if (camera == null) {
			return;
		}

		try {
			camera.release();
			camera = null;
		} catch (Exception e) {
			LogUtil.e("カメラの開放に失敗しました。", e);
		}
	}

	private void setPreviewSize(Camera.Parameters params, int maxWidth,
			int maxHeight) {
		// 端末のカメラがサポートするプレビュー解像度を取得する
		// ※Android OS 2.1以降
		List<Camera.Size> list = params.getSupportedPreviewSizes();

		// サポートする解像度から、画面のサイズ以下を選択する
		for (Camera.Size size : list) {
			if (size.width <= maxWidth && size.height <= maxHeight) {
				// 解像度を設定
				params.setPreviewSize(size.width, size.height);
				LogUtil.i("PreviewSize: " + size.width + "x" + size.height);

				// プレビュー画像のサイズを設定
				int[] scaledSize = getScaledSize(size.width, size.height,
						maxWidth, maxHeight);
				ViewGroup.LayoutParams layoutParams = cameraRecView
						.getLayoutParams();
				layoutParams.width = scaledSize[0];
				layoutParams.height = scaledSize[1];
				cameraRecView.setLayoutParams(layoutParams);
				LogUtil.i("CameraViewSize: " + layoutParams.width + "x"
						+ layoutParams.height);

				break;
			}
		}
	}

	public int[] getScaledSize(int srcWidth, int srcHeight, int maxWidth,
			int maxHeight) {
		int[] scaledSize = new int[2];

		double widthScale = (double) maxWidth / srcWidth;
		double heightScale = (double) maxHeight / srcHeight;
		if (widthScale <= heightScale) {
			scaledSize[0] = maxWidth;
			scaledSize[1] = (int) (srcHeight * widthScale);
		} else {
			scaledSize[0] = (int) (srcWidth * heightScale);
			scaledSize[1] = maxHeight;
		}

		return scaledSize;
	}

}
