/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.mediaframeworktest.functional;



//import android.content.Resources;
import com.android.mediaframeworktest.MediaFrameworkTest;
import com.android.mediaframeworktest.MediaNames;

import android.content.res.AssetFileDescriptor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
/**
 * Junit / Instrumentation test case for the media player api
 
 */  
public class CodecTest {    
    private static String TAG = "MediaPlayerApiTest";
    private static MediaPlayer mMediaPlayer;
    private MediaPlayer.OnPreparedListener mOnPreparedListener;
    
    private static int WAIT_FOR_COMMAND_TO_COMPLETE = 10000;  //10 seconds max.
    private static boolean mInitialized = false;
    private static Looper mLooper = null;
    private static final Object lock = new Object();
    private static final Object prepareDone = new Object();
    private static boolean onPrepareSuccess = false;
    

    public static String printCpuInfo(){      
        String cm = "dumpsys cpuinfo";
        String cpuinfo =null;
        int ch;
        try{
            Process  p = Runtime.getRuntime().exec(cm);
            InputStream in = p.getInputStream();        
            StringBuffer sb = new StringBuffer(512);
            while ( ( ch = in.read() ) != -1 ){  
                sb.append((char) ch); 
            }
            cpuinfo = sb.toString();      
        }catch (IOException e){
            Log.v(TAG, e.toString());
        }
        return cpuinfo;
    }


    public static int getDuration(String filePath) {
        Log.v(TAG, "getDuration - " + filePath);
        MediaPlayer mp = new MediaPlayer();
        try{
            mp.setDataSource(filePath);
            mp.prepare(); 
        }catch (Exception e){}
        int duration = mp.getDuration();
        Log.v(TAG, "Duration " + duration);
        mp.release();
        Log.v(TAG, "release");      
        return duration;
    }

    public static boolean getCurrentPosition(String filePath){
        Log.v(TAG, "GetCurrentPosition - " + filePath);
        int currentPosition = 0;
        long t1=0;
        long t2 =0;
        MediaPlayer mp = new MediaPlayer();
        try{
            mp.setDataSource(filePath);
            Log.v(TAG, "start playback");
            mp.prepare();
            mp.start();
            t1=SystemClock.uptimeMillis();
            Thread.sleep(10000);
            mp.pause();
            Thread.sleep(MediaNames.PAUSE_WAIT_TIME);
            t2=SystemClock.uptimeMillis();
        }catch (Exception e){
            Log.v(TAG, e.toString());
        }
        currentPosition = mp.getCurrentPosition();
        mp.stop();
        mp.release();   
        Log.v(TAG, "mp currentPositon = " + currentPosition + " play duration = " + (t2-t1));
        //The currentposition should be within 10% of the sleep time
        //For the very short mp3, it should return the length instead of 10 seconds
        if (filePath.equals(MediaNames.SHORTMP3)){
            if (currentPosition < 1000 )
                return true;
        }
        if ((currentPosition < ((t2-t1) *1.2)) && (currentPosition > 0)) 
            return true;
        else
            return false;
    }  

    public static boolean seekTo(String filePath){
        Log.v(TAG, "seekTo " + filePath);
        int currentPosition = 0;
        MediaPlayer mp = new MediaPlayer();
        try{
            mp.setDataSource(filePath);
            mp.prepare();
            mp.start();
            mp.seekTo(MediaNames.SEEK_TIME);
            Thread.sleep(MediaNames.WAIT_TIME);
            currentPosition = mp.getCurrentPosition();
        }catch (Exception e){
            Log.v(TAG, e.getMessage());
        }      
        mp.stop();
        mp.release();
        Log.v(TAG, "CurrentPosition = " + currentPosition);
        //The currentposition should be at least greater than the 80% of seek time
        if ((currentPosition > MediaNames.SEEK_TIME *0.8)) 
            return true;
        else
            return false;
    }

