package jp.sourceforge.nicoro;

import static jp.sourceforge.nicoro.Log.LOG_TAG;

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

import org.apache.http.client.ClientProtocolException;
import org.apache.http.impl.client.DefaultHttpClient;

import jp.gr.java_conf.shiseissi.commonlib.APILevelWrapper;
import jp.gr.java_conf.shiseissi.commonlib.ViewUtil;
import jp.sourceforge.nicoro.NicoroAPIManager.ParseGetFLVJikkyo;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.support.v4.app.FragmentActivity;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.ToggleButton;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;

public class NicoroJikkyoPlayer extends FragmentActivity
implements MessageView.Callback, Handler.Callback {
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG & true;
    private static final boolean DEBUG_LOGV = Release.IS_DEBUG & false;

    /** コメントを描画 */
	private static final int MSG_ID_MESSAGE_DRAW_MESSAGE = 0;
	/** コメントサーバーから追加のコメントを取得 */
	private static final int MSG_ID_MESSAGE_ADDED_MESSAGE = 1;
    /** コメントサーバーとの接続エラー */
	private static final int MSG_ID_MESSAGE_OCCURRED_ERROR = 2;
	/** カメラへのパラメータ設定でエラー */
    private static final int MSG_ID_CAMERA_SET_PARAMETERS_FAILED = 3;
    /** カメラのオープンでエラー */
    private static final int MSG_ID_CAMERA_OPEN_FAILED = 4;
    /** カメラへのSurface設定でエラー */
    private static final int MSG_ID_CAMERA_SET_SURFACE_FAILED = 5;

	public static final String INTENT_NAME_COOKIE = "COOKIE";
	public static final String INTENT_NAME_JIKKYO_NUMBER = "JIKKYO_NUMBER";
	public static final String INTENT_NAME_MESSAGE_URL = "MESSAGE_URL";
	public static final String INTENT_NAME_MESSAGE_PORT = "MESSAGE_PORT";
	public static final String INTENT_NAME_THREAD_ID = "THREAD_ID";
	public static final String INTENT_NAME_USER_ID = "USER_ID";
	public static final String INTENT_NAME_HTTP_PORT = "HTTP_PORT";

	private SurfaceView mCameraPreview;
	private MessageView mMessageView;
	private TextView mChannelName;
	private View mBackground;

	private ViewGroup mPlayerController;
	private ToggleButton mButtonCameraOnOff;

	private SurfaceHolder.Callback mCameraPreviewCallback;

    private CameraManager mCameraManager = new CameraManager();

	private LiveMessageLoader mLiveMessageLoader;

//    private Matrix mMatrixMessage = new Matrix();
//    private Random mRandom = new Random();
	private long mStartTime;
//    private Paint mPaintText = new Paint();

    private MessageChatController mMessageChatController = new MessageChatController();

    private SharedPreferences mSharedPreferences;

    private PlayerInfoControllerManager mPlayerInfoControllerManager;

    private DisplayMetrics mMetrics = new DisplayMetrics();
    private int mLastOrientation;

    private boolean mEnableHardwareAccelerated;

    private Context mContext;

	private final HandlerWrapper mHandler = new HandlerWrapper(this);

	@Override
	public boolean handleMessage(Message msg) {
		switch (msg.what) {
		case MSG_ID_MESSAGE_DRAW_MESSAGE:
			if (!isFinishing()) {
				mMessageView.invalidate();

				Message message = mHandler.obtainMessage(
						MSG_ID_MESSAGE_DRAW_MESSAGE);
				mHandler.sendMessageDelayed(message, 33L);
			}
			break;
		case MSG_ID_MESSAGE_ADDED_MESSAGE:
			if (!mHandler.hasMessages(MSG_ID_MESSAGE_DRAW_MESSAGE)) {
				mHandler.sendEmptyMessageDelayed(MSG_ID_MESSAGE_DRAW_MESSAGE, 33L);
			}
			break;
		case MSG_ID_MESSAGE_OCCURRED_ERROR:
			Util.showErrorDialog(NicoroJikkyoPlayer.this,
					(String) msg.obj, true);
			break;
		case MSG_ID_CAMERA_SET_PARAMETERS_FAILED:
        case MSG_ID_CAMERA_OPEN_FAILED:
        case MSG_ID_CAMERA_SET_SURFACE_FAILED:
            Util.showErrorDialog(NicoroJikkyoPlayer.this,
                    getString(R.string.errormessage_jikkyo_camera_failed), /*true*/false);
		    break;
		default:
			break;
		}
		return true;
	}

	private class GetFLVTask extends AsyncTask<String, Void, ParseGetFLVJikkyo> {
		public void executeWrapper(String jikkyoNumber, String cookieUserSession,
				String userAgent) {
			execute(jikkyoNumber, cookieUserSession, userAgent);
		}

		@Override
		protected ParseGetFLVJikkyo doInBackground(String... params) {
			assert params.length == 3;

			final String jikkyoNumber = params[0];
			final String cookieUserSession = params[1];
			final String userAgent = params[2];

			ParseGetFLVJikkyo parseGetFLVJikkyo = null;
            DefaultHttpClient httpClient = Util.createHttpClient();
			try {
				httpClient.getCookieStore().clear();
				parseGetFLVJikkyo = new ParseGetFLVJikkyo();
				parseGetFLVJikkyo.initialize(httpClient,
						jikkyoNumber,
						cookieUserSession, userAgent);
			} catch (ClientProtocolException e) {
				Log.d(LOG_TAG, e.toString(), e);
			} catch (IOException e) {
				Log.d(LOG_TAG, e.toString(), e);
			} finally {
			    httpClient.getConnectionManager().shutdown();
			}

			return parseGetFLVJikkyo;
		}

		@Override
		protected void onPostExecute(ParseGetFLVJikkyo result) {
			if (result == null) {
				return;
			}
			ParseGetFLVJikkyo parseGetFLVJikkyo = result;

			String cookieUserSession = NicoroConfig.getCookieUserSession(mSharedPreferences);
			final String messageUrl = parseGetFLVJikkyo.get("ms");
			final String messagePort = parseGetFLVJikkyo.get("ms_port");
			final String threadId = parseGetFLVJikkyo.get("thread_id");
			final String userId = parseGetFLVJikkyo.get("user_id");

			mLiveMessageLoader = new LiveMessageLoader(messageUrl,
	    			messagePort,
//	    			httpPort,
	    			threadId, cookieUserSession, userId,
	    			getApplicationContext());
			mLiveMessageLoader.setEventListener(new LiveMessageLoaderInterface.EventListener() {
			    @Override
		        public void onConnected(LiveMessageLoaderInterface loader) {
                    // nothing
		        }
				@Override
				public void onAddedMessage(LiveMessageLoaderInterface loader) {
			        Handler handler = mHandler;
                    if (handler != null) {
    					if (!handler.hasMessages(MSG_ID_MESSAGE_ADDED_MESSAGE)) {
    						handler.sendEmptyMessage(MSG_ID_MESSAGE_ADDED_MESSAGE);
    					}
                    }
				}
				@Override
				public void onFinished(LiveMessageLoaderInterface loader) {
					// nothing
				}
				@Override
				public void onOccurredError(LiveMessageLoaderInterface loader,
						String errorMessage) {
			        Handler handler = mHandler;
					if (handler != null) {
						Message message = handler.obtainMessage(MSG_ID_MESSAGE_OCCURRED_ERROR, errorMessage);
						handler.sendMessage(message);
					}
				}
			});
			mLiveMessageLoader.startLoad();

			final String channelName = parseGetFLVJikkyo.get("channel_name");
			if (channelName != null) {
				mChannelName.setText(channelName);
			}
		}
	}
	private GetFLVTask mGetFLVTask;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

        if (DEBUG_LOGD) {
        	Log.d(LOG_TAG, "NicoroJikkyoPlayer onCreate");
        }

        mContext = getApplicationContext();

        mSharedPreferences = Util.getDefaultSharedPreferencesMultiProcess(
                mContext);
        int screenOrientation = mSharedPreferences.getInt(
                NicoroConfig.PLAYER_SCREEN_ORIENTATION,
                ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
        setScreenOrientation(screenOrientation);

        mMessageChatController.resetMessageDataChats(null);

        boolean messageAntialias = mSharedPreferences.getBoolean(
                getString(R.string.pref_key_message_antialias), false);
        mMessageChatController.setAntiAlias(messageAntialias);
//        mPaintText.setAntiAlias(messageAntialias);

        final boolean showHintToast = mSharedPreferences.getBoolean(
                getString(R.string.pref_key_show_hint_toast), true);
        if (showHintToast) {
            Util.showInfoToast(mContext,
                    R.string.toast_explain_player_ctrl);
        }

        Resources res = getResources();
        mEnableHardwareAccelerated = mSharedPreferences.getBoolean(
                res.getString(R.string.pref_key_enable_hardware_accelerated),
                res.getBoolean(R.bool.pref_default_enable_hardware_accelerated));
        if (mEnableHardwareAccelerated) {
            getWindow().setFlags(
                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                    WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
        }

        Configuration config = getResources().getConfiguration();
        mLastOrientation = config.orientation;

        onCreateView();

        Intent intent = getIntent();
        mGetFLVTask = new GetFLVTask();
        final String jikkyoNumber = intent.getStringExtra(
                NicoroJikkyoPlayer.INTENT_NAME_JIKKYO_NUMBER);
        final String cookieUserSession = NicoroConfig.getCookieUserSession(mSharedPreferences);
        final String userAgent = mSharedPreferences.getString(
                NicoroConfig.USER_AGENT, null);
        mGetFLVTask.executeWrapper(jikkyoNumber, cookieUserSession,
                userAgent);

        mStartTime = SystemClock.elapsedRealtime();
    }

	@SuppressWarnings("deprecation")
    private void onCreateView() {
		setContentView(R.layout.nicoro_jikkyoplayer);
		mCameraPreview = ViewUtil.findViewById(this, R.id.camera);
		SurfaceHolder holder = mCameraPreview.getHolder();
		holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
		mCameraPreviewCallback = new SurfaceHolder.Callback() {
			@Override
			public void surfaceChanged(SurfaceHolder holder, int format,
					int width, int height) {
		        if (DEBUG_LOGD) {
		        	Log.d(LOG_TAG, Log.buf().append("surfaceChanged format=")
		        			.append(format).append(" width=").append(width)
		        			.append(" height=").append(height).toString());
		        }

		        mCameraManager.surfaceChanged(format, width, height);
			}
			@Override
			public void surfaceCreated(SurfaceHolder holder) {
			    mCameraManager.setSurfaceHolder(holder);
			}
			@Override
			public void surfaceDestroyed(SurfaceHolder holder) {
			    mCameraManager.removeSurfaceHolder();
			}
		};
		holder.addCallback(mCameraPreviewCallback);

		mChannelName = ViewUtil.findViewById(this, R.id.channel_name);

		mMessageView = ViewUtil.findViewById(this, R.id.message_view);
		if (mEnableHardwareAccelerated) {
            // XXX 常にsoftware layer使用
            APILevelWrapper api = APILevelWrapper.createInstance();
            api.setLayerType(mMessageView, View.LAYER_TYPE_SOFTWARE, null);
		}
		mMessageView.setCallback(this);

        mPlayerController = ViewUtil.findViewById(this, R.id.player_controller);
        mButtonCameraOnOff = ViewUtil.findViewById(mPlayerController,
                R.id.button_camera_onoff);
        mButtonCameraOnOff.setChecked(false);
        mButtonCameraOnOff.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
			@Override
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
			    mCameraManager.openCamera();
				if (isChecked) {
				    if (mCameraPreview != null) {
				        mCameraPreview.setVisibility(View.VISIBLE);
				    }
				    mBackground.setVisibility(View.INVISIBLE);
			        mCameraManager.startPreview();
				} else {
                    if (mCameraPreview != null) {
                        mCameraPreview.setVisibility(View.INVISIBLE);
                    }
                    mBackground.setVisibility(View.VISIBLE);
                    mCameraManager.stopPreview();
				}
			}
        });

        mBackground = ViewUtil.findViewById(this, R.id.background);

        assert mCameraPreview.getVisibility() != View.VISIBLE;
        assert mBackground.getVisibility() == View.VISIBLE;

        mPlayerController.findViewById(R.id.button_more).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                openOptionsMenu();
            }
        });

        mPlayerController.findViewById(R.id.button_hide).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPlayerInfoControllerManager.hidePlayerController();
            }
        });

        AbstractPlayerFragment.setSystemUiVisibility(mCameraPreview,
                mSharedPreferences, getResources());

        mPlayerInfoControllerManager = new PlayerInfoControllerManager(
                mContext, null, mPlayerController, null, null);

        updateMessageViewSize(mLastOrientation);
	}

	@Override
	protected void onDestroy() {
		mGetFLVTask.cancel(true);
		// XXX 非同期終了対応を
    	if (mLiveMessageLoader != null) {
    		mLiveMessageLoader.finish();
    		mLiveMessageLoader = null;
    	}

		super.onDestroy();
		mCameraPreview.getHolder().removeCallback(mCameraPreviewCallback);
		mCameraPreviewCallback = null;
        mCameraPreview = null;
		mHandler.release();
	}

	@Override
	protected void onResume() {
		super.onResume();
	}

	@Override
	protected void onPause() {
		super.onPause();
	}

	@Override
    protected void onStart() {
        super.onStart();

        mPlayerInfoControllerManager.setActionBar(this);

        APILevelWrapper api = APILevelWrapper.createInstance();
        api.setDisplayHomeAsUpEnabled_ActionBar(api.getActionBar(this), true);

        if (mButtonCameraOnOff.isChecked()) {
            mCameraManager.openCamera();
            mCameraManager.startPreview();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        mCameraManager.stopPreview();
        mCameraManager.releaseCamera();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
	    if (mPlayerInfoControllerManager.onTouchEvent(event)) {
			return true;
		}
		return super.onTouchEvent(event);
    }

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
	    if (mPlayerInfoControllerManager.onKeyDown(keyCode, event)) {
	        return true;
	    }
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		return super.onKeyUp(keyCode, event);
	}

    @Override
    public void onBackPressed() {
        if (!mPlayerInfoControllerManager.onBackPressed()) {
            super.onBackPressed();
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (mLastOrientation != newConfig.orientation) {
            updateMessageViewSize(newConfig.orientation);
            mLastOrientation = newConfig.orientation;
        }
    }

    public static class OptionsMenuItem {
        MenuItem playerScreenOrientationLandscape;
        MenuItem playerScreenOrientationPortrait;
        MenuItem playerScreenOrientationUser;
    }
    private OptionsMenuItem mOptionsMenuItem;

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.jikkyo_player, menu);
        if (mOptionsMenuItem == null) {
            mOptionsMenuItem = new OptionsMenuItem();
        }

        mOptionsMenuItem.playerScreenOrientationLandscape =
            menu.findItem(R.id.menu_player_screen_orientation_landscape);
        mOptionsMenuItem.playerScreenOrientationPortrait =
            menu.findItem(R.id.menu_player_screen_orientation_portrait);
        mOptionsMenuItem.playerScreenOrientationUser =
            menu.findItem(R.id.menu_player_screen_orientation_user);

        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        switch (getRequestedOrientation()) {
            case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
                mOptionsMenuItem.playerScreenOrientationLandscape.setChecked(true);
                break;
            case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
                mOptionsMenuItem.playerScreenOrientationPortrait.setChecked(true);
                break;
            case ActivityInfo.SCREEN_ORIENTATION_USER:
                mOptionsMenuItem.playerScreenOrientationUser.setChecked(true);
                break;
        }
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case android.R.id.home:
                finish();
                return true;
            case R.id.menu_hide_actionbar:
                mPlayerInfoControllerManager.hidePlayerController();
                return true;
            case R.id.menu_player_screen_orientation_landscape:
                setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                saveScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                item.setChecked(true);
                return true;
            case R.id.menu_player_screen_orientation_portrait:
                setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                saveScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                item.setChecked(true);
                return true;
            case R.id.menu_player_screen_orientation_user:
                setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
                saveScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);
                item.setChecked(true);
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void updateMessageViewSize(int orientation) {
        ViewGroup.LayoutParams params = mMessageView.getLayoutParams();
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
            params.width = mMetrics.widthPixels;
            params.height = (params.width * 3 + 3) / 4;
        } else {
            params.width = MATCH_PARENT;
            params.height = MATCH_PARENT;
        }
        mMessageView.requestLayout();
    }

    @Override
    protected void finalize() throws Throwable {
    	try {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, "NicoroJikkyoPlayer#finalize start");
            }
    		super.finalize();
    	} finally {
        	if (mLiveMessageLoader != null) {
        		mLiveMessageLoader.finish();
        	}
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, "NicoroJikkyoPlayer#finalize end");
            }
    	}
    }

