package info.guardianproject.keanuapp.ui.qr;

import static android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT;
import static android.view.SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS;
import static info.guardianproject.keanu.core.KeanuConstants.LOG_TAG;

import java.io.IOException;
import java.util.List;

import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;


@SuppressWarnings("deprecation")
public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
AutoFocusCallback {

	private static String TAG = CameraView.class.getPackage().getName();

	private Camera camera = null;
	private PreviewConsumer previewConsumer = null;
	private Size desiredSize = null;
	private int displayOrientation = 0;
	private boolean surfaceExists = false;

	public CameraView(Context context) {
		super(context);
		setKeepScreenOn(true);
	}

	void start(Camera newCamera, PreviewConsumer previewConsumer,
			int rotationDegrees, boolean macro) {

		camera = newCamera;

		this.previewConsumer = previewConsumer;
		setDisplayOrientation(rotationDegrees);
		SurfaceHolder holder = getHolder();
		holder.addCallback(this);
		holder.setType(SURFACE_TYPE_PUSH_BUFFERS); // Required on Android < 3.0
		if(surfaceExists) startPreview(holder);

		new Thread ()
		{
			public void run ()
			{
				while (camera != null) {
					try {
						Thread.sleep(1000);
					}
					catch (Exception e){}
					try {
						camera.autoFocus(CameraView.this);
					}
					catch (Exception e)
					{
						Log.w(LOG_TAG,"qr code scanning not working: " + e.toString());
					}

				}
			}
		}.start();
	}

	void stop() {
		stopPreview();
		getHolder().removeCallback(this);
        camera = null;
	}

	private void startPreview(SurfaceHolder holder) {
		try {

			Parameters params = camera.getParameters();

			if (params.getSupportedFocusModes().contains(
					Parameters.FOCUS_MODE_AUTO)) {
				params.setFocusMode(Parameters.FOCUS_MODE_AUTO);
			}

			List<Size> previewSizes = camera.getParameters().getSupportedPreviewSizes();
			List<Size> pictureSizes = camera.getParameters().getSupportedPictureSizes();

            params.setPreviewSize(previewSizes.get(previewSizes.size()-2).width,previewSizes.get(previewSizes.size()-2).height);
			params.setPictureSize(pictureSizes.get(pictureSizes.size()-2).width,pictureSizes.get(pictureSizes.size()-2).height);
			camera.setParameters(params);

			camera.setPreviewDisplay(holder);
			camera.startPreview();

			previewConsumer.start(camera);
		} catch(IOException e) {
			Log.e(TAG, "Error starting camera preview", e);
		}
	}

	private void stopPreview() {
		try {
			previewConsumer.stop();

            if (camera != null) {
                camera.stopPreview();
            }
		} catch(Exception e) {
			Log.e(TAG, "Error stopping camera preview", e);
		}
	}

	private void setDisplayOrientation(int rotationDegrees) {
		int orientation;
		CameraInfo info = new CameraInfo();
		Camera.getCameraInfo(0, info);
		if(info.facing == CAMERA_FACING_FRONT) {
			orientation = (info.orientation + rotationDegrees) % 360;
			orientation = (360 - orientation) % 360;
		} else {
			orientation = (info.orientation - rotationDegrees + 360) % 360;
		}
		camera.setDisplayOrientation(orientation);
		displayOrientation = orientation;
	}

	public void surfaceCreated(SurfaceHolder holder) {
		surfaceExists = true;
		startPreview(holder);
	}

	public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
		stopPreview();

        if (camera != null) {
            boolean rotatePreview = displayOrientation % 180 == 90;
            Parameters params = camera.getParameters();
            List<Size> sizes = params.getSupportedPreviewSizes();
            Size bestSize = null;
            int bestError = 0;
            for (Size size : sizes) {
                int width = rotatePreview ? size.height : size.width;
                int height = rotatePreview ? size.width : size.height;
                int widthError = Math.abs(width - w);
                int heightError = Math.abs(height - h);
                int error = Math.max(widthError, heightError);
                if (bestSize == null || error < bestError) {
                    bestSize = size;
                    bestError = error;
                }
            }

            params.setPreviewSize(bestSize.width, bestSize.height);


            camera.setParameters(params);
            desiredSize = bestSize;

            startPreview(holder);
        }
	}

	public void surfaceDestroyed(SurfaceHolder holder) {
		surfaceExists = false;
	}

	public void onAutoFocus(boolean success, Camera camera) {
		Log.d(TAG, "Auto focus: " + success);
	}

	@Override
	protected void onMeasure(int width, int height) {
		if(desiredSize == null) super.onMeasure(width, height);
		else if(displayOrientation % 180 == 90)
			setMeasuredDimension(desiredSize.height, desiredSize.width);
		else setMeasuredDimension(desiredSize.width, desiredSize.height);
	}
}