    public static boolean setLooping(String filePath){
        int currentPosition = 0;
        int duration = 0;
        long t1 =0;
        long t2 =0;
        Log.v (TAG, "SetLooping - " + filePath);
        MediaPlayer mp = new MediaPlayer();
        try{
            mp.setDataSource(filePath);
            mp.prepare();
            duration = mp.getDuration(); 
            Log.v(TAG, "setLooping duration " + duration);
            mp.setLooping(true);
            mp.start();
            Thread.sleep(5000);
            mp.seekTo(duration - 5000);
            t1=SystemClock.uptimeMillis();
            Thread.sleep(20000);
            t2=SystemClock.uptimeMillis();
            Log.v(TAG, "pause");
            //Bug# 1106852 - IllegalStateException will be thrown if pause is called 
            //in here
            //mp.pause();
            currentPosition = mp.getCurrentPosition();
            Log.v(TAG, "looping position " + currentPosition + "duration = " + (t2-t1));
        }catch (Exception e){
            Log.v(TAG, "Exception : " + e.toString());
        }      
        mp.stop();
        mp.release();
        //The current position should be within 20% of the sleep time
        //and should be greater than zero.
        if ((currentPosition < ((t2-t1-5000)*1.2)) && currentPosition > 0)
            return true;
        else
            return false;
    }  

    public static boolean pause(String filePath) throws Exception {
        Log.v(TAG, "pause - " + filePath);
        boolean misPlaying = true;
        boolean pauseResult = false;
        long t1=0;
        long t2=0;
        MediaPlayer mp = new MediaPlayer();
        mp.setDataSource(filePath);
        mp.prepare();    
        int duration = mp.getDuration();
        mp.start();
        t1=SystemClock.uptimeMillis();
        Thread.sleep(5000);
        mp.pause();
        Thread.sleep(MediaNames.PAUSE_WAIT_TIME);
        t2=SystemClock.uptimeMillis();
        misPlaying = mp.isPlaying();
        int curPosition = mp.getCurrentPosition();
        Log.v(TAG, filePath + " pause currentPositon " + curPosition);
        Log.v(TAG, "isPlaying "+ misPlaying + " wait time " + (t2 - t1) );
        String cpuinfo = printCpuInfo();
        Log.v(TAG, cpuinfo);
        if ((curPosition>0) && (curPosition < ((t2-t1) * 1.3)) && (misPlaying == false))
            pauseResult = true;
        mp.stop();
        mp.release();
        return pauseResult;
    }

    public static void prepareStopRelease(String filePath) throws Exception {
        Log.v(TAG, "prepareStopRelease" + filePath);
        MediaPlayer mp = new MediaPlayer();
        mp.setDataSource(filePath);
        mp.prepare();
        mp.stop();
        mp.release();
    }

    public static void preparePauseRelease(String filePath) throws Exception {
        Log.v(TAG, "preparePauseRelease" + filePath);
        MediaPlayer mp = new MediaPlayer();
        mp.setDataSource(filePath);
        mp.prepare();
        mp.pause();
        mp.release();
    }

    public static int videoHeight(String filePath) throws Exception {
        Log.v(TAG, "videoHeight - " + filePath);
        int videoHeight = 0;
        MediaPlayer mp = new MediaPlayer();
        mp.setDataSource(filePath);
        mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
        mp.prepare();
        videoHeight = mp.getVideoHeight();
        mp.release();
        return videoHeight;
    }

    public static int videoWidth(String filePath) throws Exception {
        Log.v(TAG, "videoWidth - " + filePath);
        int videoWidth = 0;
        MediaPlayer mp = new MediaPlayer();
        mp.setDataSource(filePath);
        mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
        mp.prepare();
        videoWidth = mp.getVideoWidth();
        mp.release();
        return videoWidth;
    }

