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.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.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.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.audiofx.Visualizer;
import android.net.Uri;
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;

public class AudioPlayView extends RelativeLayout {
    private static final String TAG = "AudioPlayView";
	private static final int FW_BW_MSEC = 10000;

    private MediaPlayer mMediaPlayer;
    private Visualizer mVisualizer;

    private RelativeLayout controlLayout;
    private VisualizerView mVisualizerView;
    private ImageButton mBtnPlay;
    private ImageButton imgBtnBack;
    private SeekBar mSeekBar;
	private TextView txtCurrentTime;

    private String mAudiofile;
    protected boolean mPlayErrorFlg;
	private int duration;
	private int mCurrentPosition;

	private ScheduledExecutorService mExecutor;
	private Handler mHandler = new Handler();
	private MeetingManager meetingManager = MeetingManager.getInstance();
	private Long contentId;
	private boolean repeatable;
	private boolean isMobile;

	public AudioPlayView(Context context) {
		super(context);
	}
	
	public AudioPlayView(Context context, String audiofile, String contentName, Long contentId, boolean isMobile) {
		super(context);
		mAudiofile = audiofile;
		this.contentId = contentId;
		this.isMobile = isMobile;

		// コントロールレイアウト
		LayoutInflater layoutInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		controlLayout = (RelativeLayout) layoutInflater.inflate(R.layout.audioview, this);
        
    	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(audiofile)));
    	}

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

        // Create the MediaPlayer
        changePermission(audiofile);
        if (mMediaPlayer == null) {
        	mMediaPlayer = MediaPlayer.create(getContext(), Uri.parse("file://" + audiofile));
        }
        Logger.d(TAG, "MediaPlayer audio session ID: " + mMediaPlayer.getAudioSessionId());
        
		repeatable = PreferenceUtil.get(getContext(), DefPrefKey.REPEATABLE_PLAY, getContext().getResources().getBoolean(R.bool.repeat_default));

        // イコライザービュー（波形を表示するためだけに使用）
        setupVisualizerFxAndUI();
        mVisualizer.setEnabled(true);

        // When the stream ends, we don't need to collect any more data. We don't do this in
        // setupVisualizerFxAndUI because we likely want to have more, non-Visualizer related code
        // in this callback.
        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mediaPlayer) {
                mVisualizer.setEnabled(false);
            }
        });
        mMediaPlayer.start();
		//***** Audioの再生エラー
		mMediaPlayer.setOnErrorListener(new OnErrorListener() {
			@Override
			public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
				mPlayErrorFlg = true;
				Logger.e(TAG, "Error Path: " + mAudiofile);
				return false;
			}
		});
		//***** Audioの準備が整ったときはOnPreparedイベントが発生
		mMediaPlayer.setOnPreparedListener(new OnPreparedListener() {
			@Override
            public void onPrepared(MediaPlayer mp) {
				try {
					duration = mMediaPlayer.getDuration();
					mSeekBar.setMax(duration);
			        TextView txtTotalTime = (TextView)controlLayout.findViewById(R.id.txtTotalTime);
			        txtTotalTime.setText(DateTimeUtil.toTimeFormat(duration));
					initAudioStart();
				} catch (Exception e) {
					Logger.e(TAG, "Audio Start Error: " + e.getMessage());
					ABVToastUtil.showMakeText(getContext(), "Audio Start Error", Toast.LENGTH_LONG);
				}
			}
		});
		//***** Audioの再生が完了したらOnCompletionイベントが発生
		mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
			@Override
			public void onCompletion(MediaPlayer mp) {
				if (mPlayErrorFlg == false && repeatable) {
					try { 
						mp.seekTo(0);
						mp.start();
					} catch (Exception e) {
						Logger.e(TAG, "Audio Start Error: " + e.getMessage());
						ABVToastUtil.showMakeText(getContext(), "Audio Start Error", Toast.LENGTH_LONG);
					}
				} else {
					mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_play));
				}
			}
		});
		// シークバー
		mSeekBar = (SeekBar)findViewById(R.id.mVideoSeekBar);
		mCurrentPosition = 0;
		mSeekBar.setProgress(mCurrentPosition);//시크바 초기화 설정
		mSeekBar.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 (mMediaPlayer.isPlaying()) {
					mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_play));
					pause();
				}
				else{
					mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_pause));
					restart();
				}
			}
		});
		
		// 戻りボタン
		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) findViewById(R.id.imgBtnBack);
		imgBtnBack.setOnClickListener(new View.OnClickListener() {
			@Override
			public void onClick(View v) {
				close();
			}
		});
		
    }

	/**
	 * 縦横が変わった場合に、シークバーの幅を変更する。
	 * 
	 */
	@Override
	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
		super.onSizeChanged(w, h, oldw, oldh);
		
		Point point = DisplayUtil.getDisplaySize(getContext());
        float density = getResources().getDisplayMetrics().density;
		Logger.v(TAG, "displayWidth=%s displayHeight=%s density=%s", point.x, point.y, 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 && point.x < point.y) { // モバイルで縦長の場合、戻り・早送りはなし
        	btnBackward.setVisibility(View.GONE);
        	btnForward.setVisibility(View.GONE);
        }
        else {
        	btnBackward.setVisibility(View.VISIBLE);
        	btnForward.setVisibility(View.VISIBLE);
        }
        
        postInvalidate();
	}

	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 { // 非暗号化の場合： バーミッションを変えないと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
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_BACK) {
			close();
		} else  {
			return super.onKeyUp(keyCode, event);
		}
		return false;
	}

    private void setupVisualizerFxAndUI() {
        mVisualizerView = (VisualizerView) controlLayout.findViewById(R.id.visualizer);
        mVisualizer = new Visualizer(mMediaPlayer.getAudioSessionId());
        mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
        mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
            @Override
            public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {
                mVisualizerView.updateVisualizer(bytes);
            }

            @Override
            public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {
            }
        }, Visualizer.getMaxCaptureRate() / 2, true, false);
    }

	public synchronized void close() {
		stop();
		try {
			// 読み取り不可に戻す
			RuntimeUtil.exec("chmod 750 " + mAudiofile);
		} catch (IOException e) {
			Logger.e(TAG, "chmod error. " + mAudiofile, e);
		}
		((Activity)getContext()).finish();
	}

	private void initAudioStart() {
		mMediaPlayer.start();
		mExecutor = Executors.newSingleThreadScheduledExecutor();
		mExecutor.scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				mHandler.post(new Runnable() {
					@Override
					public void run() {
						try {
							mCurrentPosition = mMediaPlayer.getCurrentPosition();
						} catch(Exception e) { // ignore
						}
						mSeekBar.setProgress(mCurrentPosition);
						txtCurrentTime.setText(DateTimeUtil.toTimeFormat(mCurrentPosition));
					}
				});
			}
		} , 500 , 500 , TimeUnit.MICROSECONDS);
	}
	
	public void pause() {
        if (meetingManager.isSendable()) {
        	JSONObject json = new JSONObject();
			json.put(MeetingManager.TYPE, MeetingManager.PAUSE);
        	meetingManager.sendWs(MeetingManager.CMD_AUDIOACTION, contentId, 0, null, json);
        }

		mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_play));
		if (mMediaPlayer != null) {
			mMediaPlayer.pause();
		}
	}

	public void restart() {
        if (meetingManager.isSendable()) {
        	JSONObject json = new JSONObject();
			json.put(MeetingManager.TYPE, MeetingManager.START);
        	meetingManager.sendWs(MeetingManager.CMD_AUDIOACTION, contentId, 0, null, json);
        }

		mBtnPlay.setImageDrawable(getResources().getDrawable(R.drawable.btn_pause));
		mMediaPlayer.start();
	}
	
	public void stop() {
        if (mMediaPlayer != null) {
        	mVisualizer.release();
        	mMediaPlayer.release();
        	mMediaPlayer = null;
        }
		if (mExecutor != null) {
			mExecutor.shutdownNow();
		}
	}

	public void jump(int progress) {
		mCurrentPosition = progress;
		mMediaPlayer.seekTo(mCurrentPosition);
		mSeekBar.setProgress(mCurrentPosition);
        if (meetingManager.isSendable()) {
        	JSONObject json = new JSONObject();
			json.put(MeetingManager.TYPE, mMediaPlayer.isPlaying()? MeetingManager.START: MeetingManager.PAUSE);
			json.put(MeetingManager.PLAY_TIME, progress / 1000);
        	meetingManager.sendWs(MeetingManager.CMD_AUDIOACTION, contentId, 0, null, json);
        }
	}

	public int getDuration() {
		return duration;
	}
	
}