//	private static LiveMessageLoader createLiveMessageLoader(Intent intent, Context context,
//			String userSession) {
//		LiveMessageLoader liveMessageLoader = null;
//		String cookie = intent.getStringExtra(
//				NicoroJikkyoPlayer.INTENT_NAME_COOKIE);
//		String jikkyoNumber = intent.getStringExtra(
//				NicoroJikkyoPlayer.INTENT_NAME_JIKKYO_NUMBER);
//		String messageUrl = intent.getStringExtra(
//				NicoroJikkyoPlayer.INTENT_NAME_MESSAGE_URL);
//		String messagePort = intent.getStringExtra(
//				NicoroJikkyoPlayer.INTENT_NAME_MESSAGE_PORT);
//		String threadId = intent.getStringExtra(
//				NicoroJikkyoPlayer.INTENT_NAME_THREAD_ID);
//		String userId = intent.getStringExtra(
//				NicoroJikkyoPlayer.INTENT_NAME_USER_ID);
//		String httpPort = intent.getStringExtra(
//				NicoroJikkyoPlayer.INTENT_NAME_HTTP_PORT);
//
//    	liveMessageLoader = new LiveMessageLoader(messageUrl,
//    			messagePort,
////    			httpPort,
//    			threadId, userSession, userId,
//    			context);
//
//		return liveMessageLoader;
//	}

	@Override
	public void drawMessageView(MessageView messageView, Canvas canvas) {
		canvas.drawColor(0x00000000);
		// vposは現在時間基準
		final int vpos = (int) (SystemClock.elapsedRealtime() - mStartTime) / 10;

		mMessageChatController.drawMessageForLive(
				canvas, vpos,
				messageView.getWidth(), messageView.getHeight(),
				false, mLiveMessageLoader);
	}

    private void setScreenOrientation(int screenOrientation) {
        PlayerActivity.setScreenOrientation(this, screenOrientation);
    }
    private void saveScreenOrientation(int screenOrientation) {
        PlayerActivity.saveScreenOrientation(mSharedPreferences, screenOrientation);
    }

    private class CameraManager {
        private Camera mCamera;
        private SurfaceHolder mHolder;
        private boolean mPendingPreview = false;
        private OpenCameraTask mTask;
        private int mSurfaceFormat;
        private int mSurfaceWidth;
        private int mSurfaceHeight;
        private boolean mPendingSurfaceChanged = false;

        public void openCamera() {
            if (mCamera == null) {
                if (mTask == null) {
                    mTask = new OpenCameraTask();
                    mTask.execute();
                }
            }
        }

        public void releaseCamera() {
            if (mTask != null) {
                mTask = null;
            }
            if (mCamera != null) {
                mCamera.release();
                mCamera = null;
            }
        }

        public void setSurfaceHolder(SurfaceHolder holder) {
            mHolder = holder;
            if (mCamera != null) {
                setPreviewDisplay();
                if (mPendingPreview) {
                    mCamera.startPreview();
                    mPendingPreview = false;
                }
            }
        }

        public void removeSurfaceHolder() {
            mHolder = null;
            stopPreview();
        }

        public void surfaceChanged(int format,
                int width, int height) {
//            mFormat = format;
            mSurfaceWidth = width;
            mSurfaceHeight = height;
            if (mCamera == null) {
                if (mTask != null) {
                    mPendingSurfaceChanged = true;
                }
            } else {
                surfaceChangedImpl();
                mPendingSurfaceChanged = false;
            }
        }

        @SuppressWarnings("deprecation")
        private void surfaceChangedImpl() {
            int format = mSurfaceFormat;
            int width = mSurfaceWidth;
            int height = mSurfaceHeight;
            Camera camera = mCamera;
            camera.stopPreview();
            Camera.Parameters params = camera.getParameters();
            if (DEBUG_LOGD) {
                Log.d(LOG_TAG, Log.buf().append("Original Camera.Parameters: ")
                        .append(params.flatten()).toString());
            }

            APILevelWrapper api = APILevelWrapper.createInstance();
            List<Integer> formats = api.getSupportedPreviewFormats(params);
            if (formats == null || formats.size() == 0) {
                // XXX サポートするフォーマット情報が取れないときはとりあえず決め打ち
                params.setPreviewFormat(PixelFormat.JPEG);
            } else {
                if (formats.contains(format)) {
                    params.setPreviewFormat(format);
                }
            }

            int previewWidth;
            int previewHeight;
            List<Camera.Size> sizes = api.getSupportedPreviewSizes(params);
            if (sizes == null || sizes.size() == 0) {
                // Surfaceのサイズそのまま使用
                previewWidth = width;
                previewHeight = height;
            } else {
                if (DEBUG_LOGD) {
                    StringBuilder buf = Log.buf().append("SupportedPreviewSizes:");
                    for (Camera.Size s : sizes) {
                        buf.append(' ').append(s.width).append('x').append(s.height);
                    }
                    Log.d(LOG_TAG, buf.toString());
                }
                Camera.Size useSize = sizes.get(0);
                boolean overSurface = false;
                if ((useSize.width >= width && useSize.height >= height)
                        || (useSize.width >= height && useSize.height >= width)) {
                    overSurface = true;
                }
                final int len = sizes.size();
                for (int i = 1; i < len; ++i) {
                    Camera.Size s = sizes.get(i);
                    if (overSurface) {
                        if (s.width <= useSize.width && s.height <= useSize.height) {
                            if ((s.width >= width && s.height >= height)
                                    || (s.width >= height && s.height >= width)) {
                                useSize = s;
                            }
                        }
                    } else {
                        if (s.width >= useSize.width && s.height >= useSize.height) {
                            useSize = s;
                            if ((useSize.width >= width && useSize.height >= height)
                                    || (useSize.width >= height && useSize.height >= width)) {
                                overSurface = true;
                            }
                        }
                    }
                }
                previewWidth = useSize.width;
                previewHeight = useSize.height;
            }

            int degrees = getPreviewDegrees();
            if ((degrees == 90 || degrees == 270)
                    && Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
                if (previewWidth > previewHeight) {
                    int temp = previewWidth;
                    previewWidth = previewHeight;
                    previewHeight = temp;
                }
            }

            if (DEBUG_LOGD) {
                Log.d(LOG_TAG, Log.buf().append("Use Preview Size: ")
                        .append(previewWidth).append('x').append(previewHeight)
                        .toString());
            }
            api.setPreviewSize(params, previewWidth, previewHeight);
            try {
                camera.setParameters(params);
            } catch (RuntimeException e) {
                Log.w(LOG_TAG, "Camera setParameters failed", e);
                Handler handler = mHandler;
                if (handler != null) {
                    handler.sendEmptyMessage(MSG_ID_CAMERA_SET_PARAMETERS_FAILED);
                }
                return;
            }

            api.setDisplayOrientation(camera, degrees);
            if (mButtonCameraOnOff.isChecked()) {
                camera.startPreview();
            }
        }

        @SuppressWarnings("deprecation")
        public int getPreviewDegrees() {
            APILevelWrapper api = APILevelWrapper.createInstance();
            APILevelWrapper.CameraInfo cameraInfo = new APILevelWrapper.CameraInfo();
            api.getCameraInfo(0, cameraInfo);
            int rotation = getWindowManager().getDefaultDisplay().getOrientation();
            int degrees = 0;
            switch (rotation) {
                case Surface.ROTATION_0:
                    degrees = 0;
                    break;
                case Surface.ROTATION_90:
                    degrees = 90;
                    break;
                case Surface.ROTATION_180:
                    degrees = 180;
                    break;
                case Surface.ROTATION_270:
                    degrees = 270;
                    break;
            }
            int result;
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                result = (cameraInfo.orientation + degrees) % 360;
                result = (360 - result) % 360;
            } else {
                result = (cameraInfo.orientation - degrees + 360) % 360;
            }
            return result;
        }

        public void startPreview() {
            if (mCamera == null) {
                if (mTask != null) {
                    mPendingPreview = true;
                }
            } else {
                if (mHolder == null) {
                    mPendingPreview = true;
                } else {
                    mCamera.startPreview();
                    mPendingPreview = false;
                }
            }
        }

        public void stopPreview() {
            if (mCamera != null) {
                mCamera.stopPreview();
            }
            mPendingPreview = false;
        }

        private void setPreviewDisplay() {
            try {
                mCamera.setPreviewDisplay(mHolder);
            } catch (IOException e) {
                Log.w(LOG_TAG, "Set Surface on Camera failed", e);
                Handler handler = mHandler;
                if (handler != null) {
                    handler.sendEmptyMessage(MSG_ID_CAMERA_SET_SURFACE_FAILED);
                }
            }
        }

        private class OpenCameraTask extends AsyncTask<Void, Void, Camera> {
            @Override
            protected Camera doInBackground(Void... params) {
                try {
                    return Camera.open();
                } catch (RuntimeException e) {
                    // カメラオープン失敗（カメラが別プロセスで使用中、カメラが無効化されているなど）
                    Log.w(LOG_TAG, "Camera open failed", e);
                    return null;
                }
            }

            @Override
            protected void onPostExecute(Camera result) {
                super.onPostExecute(result);
                if (result == null) {
                    Handler handler = mHandler;
                    if (handler != null) {
                        handler.sendEmptyMessage(MSG_ID_CAMERA_OPEN_FAILED);
                    }
                    return;
                }
                if (mTask == null) {
                    // キャンセル
                    result.release();
                } else {
                    mCamera = result;
                    if (mHolder != null) {
                        setPreviewDisplay();
                        if (mPendingSurfaceChanged) {
                            surfaceChangedImpl();
                            mPendingSurfaceChanged = false;
                        }
                        if (mPendingPreview) {
                            mCamera.startPreview();
                            mPendingPreview = false;
                        }
                    }
                    mTask = null;
                }
            }
        }
    }
}
