package jp.agentec.abook.abv.ui.viewer.view;

import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import jp.agentec.abook.abv.bl.common.ABVEnvironment;
import jp.agentec.abook.abv.bl.common.log.Logger;
import jp.agentec.abook.abv.bl.websocket.MeetingManager;
import jp.agentec.abook.abv.cl.util.ContentLogUtil;
import jp.agentec.abook.abv.cl.util.PreferenceUtil;
import jp.agentec.abook.abv.launcher.android.R;
import jp.agentec.abook.abv.ui.common.appinfo.AppDefType.DefPrefKey;
import jp.agentec.abook.abv.ui.common.util.ABVToastUtil;
import jp.agentec.abook.abv.ui.common.util.DisplayUtil;
import jp.agentec.abook.abv.ui.viewer.activity.VideoViewActivity;
import jp.agentec.adf.util.DateTimeUtil;
import jp.agentec.adf.util.FileUtil;
import jp.agentec.adf.util.RuntimeUtil;
import jp.agentec.adf.util.StringUtil;

import org.json.adf.JSONObject;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.Point;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;

public class FullVideoView extends RelativeLayout {
	private static final String TAG = "FullVideoView";
	private static final int FW_BW_MSEC = 10000;
	
	private VideoView mVideoView;
	private RelativeLayout controlLayout;
	private ImageButton mBtnPlay;
	private ImageButton imgBtnBack;
	private SeekBar mVideoSeekBar;
	private TextView txtCurrentTime;
	
	private ScheduledExecutorService mExecutor;
	private Handler mHandler = new Handler();
	private MeetingManager meetingManager = MeetingManager.getInstance();

	private Long contentId;
	private Integer pageNumber;
	private Long mObjectId;
	private String mVideofile;
	private boolean isMobile;
	
	private int duration;
	private int mCurrentPosition;
	private boolean visible = false;
	private boolean repeatable;
	private int errorCount;

	private int objectLogId =-1;
	private int actionTime = 0;

	private OnCloseListener listener;

	public interface OnCloseListener {
		void onClose();
	}

	public void setOnCloseListener(OnCloseListener listener) {
		this.listener = listener;
	}

	public FullVideoView(Context context) {
		super(context);
	}
	
	public FullVideoView(Context context, String videofile, String contentName, Long contentId, Integer pageNumber, Long objectId, boolean isMobile, int objectLogId) {
		super(context);
		mVideofile = videofile;
		this.contentId = contentId;
		this.pageNumber = pageNumber;
		this.mObjectId = objectId;
		this.isMobile = isMobile;
		this.objectLogId = objectLogId;

		// VideoViewレイアウト
		mVideoView = new VideoView(context);
		RelativeLayout videoLayout = new RelativeLayout(context);
		videoLayout.setBackgroundColor(Color.BLACK);
		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
		params.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.CENTER_IN_PARENT);
		videoLayout.addView(mVideoView, params);
		addView(videoLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
		
		setOnTouchListener(new OnTouchListener() {
			@Override
			public boolean onTouch(View view, MotionEvent ev) {
				Logger.e(TAG, "onTouch:" + ev);
				return true; // do nothing 他のイベントを受け付けない
			}
			
		});

		// コントロールレイアウト
		LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		
    	controlLayout = (RelativeLayout)layoutInflater.inflate(R.layout.videoview, null);

    	if (StringUtil.isNullOrEmpty(contentName)) { // オーサリング動画の場合
    		LinearLayout linearLayout1 = (LinearLayout)controlLayout.findViewById(R.id.linear1);
    		linearLayout1.setVisibility(View.GONE);
    	}
    	else { // 動画コンテンツの場合
        	TextView txtTitle = (TextView)controlLayout.findViewById(R.id.txtTitle);
        	txtTitle.setText(contentName);
        	
        	TextView txtFileSize = (TextView)controlLayout.findViewById(R.id.txtFileSize);
            txtFileSize.setText(FileUtil.formatByte2(FileUtil.getFileSize(videofile)));
    	}

        txtCurrentTime = (TextView)controlLayout.findViewById(R.id.txtCurrentTime);
        txtCurrentTime.setText("00:00:00");

		addView(controlLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));

		repeatable = PreferenceUtil.get(getContext(), DefPrefKey.REPEATABLE_PLAY, getContext().getResources().getBoolean(R.bool.repeat_default));

