package com.theta.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.io.IOException;

import jp.agentec.abook.abv.bl.common.log.Logger;

/**
 * Motion JPEG view
 */
public class MJpegView extends SurfaceView implements SurfaceHolder.Callback {
    private static final String TAG = "MJpegView";
    private MJpegViewThread mMJpegViewThread = null;
    private MJpegInputStream mMJpegInputStream = null;
    private boolean existSurface = false;
    private int mDisplayWidth;
    private int mDisplayHeight;

    private MJpegView.LiveCameraListener mListener;

    public interface LiveCameraListener {
        void liveCameraPlayFail();
    }

    public void setLiveCameraListenerListener(MJpegView.LiveCameraListener listener) {
        this.mListener = listener;
    }
    /**
     * Constructor
     * @param context
     */
    public MJpegView(Context context) {
        super(context);
        init();
    }

    /**
     * Constructor
     * @param context
     * @param attrs
     */
    public MJpegView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    /**
     * Constructor
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public MJpegView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    /**
     * Initialization process
     */
    private void init() {
        SurfaceHolder holder = getHolder();
        holder.addCallback(this);
        setFocusable(true);
        mDisplayWidth = getWidth();
        mDisplayHeight = getHeight();
    }

    /**
     * Start playback
     */
    public void play() {
        if (mMJpegViewThread != null) {
            stopPlay();
        }

        if(mMJpegInputStream != null) {
            if (mMJpegViewThread != null) {
                if (mMJpegViewThread.getState() == Thread.State.NEW) {
                    mMJpegViewThread.start();
                }
            } else {
                mMJpegViewThread = new MJpegViewThread(getHolder());
                mMJpegViewThread.start();
            }
        }
    }

    /**
     * Stop playback
     */
    public void stopPlay() {
        if (mMJpegViewThread != null) {
            mMJpegViewThread.cancel();
            boolean retry = true;
            while (retry) {
                try {
                    mMJpegViewThread.join();
                    retry = false;
                    mMJpegViewThread = null;
                } catch (InterruptedException e) {
                    e.getStackTrace();
                }
            }
        }
    }

    /**
     * Set source stream for receiving motion JPEG
     * @param source Source stream
     */
    public void setSource(MJpegInputStream source) {
        if (source == null) { //mMJpegInputStreamのクローズ作業のため、現在スレッドをストップする。
            stopPlay();
        } else {
            mMJpegInputStream = source;
            play();
        }
    }

    /**
     * Get source stream for receiving motion JPEG
     * @return Source stream
     */
    public MJpegInputStream getSource() {
        return mMJpegInputStream;
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        existSurface = true;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        synchronized(holder) {
            mDisplayWidth = width;
            mDisplayHeight = height;
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        existSurface = false;
        stopPlay();
    }

    /**
     * Thread class for receiving motion JPEG
     */
    private class MJpegViewThread extends Thread {
        private final SurfaceHolder mSurfaceHolder;
        private boolean keepRunning = true;

        /**
         * Constructor
         * @param surfaceHolder
         */
        public MJpegViewThread(SurfaceHolder surfaceHolder) {
            mSurfaceHolder = surfaceHolder;
        }

        /**
         * Acquire image size according to display area<p>
         *     Calculates the size that fits the display area while maintaining the aspect ratio of the motion JPEG.
         * @param bitmapWidth Width of motion JPEG
         * @param bitmapHeight Height of motion JPEG
         * @return Image size
         */
        private Rect getImageRect(int bitmapWidth, int bitmapHeight) {
            float bitmapAspectRatio = (float) bitmapWidth / (float) bitmapHeight;
            bitmapWidth = mDisplayWidth;
            bitmapHeight = (int) (mDisplayWidth / bitmapAspectRatio);
            if (bitmapHeight > mDisplayHeight) {
                bitmapHeight = mDisplayHeight;
                bitmapWidth = (int) (mDisplayHeight * bitmapAspectRatio);
            }
            int bitmapX = (mDisplayWidth / 2) - (bitmapWidth / 2);
            int bitmapY = (mDisplayHeight / 2) - (bitmapHeight / 2);
            return new Rect(0, bitmapY, mDisplayWidth, bitmapHeight + bitmapY);
        }

        /**
         * Abort thread
         */
        public void cancel() {
            keepRunning = false;
        }

        @Override
        public void run() {
            Bitmap bitmap;
            Rect bitmapRect;
            Canvas bitmapCanvas = null;
            while (keepRunning) {
                if (existSurface) {
                    try {
                        bitmapCanvas = mSurfaceHolder.lockCanvas();
                        synchronized (mSurfaceHolder) {
                            try {
                                if ((mMJpegInputStream != null) && (bitmapCanvas != null)) {
                                    bitmap = mMJpegInputStream.readMJpegFrame();
                                    bitmapRect = getImageRect(bitmap.getWidth(), bitmap.getHeight());
                                    bitmapCanvas.drawColor(Color.BLACK);
                                    bitmapCanvas.drawBitmap(bitmap, null, bitmapRect, new Paint());
                                    bitmap.recycle();
                                }
                            } catch (IOException e) {
                                e.getStackTrace();
                                Logger.e(TAG, "MJpegView IOException = " + e.toString());
                                keepRunning = false;
                                mListener.liveCameraPlayFail();
                            }
                        }
                    } finally {
                        if (bitmapCanvas != null) {
                            mSurfaceHolder.unlockCanvasAndPost(bitmapCanvas);
                        }
                    }
                }
            }

            bitmapCanvas = mSurfaceHolder.lockCanvas();
            synchronized (mSurfaceHolder) {
                if (bitmapCanvas != null) {
                    bitmapCanvas.drawColor(Color.BLACK);
                }
            }

            if (bitmapCanvas != null) {
                mSurfaceHolder.unlockCanvasAndPost(bitmapCanvas);
            }

            if (mMJpegInputStream != null) {
                try {
                    mMJpegInputStream.close();
                    mMJpegInputStream = null;
                } catch (IOException e) {
                    e.printStackTrace();
                    Logger.e(TAG, "mMJpegInputStream IOException = " + e.toString());
                }
            }
        }
    }
}