    //This also test the streaming video which may take a long
    //time to start the playback. 
    public static boolean videoSeekTo(String filePath) throws Exception {
        Log.v(TAG, "videoSeekTo - " + filePath);
        int currentPosition = 0;
        int duration = 0;
        boolean videoResult = false;
        MediaPlayer mp = new MediaPlayer();
        mp.setDataSource(filePath);
        mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
        mp.prepare();
        mp.start();
        if (filePath.equals(MediaNames.VIDEO_SHORT_3GP)){
            mp.pause();
            Thread.sleep(MediaNames.PAUSE_WAIT_TIME);
            mp.seekTo(0);
            mp.start();
            Thread.sleep(1000);
            currentPosition = mp.getCurrentPosition();
            Log.v(TAG,"short position " + currentPosition);
            if (currentPosition > 100 )
                return true;
            else
                return false;
        }
        Thread.sleep(5000);
        duration = mp.getDuration();
        Log.v(TAG, "video duration " + duration);
        mp.pause();
        Thread.sleep(MediaNames.PAUSE_WAIT_TIME);
        mp.seekTo(duration - 20000 );
        mp.start();
        Thread.sleep(1000);
        mp.pause();
        Thread.sleep(MediaNames.PAUSE_WAIT_TIME);
        mp.seekTo(duration/2);
        mp.start();
        Thread.sleep(10000);
        currentPosition = mp.getCurrentPosition();
        Log.v(TAG, "video currentPosition " + currentPosition);
        mp.release();
        if (currentPosition > (duration /2 )*0.9)
            return true;
        else
            return false;

    }

    public static boolean seekToEnd(String filePath){
        Log.v(TAG, "seekToEnd - " + filePath);
        int duration = 0;
        int currentPosition = 0;
        boolean isPlaying = false;
        MediaPlayer mp = new MediaPlayer();
        try{
            mp.setDataSource(filePath);
            Log.v(TAG, "start playback");
            mp.prepare();
            duration = mp.getDuration();
            mp.seekTo(duration - 3000);
            mp.start();
            Thread.sleep(6000);
        }catch (Exception e){}
        isPlaying = mp.isPlaying();
        currentPosition = mp.getCurrentPosition();
        Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
        mp.stop();
        mp.release();   
        Log.v(TAG, "duration = " + duration);
        if (currentPosition < 0.9 * duration || isPlaying)
            return false;
        else
            return true;        
    }

    public static boolean shortMediaStop(String filePath){
        Log.v(TAG, "shortMediaStop - " + filePath);
        //This test is only for the short media file
        int duration = 0;
        int currentPosition = 0;
        boolean isPlaying = false;
        MediaPlayer mp = new MediaPlayer();
        try{
            mp.setDataSource(filePath);
            Log.v(TAG, "start playback");
            mp.prepare();
            duration = mp.getDuration();
            mp.start();
            Thread.sleep(10000);
        }catch (Exception e){}
        isPlaying = mp.isPlaying();
        currentPosition = mp.getCurrentPosition();
        Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
        mp.stop();
        mp.release();   
        Log.v(TAG, "duration = " + duration);
        if (currentPosition > duration || isPlaying)
            return false;
        else
            return true;        
    }

    public static boolean playToEnd(String filePath){
        Log.v(TAG, "shortMediaStop - " + filePath);
        //This test is only for the short media file
        int duration = 200000;
        int updateDuration = 0;
        int currentPosition = 0;
        boolean isPlaying = false;
        MediaPlayer mp = new MediaPlayer();
        try{
            Thread.sleep(5000);
            mp.setDataSource(filePath);
            Log.v(TAG, "start playback");
            mp.prepare();
            //duration = mp.getDuration();
            mp.start();
            Thread.sleep(50000);
        }catch (Exception e){}
        isPlaying = mp.isPlaying();
        currentPosition = mp.getCurrentPosition();
        //updateDuration = mp.getDuration();
        Log.v(TAG, "seekToEnd currentPosition= " + currentPosition + " isPlaying = " + isPlaying);
        mp.stop();
        mp.release();   
        //Log.v(TAG, "duration = " + duration);
        //Log.v(TAG, "Update duration = " + updateDuration);
        if (currentPosition > duration || isPlaying)
            return false;
        else
            return true;        
    }