		// VideoView設定
		Logger.d(TAG, "setVideoPath: " + mVideofile);
		changePermission(mVideofile);
		mVideoView.setVideoPath(mVideofile);
		mVideoView.requestFocus();
		mVideoView.start();
		//***** 動画の再生エラー
		mVideoView.setOnErrorListener(new OnErrorListener() {
			@Override
			public boolean onError(MediaPlayer mp, int arg1, int arg2) {
				Logger.e(TAG, "Error VideoPath: " + mVideofile + " arg1=" + arg1 + " arg2=" + arg2);
				errorCount++;
				if (errorCount > 10) { // error -38が続く場合、いったんリリースする。
					mp.release();
					ABVToastUtil.showMakeText(getContext(), getContext().getString(R.string.MOVIE_FATAL_ERROR), Toast.LENGTH_LONG);
					errorCount = 0;
				}
				return false;
			}
		});
		//***** ビデオの準備が整ったときはOnPreparedイベントが発生
		mVideoView.setOnPreparedListener(new OnPreparedListener() {
			@Override
            public void onPrepared(MediaPlayer mp) {
				try {
					errorCount = 0;
					mp.setLooping(repeatable);
					duration = mVideoView.getDuration();
					mVideoSeekBar.setMax(duration); //시크바 최대값 설정
			        TextView txtTotalTime = (TextView)controlLayout.findViewById(R.id.txtTotalTime);
			        txtTotalTime.setText(DateTimeUtil.toTimeFormat(duration));
					initVideoStart();
				} catch (Exception e) {
					Logger.e(TAG, "Video Start Error: " + e.getMessage());
					ABVToastUtil.showMakeText(getContext(), "Video Start Error", Toast.LENGTH_LONG);
				}
			}
		});
		//***** ビデオの再生が完了したらOnCompletionイベントが発生
		mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
			@Override
			public void onCompletion(MediaPlayer mp) {
				if (!repeatable && !meetingManager.isSubscribed()) {
					// 動画コンテンツは閉じない 
                    if (mObjectId == -1) {
                        mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_play));
                    } else {
                        close();
                    }
				}
			}
		});
		// タッチでコントトールの表示・非表示を切り替える
		setOnTouchListener(new OnTouchListener() {
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				Logger.d(TAG, "mVideoView.onTouch:" + event);
				if (event.getAction() == MotionEvent.ACTION_DOWN) {
					visible = !visible;
					setControlVisible();
					return true;
				}
				return true;
			}
		});

		// シークバー
		mVideoSeekBar = (SeekBar)controlLayout.findViewById(R.id.mVideoSeekBar);
		mCurrentPosition = 0;
		mVideoSeekBar.setProgress(mCurrentPosition);//시크바 초기화 설정
		mVideoSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
			@Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
				if (fromUser) {   //ユーザ操作の場合
					jump(progress);
				}
			}

			@Override
            public void onStopTrackingTouch(SeekBar seekBar) {
			}

			@Override
            public void onStartTrackingTouch(SeekBar seekBar) {
			}
		});

		// 再生・一時停止ボタン
		mBtnPlay = (ImageButton)controlLayout.findViewById(R.id.btnPlay);
		mBtnPlay.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				if (mVideoView.isPlaying()) {
					mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_play));
					pauseVideo();
				}
				else{
					mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_pause));
					restartVideo();
				}
			}
		});
		
		// 戻りボタン
		final ImageButton btnBackward = (ImageButton)controlLayout.findViewById(R.id.btnBackward);
        btnBackward.setOnTouchListener(new OnTouchListener() {
			
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				Logger.v(TAG, "btnBackward onTouch");
				if (event.getAction() == MotionEvent.ACTION_DOWN) {
					btnBackward.setImageDrawable(getResources().getDrawable(R.drawable.btn_backward_over));
					int position = mCurrentPosition - FW_BW_MSEC; // 10秒前に
					if (position < 0) {
						position = 0;
					}
					jump(position);
				}
				else {
					btnBackward.setImageDrawable(getResources().getDrawable(R.drawable.btn_backward));
				}
				return false;
			}
		});
        
        // 進むボタン
        final ImageButton btnForward = (ImageButton)controlLayout.findViewById(R.id.btnForward);
        btnForward.setOnTouchListener(new OnTouchListener() {
			
			@Override
			public boolean onTouch(View v, MotionEvent event) {
				Logger.v(TAG, "btnForward onTouch");
				if (event.getAction() == MotionEvent.ACTION_DOWN) {
					btnForward.setImageDrawable(getResources().getDrawable(R.drawable.btn_forward_over));
					int position = mCurrentPosition + FW_BW_MSEC; // 10秒後に
					if (position > duration) {
						position = duration;
					}
					jump(position);
				}
				else {
					btnForward.setImageDrawable(getResources().getDrawable(R.drawable.btn_forward));
				}
				return false;
			}
		});
		

		// 閉じるボタン
		imgBtnBack = (ImageButton) controlLayout.findViewById(R.id.imgBtnBack);
		imgBtnBack.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				close();
			}
		});

		// コントロールを非表示にする
		setControlVisible();
	}

	private void changePermission(String path) {
        if (ABVEnvironment.getInstance().isContentProtected) {
            try {
                // MediaPlayerで/data/dataのファイルを読み込めないので一時的に読み取り可に変更する
                RuntimeUtil.exec("chmod 754 " + path);
            } catch (IOException e) {
                Logger.e(TAG, "chmod error. " + path, e);
            }
        } else { // 非暗号化の場合： MediaPlayerを直接使う埋め込み動画だと問題ないが、VideoViewだとバーミッションを変えないとerror (1, -2147483648)が発生
            try {
                RuntimeUtil.exec("chmod 604 " + path);
                String target = path;
                for (int i = 0; i < 4; i++) { // files/ABook/contents/コンテンツID/resources, files/ABook/contents/コンテンツID, files/ABook/contents, files/ABookまでパーミッション変更
                    target = FileUtil.getParentPath(target);
                    RuntimeUtil.exec("chmod 701 " + target); // ディレクトリ実行までのパーミッションでよい
                }
            } catch (IOException e) {
                Logger.e(TAG, "chmod error. " + path, e);
            }
        }
	}
	
	/**
	 * 縦横が変わった場合に、シークバーの幅を変更する。
	 * 
	 */
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		
		Point point = DisplayUtil.getDisplaySize(getContext());
		int displayWidth = point.x;
		int displayHeight = point.y;
        float density = getResources().getDisplayMetrics().density;
		Logger.v(TAG, "displayWidth=%s displayHeight=%s density=%s", displayWidth, displayHeight, density);

        Configuration config = getContext().getResources().getConfiguration();
        LinearLayout linearLayout = (LinearLayout)controlLayout.findViewById(R.id.linearSeek);
        LinearLayout.LayoutParams params;
        if (config.orientation == Configuration.ORIENTATION_LANDSCAPE && !isMobile) {
        	params = new LinearLayout.LayoutParams((int) (density * 500), ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        else {
        	params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        
        params.setMargins(20, (int)(density * 28), (int)(density * 10), 0);
        linearLayout.setLayoutParams(params);
        
        ImageButton btnBackward = (ImageButton)controlLayout.findViewById(R.id.btnBackward);
        ImageButton btnForward = (ImageButton)controlLayout.findViewById(R.id.btnForward);
        if (isMobile && displayWidth < displayHeight) { // モバイルで縦長の場合、戻り・早送りはなし
        	btnBackward.setVisibility(View.GONE);
        	btnForward.setVisibility(View.GONE);
        }
        else {
        	btnBackward.setVisibility(View.VISIBLE);
        	btnForward.setVisibility(View.VISIBLE);
        }
        
        postInvalidate();
	}

	/**
	 * コントロールの表示切替をする
	 * 
	 * @param visible
	 */
	public void setControlVisible(boolean visible) {
		this.visible = visible;
		setControlVisible();
	}
	
	/**
	 * コントロールの表示切替をする
	 */
	private void setControlVisible() {
		int visibility = visible? View.VISIBLE: View.INVISIBLE;
		controlLayout.setVisibility(visibility);
	}

	@Override
    public boolean onKeyUp(int keyCode, KeyEvent event) { // TODO later これが呼ばれることはない。Activityが拾ってしまう。
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			if (getContext() instanceof VideoViewActivity) {
				Logger.d(TAG, "onKeyUp");
				return super.onKeyUp(keyCode, event);
			} else {
				close();
			}
		}
		return true;
	}

	public void close() {
		stop();
		
        if (mObjectId != -1 && meetingManager.isSendable()) {
        	meetingManager.sendWs(MeetingManager.CMD_CLOSEPOPUP, contentId, pageNumber, mObjectId, null);
        }

		ViewGroup parent = (ViewGroup)getParent();  
		if (parent != null) {
			parent.removeView(this);
		}
		
		if (getContext() instanceof VideoViewActivity) {
			((VideoViewActivity)getContext()).finish();
		}
		
		try {
			RuntimeUtil.exec("chmod 750 " + mVideofile);
		} catch (IOException e) {
			Logger.e(TAG, "chmod error. " + mVideofile, e);
		}

		if (listener != null) {
			listener.onClose();
		}
	}

	//remoteEvent Bug Fixed 20130205 by jeonghun(VideoActivty controller 수정 및 조작연동 가능 수정)
	private void initVideoStart() {
		mVideoView.start();
		mExecutor = Executors.newSingleThreadScheduledExecutor();
		mExecutor.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				mHandler.post(new Runnable() {
					@Override
					public void run() {
						try {
							if((mVideoView.getCurrentPosition()  - mCurrentPosition) > 0){
								actionTime += (mVideoView.getCurrentPosition()  - mCurrentPosition);
							}
							mCurrentPosition = mVideoView.getCurrentPosition();
						}catch(Exception e) { // ignore
						}
						mVideoSeekBar.setProgress(mCurrentPosition);
						txtCurrentTime.setText(DateTimeUtil.toTimeFormat(mCurrentPosition));
					}
				});
			}
		} , 200 , 200 , TimeUnit.MILLISECONDS);
	}
	
	/**
	 * ビデオを一時停止する。
	 * 
	 */
	public void pauseVideo() {
        if (meetingManager.isSendable()) {
        	JSONObject json = new JSONObject();
			json.put(MeetingManager.PLAY_TIME, mCurrentPosition / 1000);
			json.put(MeetingManager.TYPE, MeetingManager.PAUSE);
        	meetingManager.sendWs(MeetingManager.CMD_MOVIEACTION, contentId, pageNumber, mObjectId, json);
        }

		if (mExecutor != null) {
			boolean logUpdateFlg = mExecutor.isShutdown();
			mExecutor.shutdownNow();
			if (!logUpdateFlg) {
				ContentLogUtil.getInstance().updateActionTime(contentId, objectLogId, actionTime);
			}
		}

		mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_play));
		mVideoView.pause();
	}

	/**
	 * ビデオを再生する。
	 */
	public void restartVideo() {
        if (meetingManager.isSendable()) {
        	JSONObject json = new JSONObject();
			json.put(MeetingManager.TYPE, MeetingManager.START);
			json.put(MeetingManager.PLAY_TIME, mCurrentPosition / 1000);
        	meetingManager.sendWs(MeetingManager.CMD_MOVIEACTION, contentId, pageNumber, mObjectId, json);
        }

		mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_pause));
		initVideoStart();
	}
	
	/**
	 * ビデオおよびExecutorを停止する。
	 * 
	 */
	public void stop() {
		if (mExecutor != null) {
			boolean logUpdateFlg = mExecutor.isShutdown();
			mExecutor.shutdownNow();
			if(!logUpdateFlg){
        		ContentLogUtil.getInstance().updateActionTime(contentId, objectLogId, actionTime);
        	}
		}
		
		if (mVideoView != null) {
			mVideoView.stopPlayback();
		}
	}

	/**
	 * 指定位置にジャンプする
	 * 
	 * @param position　ミリ秒
	 */
	public void jump(int position) {
		mCurrentPosition = position;
		mVideoView.seekTo(mCurrentPosition);
		mVideoSeekBar.setProgress(mCurrentPosition);
		
        if (meetingManager.isSendable()) {
        	JSONObject json = new JSONObject();
			json.put(MeetingManager.TYPE, mVideoView.isPlaying()? MeetingManager.START: MeetingManager.PAUSE);
			json.put(MeetingManager.PLAY_TIME, position / 1000);
        	meetingManager.sendWs(MeetingManager.CMD_MOVIEACTION, contentId, pageNumber, mObjectId, json);
        }
	}

	public Integer getDuration() {
		return duration;
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		return true;
	}
}
