package jp.sourceforge.nicoro;

import static jp.sourceforge.nicoro.Log.LOG_TAG;
import jp.sourceforge.nicoro.R;
import android.content.Intent;
import android.graphics.Canvas;
import android.os.Bundle;
import android.os.Debug;
import android.os.Message;
import android.os.SystemClock;
import android.view.SurfaceView;

public class NicoroFFmpegPlayer extends AbstractNicoroPlayer
        implements SurfaceVideoDrawer.DrawMessage {
	private static final boolean DEBUG_LOGV = Release.IS_DEBUG && false;
	private static final boolean DEBUG_LOGD = Release.IS_DEBUG && true;
	private static final boolean DEBUG_TRACE = Release.IS_DEBUG && false;

	private static final int MSG_ID_VIDEO_CACHED = MSG_ID_SUB_OFFSET + 0;
	private static final int MSG_ID_SURFACE_READY = MSG_ID_SUB_OFFSET + 1;
	private static final int MSG_ID_SURFACE_DESTROYED = MSG_ID_SUB_OFFSET + 2;
	private static final int MSG_ID_VIDEO_DOWNLOAD_FINISHED = MSG_ID_SUB_OFFSET + 3;

	private SurfaceVideoDrawer mSurfaceVideoDrawer;
	/*private*/ volatile boolean mIsFinish;
	/*private*/ volatile boolean mIsDecodedComplete;
	/*private*/ StreamAudioPlayer mStreamAudioPlayer = new StreamAudioPlayer();

	private boolean mIsSurfaceOk;
	private boolean mIsVideoCachedOk;
    private boolean mIsVideoCachedFinished;

    private FFmpegVideoDecoder mFFmpegVideoDecoder;

	private Rational mRationalDebugLog = (DEBUG_LOGV || DEBUG_LOGD) ? new Rational() : null;

    private volatile boolean mIsPlaying = false;

    private volatile boolean mIsPause = false;

    public NicoroFFmpegPlayer() {
    	mHandler = new MessageHandler() {
    		@Override
    		public void handleMessage(Message msg) {
    			if (mHandler == null) {
    				if (DEBUG_LOGD) {
    					Log.d(LOG_TAG, Log.buf().append("Activity was destroyed. ignore message=")
    							.append(msg.toString()).toString());
    				}
    				return;
    			}
    			switch (msg.what) {
    			case MSG_ID_VIDEO_CACHED:
    			    try {
        				mIsVideoCachedOk = true;
        				// 動画データを元にSurface準備
        				if (!mFFmpegVideoDecoder.prepareFFmpeg(true)) {
        		            // 動画読み込み完了後にもう１回チャレンジ
        				    mIsVideoCachedOk = false;
        				}
    			    } catch (IllegalArgumentException e) {
    			        String error = e.toString();
    			        Log.e(LOG_TAG, error, e);
                        if (mHandler != null) {
                            mHandler.obtainMessage(MSG_ID_PLAY_ERROR,
                                    error).sendToTarget();
                        }
    			    }
    				break;
    			case MSG_ID_SURFACE_READY:
    			    assert mIsVideoCachedOk;
    				mIsSurfaceOk = true;
    				if (!mIsPlaying && canStartPlay()) {
    					startPlay();
    				}
    				break;
    			case MSG_ID_SURFACE_DESTROYED:
    				mIsSurfaceOk = false;
    				break;
    			case MSG_ID_VIDEO_DOWNLOAD_FINISHED:
					mSeekBar.setSecondaryProgress(mSeekBar.getMax());
                    mIsVideoCachedFinished = true;
					if (!mIsVideoCachedOk) {
	                    // 動画データを元にSurface準備
	                    if (!mFFmpegVideoDecoder.prepareFFmpeg(true)) {
	                        // 読み込み完了後で駄目ならエラー
	                        if (mHandler != null) {
	                            mHandler.obtainMessage(MSG_ID_PLAY_ERROR,
	                                    getString(R.string.errormessage_ffmpeg_prepare))
	                                    .sendToTarget();
	                        }
	                    }
					}
    				break;
    			default:
    				super.handleMessage(msg);
    				break;
    			}
    		}
    	};
    }

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

//    	VMRuntime.getRuntime().setMinimumHeapSize(Long.MAX_VALUE);

        Intent intent = getIntent();
        mVideoLoader = createVideoLoader(intent, getApplicationContext());
        if (mVideoLoader != null) {
        	mVideoLoader.setEventListener(new VideoLoader.EventListener() {
        		@Override
        		public void onStarted(VideoLoader streamLoader) {
        			// 何もしない
        		}
				@Override
				public void onCached(VideoLoader videoLoader) {
					if (mHandler != null) {
						mHandler.sendEmptyMessage(MSG_ID_VIDEO_CACHED);
					}
				}
				@Override
				public void onFinished(VideoLoader videoLoader) {
					if (mHandler != null) {
						mHandler.sendEmptyMessage(MSG_ID_VIDEO_DOWNLOAD_FINISHED);
					}
				}
				@Override
				public void onOccurredError(VideoLoader videoLoader, String errorMessage) {
					if (mHandler != null) {
						mHandler.obtainMessage(MSG_ID_VIDEO_OCCURRED_ERROR,
								errorMessage).sendToTarget();
					}
				}
				@Override
				public void onNotifyProgress(int num, int den) {
					if (mHandler != null) {
//						if (!mIsPlaying) {
							mHandler.removeMessages(MSG_ID_VIDEO_NOTIFY_PROGRESS);
							mHandler.obtainMessage(MSG_ID_VIDEO_NOTIFY_PROGRESS,
									num, den).sendToTarget();
//						}
					}
				}
        	});
        	mVideoLoader.startLoad();
        }

        mIsSurfaceOk = false;
        mIsVideoCachedOk = false;
        mIsVideoCachedFinished = false;

        setContentView(R.layout.nicoro_ffmpegplayer);
        mSurfaceVideoDrawer = new SurfaceVideoDrawer(
                (SurfaceView) findViewById(R.id.surface));
        mSurfaceVideoDrawer.registerEventHandler(mHandler,
                MSG_ID_SURFACE_READY, MSG_ID_SURFACE_DESTROYED);
        mSurfaceVideoDrawer.setDrawMessage(this);

        mFFmpegVideoDecoder = new FFmpegVideoDecoder(mSurfaceVideoDrawer,
                mStreamAudioPlayer, mVideoLoader, this);

		initializeView();

		mIsPlaying = false;
		mIsPause = false;
    }

    @Override
    protected void onDestroy() {
        if (DEBUG_TRACE) {
            Debug.stopMethodTracing();
        }

        mIsFinish = true;
        mStreamAudioPlayer.finish();
        mSurfaceVideoDrawer.quit();
        mFFmpegVideoDecoder.quit();
        super.onDestroy();
        mStreamAudioPlayer = null;
        mSurfaceVideoDrawer = null;
        mFFmpegVideoDecoder = null;

        System.gc();
        System.gc();
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, "NicoroFFmpegPlayer#finalize start");
            }
            super.finalize();
        } finally {
            mIsFinish = true;
            mStreamAudioPlayer.finish();
            mSurfaceVideoDrawer.quit();
            mFFmpegVideoDecoder.quit();
            if (DEBUG_LOGV) {
                Log.v(LOG_TAG, "NicoroFFmpegPlayer#finalize end");
            }
        }
    }

	@Override
	protected boolean canStartPlay() {
		return (mOnResumed && mIsSurfaceOk
		        && (mIsVideoCachedOk || mIsVideoCachedFinished)
				&& mMessageData.mIsMessageOk && mMessageDataFork.mIsMessageOk);
	}

	@Override
    protected void startPlay() {
        System.gc();
        System.gc();
        SystemClock.sleep(500L);

	    super.startPlay();

    	mIsPlaying = true;
    	mIsPause = false;
		mIsFinish = false;
		mStreamAudioPlayer.prepareStart();
		mIsDecodedComplete = false;
//        mFFmpegVideoDecoder.prepareFFmpeg();
		mFFmpegVideoDecoder.prepareDecode();
		mFFmpegVideoDecoder.start();

		mHandler.sendEmptyMessage(MSG_ID_INFO_PLAY_DATA_UPDATE);
		postStartPlayIfIsRestored();

		if (DEBUG_TRACE) {
		    Debug.startMethodTracing(getClass().getSimpleName());
		}
    }

	@Override
	protected StringBuilder appendCurrentPlayTime(StringBuilder builder) {
		if (DEBUG_LOGV) {
			Rational r = mRationalDebugLog;
			getCurrentPositionVideoPlay(r);
			if (r.den == 0) { r.den = 1; }
			Log.v(LOG_TAG, Log.buf().append("VideoPlayTime: ")
					.append(r.num).append("/").append(r.den).append(" ")
					.append(r.getDivideFloat()).toString());
			getCurrentPositionAudioPlay(r);
			if (r.den == 0) { r.den = 1; }
			Log.v(LOG_TAG, Log.buf().append("AudioPlayTime: ")
					.append(r.num).append("/").append(r.den).append(" ")
					.append(r.getDivideFloat()).toString());
			getCurrentPositionVideoDecode(r);
			if (r.den == 0) { r.den = 1; }
			Log.v(LOG_TAG, Log.buf().append("VideoDecodeTime: ")
					.append(r.num).append("/").append(r.den).append(" ")
					.append(r.getDivideFloat()).toString());
			getCurrentPositionAudioDecode(r);
			if (r.den == 0) { r.den = 1; }
			Log.v(LOG_TAG, Log.buf().append("AudioDecodeTime: ")
					.append(r.num).append("/").append(r.den).append(" ")
					.append(r.getDivideFloat()).toString());
		}

		getCurrentPositionVideoPlay(mRatinalCurrentPlayTime);
		final int posNum = mRatinalCurrentPlayTime.num;
		final int posDen = mRatinalCurrentPlayTime.den;
		return appendCurrentPlayTimeCommon(builder, posNum, posDen);
	}

	@Override
	protected void getCurrentPositionVideoPlay(Rational rational) {
        if (mIsFinish) {
            return;
        }
	    mSurfaceVideoDrawer.getCurrentPosition(rational);
	}
	@Override
	protected void getCurrentPositionAudioPlay(Rational rational) {
        if (mIsFinish) {
            return;
        }
        // TODO シークすると破綻
		mStreamAudioPlayer.getCurrentPosition(rational);
	}
	@Override
	protected void getCurrentPositionVideoDecode(Rational rational) {
        if (mIsFinish) {
            return;
        }
        mFFmpegVideoDecoder.getCurrentPositionVideoDecode(rational);
	}
	@Override
	protected void getCurrentPositionAudioDecode(Rational rational) {
        if (mIsFinish) {
            return;
        }
//	    // TODO シークすると破綻
	    mFFmpegVideoDecoder.getCurrentPositionAudioDecode(rational);
	}

	@Override
	protected boolean switchPausePlay() {
        if (mIsFinish) {
            return false;
        }
		if (mStreamAudioPlayer.isInDummyDataAtStart()) {
			return false;
		}

		if (!mIsPause) {
			pausePlay();
		} else {
			restartPlay();
		}
		setButtonPauseImage();
		return true;
	}

	@Override
	protected void pausePlay() {
        if (mIsFinish) {
            return;
        }
//		mIconPause.setVisibility(View.VISIBLE);
		if (mStreamAudioPlayer.isInDummyDataAtStart()
		        /*|| mStreamAudioPlayer.isInDummyDataAtSeek()*/) {
			mStreamAudioPlayer.reservePause();
		} else {
			mStreamAudioPlayer.pause();
		}
		mSurfaceVideoDrawer.pause();
		mFFmpegVideoDecoder.pause();
		mIsPause = true;
	}

	@Override
	protected void restartPlay() {
        if (mIsFinish) {
            return;
        }
//		mIconPause.setVisibility(View.INVISIBLE);
		mStreamAudioPlayer.restart();
		mSurfaceVideoDrawer.restart();
        mFFmpegVideoDecoder.restart();
		mIsPause = false;
	}

	@Override
	protected boolean isPausePlay() {
        if (mIsFinish) {
            // 都合上true返す
            return true;
        }
		if (mStreamAudioPlayer.isInDummyDataAtStart()) {
			// 都合上true返す
			return true;
		}
		return mIsPause;
	}

	@Override
	protected void seekBySecond(int second) {
        if (mIsFinish) {
            return;
        }
		mFFmpegVideoDecoder.seekBySecond(second);
	}

	@Override
	protected StringBuilder appendVideoResolution(StringBuilder builder) {
        if (mIsFinish) {
            return builder;
        }
        return builder.append(mFFmpegVideoDecoder.getVideoOriginalWidth())
            .append('×')
            .append(mFFmpegVideoDecoder.getVideoOriginalHeight());
	}

	@Override
	protected StringBuilder appendPlayerInfo(StringBuilder builder) {
		return builder.append(getString(R.string.info_play_data_ffmpeg));
	}

    @Override
    public void drawMessage(Canvas canvas, int vpos) {
        canvas.setMatrix(null);
        mMessageChatController.drawMessage(canvas,
                mMessageData,
                mMessageDataFork,
                vpos,
                canvas.getWidth(), canvas.getHeight(),
                mMessageDisable);
    }

    public void enableSeekBar() {
        if (mHandler != null) {
            mHandler.sendEmptyMessage(MSG_ID_ENABLE_SEEK_BAR);
        }
    }
}