    public static boolean seektoBeforeStart(String filePath){
        Log.v(TAG, "seektoBeforeStart - " + filePath);
        //This test is only for the short media file
        int duration = 0;
        int currentPosition = 0;

        MediaPlayer mp = new MediaPlayer();
        try{
            mp.setDataSource(filePath);
            mp.prepare();
            duration = mp.getDuration();
            mp.seekTo(duration - 10000);
            mp.start();
            currentPosition=mp.getCurrentPosition();
            mp.stop();
            mp.release();
        }catch (Exception e){}
        if (currentPosition < duration/2)
            return false;
        else
            return true;        
    }

    public static boolean mediaRecorderRecord(String filePath){
        Log.v(TAG, "SoundRecording - " + filePath);
        //This test is only for the short media file
        int duration = 0;
        try{
            MediaRecorder mRecorder = new MediaRecorder();
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            mRecorder.setOutputFile(filePath);
            mRecorder.prepare();
            mRecorder.start();
            Thread.sleep(500);
            mRecorder.stop();
            Log.v(TAG, "sound recorded");
            mRecorder.release();
        }catch (Exception e){
            Log.v(TAG, e.toString());
        }  

        //Verify the recorded file
        MediaPlayer mp = new MediaPlayer();
        try{
            mp.setDataSource(filePath);
            mp.prepare();
            duration = mp.getDuration();
            Log.v(TAG,"Duration " + duration);
            mp.release();
        }catch (Exception e){}
        //Check the record media file length is greate than zero
        if (duration > 0)
            return true;
        else
            return false;

    }

    //Test for mediaMeta Data Thumbnail
    public static boolean getThumbnail(String filePath, String goldenPath){
        Log.v(TAG, "getThumbnail - " + filePath);

        int goldenHeight = 0;
        int goldenWidth = 0;
        int outputWidth = 0;
        int outputHeight = 0;

        //This test is only for the short media file
        try{
            BitmapFactory mBitmapFactory = new BitmapFactory();

            MediaMetadataRetriever mMediaMetadataRetriever = new MediaMetadataRetriever();
            try {
                mMediaMetadataRetriever.setDataSource(filePath);
            } catch(Exception e) {
                e.printStackTrace();
                return false;
            }
            Bitmap outThumbnail = mMediaMetadataRetriever.captureFrame();

            //Verify the thumbnail 
            Bitmap goldenBitmap = mBitmapFactory.decodeFile(goldenPath);

            outputWidth = outThumbnail.getWidth();
            outputHeight = outThumbnail.getHeight();
            goldenHeight = goldenBitmap.getHeight();
            goldenWidth = goldenBitmap.getWidth();

            //check the image dimension
            if ((outputWidth != goldenWidth) || (outputHeight != goldenHeight))
                return false;

            //Check one line of pixel
            int x = goldenHeight/2;
            for (int j=0; j<goldenWidth; j++){
                if (goldenBitmap.getPixel(x, j) != outThumbnail.getPixel(x, j)){
                    Log.v(TAG, "pixel = " + goldenBitmap.getPixel(x, j));
                    return false;    
                }
            }
        }catch (Exception e){}
        return true;
    }

    //Load midi file from resources
    public static boolean resourcesPlayback(AssetFileDescriptor afd, int expectedDuration){
        int duration = 0;
        try{
            MediaPlayer mp = new MediaPlayer();
            mp.setDataSource(afd.getFileDescriptor(),afd.getStartOffset(), afd.getLength());
            mp.prepare();
            mp.start();
            duration = mp.getDuration();
            Thread.sleep(5000);
            mp.release();
        }catch (Exception e){
            Log.v(TAG,e.getMessage());
        }
        if (duration > expectedDuration)
            return true;
        else
            return false;
    }

    public static boolean prepareAsyncReset(String filePath){    
        //preparesAsync 
        try{
            MediaPlayer mp = new MediaPlayer();
            mp.setDataSource(filePath);
            mp.prepareAsync();
            mp.reset();
            mp.release();
        }catch (Exception e){
            Log.v(TAG,e.getMessage());
            return false;
        }
        return true;
    }


    public static boolean isLooping(String filePath) {        
        MediaPlayer mp = null;
        
        try {
            mp = new MediaPlayer();
            if (mp.isLooping()) {
                Log.v(TAG, "MediaPlayer.isLooping() returned true after ctor");
                return false;
            }
            mp.setDataSource(filePath);
            mp.prepare();

            mp.setLooping(true);
            if (!mp.isLooping()) {
                Log.v(TAG, "MediaPlayer.isLooping() returned false after setLooping(true)");
                return false;
            }
            
            mp.setLooping(false);
            if (mp.isLooping()) {
                Log.v(TAG, "MediaPlayer.isLooping() returned true after setLooping(false)");
                return false;
            }
        }catch (Exception e){
            Log.v(TAG, "Exception : " + e.toString());
            return false;
        } finally {
            if (mp != null)
                mp.release();
        }

        return true;
    }

    public static boolean isLoopingAfterReset(String filePath) {
        MediaPlayer mp = null;
        try {
            mp = new MediaPlayer();
            mp.setDataSource(filePath);
            mp.prepare();

            mp.setLooping(true);
            mp.reset();
            if (mp.isLooping()) {
                Log.v(TAG, "MediaPlayer.isLooping() returned true after reset()");
                return false;
            }
        }catch (Exception e){
            Log.v(TAG, "Exception : " + e.toString());
            return false;
        } finally {
            if (mp != null)
                mp.release();
        }

        return true;
    }
    
    /*
     * Initializes the message looper so that the mediaPlayer object can 
     * receive the callback messages.
     */
    private static void initializeMessageLooper() {
        Log.v(TAG, "start looper");
        new Thread() {
            @Override
            public void run() {
                // Set up a looper to be used by camera.
                Looper.prepare();
                Log.v(TAG, "start loopRun");
                // Save the looper so that we can terminate this thread 
                // after we are done with it.
                mLooper = Looper.myLooper();                
                mMediaPlayer = new MediaPlayer();                                
                synchronized (lock) {
                    mInitialized = true;
                    lock.notify();
                }
                Looper.loop();  // Blocks forever until Looper.quit() is called.
                Log.v(TAG, "initializeMessageLooper: quit.");
            }
        }.start();
    }
    
    /*
     * Terminates the message looper thread.
     */
    private static void terminateMessageLooper() {
        mLooper.quit();
        mMediaPlayer.release();
    }
    
    static MediaPlayer.OnPreparedListener mPreparedListener = new MediaPlayer.OnPreparedListener() {
        public void onPrepared(MediaPlayer mp) {
            synchronized (prepareDone) {
                Log.v(TAG, "notify the prepare callback");
                prepareDone.notify();
                onPrepareSuccess = true;
            }
        }
    };
   
    public static boolean prepareAsyncCallback(String filePath) throws Exception {
        int videoWidth = 0;
        int videoHeight = 0;
        boolean checkVideoDimension = false;
        
        initializeMessageLooper();
        synchronized (lock) {
            try {
                lock.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
            } catch(Exception e) {
                Log.v(TAG, "looper was interrupted.");
                return false;
            }
        }
        try{
            mMediaPlayer.setOnPreparedListener(mPreparedListener);
            mMediaPlayer.setDataSource(filePath);
            mMediaPlayer.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
            mMediaPlayer.prepareAsync(); 
            synchronized (prepareDone) {
                try {
                    prepareDone.wait(WAIT_FOR_COMMAND_TO_COMPLETE);
                    Log.v(TAG, "setPreview done");
                } catch (Exception e) {
                    Log.v(TAG, "wait was interrupted.");
                }
            }
            videoWidth = mMediaPlayer.getVideoWidth();
            videoHeight = mMediaPlayer.getVideoHeight();
            
            terminateMessageLooper();
        }catch (Exception e){
            Log.v(TAG,e.getMessage());
        }      
       return onPrepareSuccess;
    }
    
    
    
}

