/*
 * Copyright (C) 2011 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.cts.verifier.audio;

import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;

import android.content.Context;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.PopupWindow;
import android.widget.TextView;
import java.util.Arrays;

import com.androidplot.xy.SimpleXYSeries;
import com.androidplot.xy.XYSeries;
import com.androidplot.xy.*;

public class HifiUltrasoundSpeakerTestActivity extends PassFailButtons.Activity {

  public enum Status {
    START, RECORDING, DONE, PLAYER
  }

  private static final String TAG = "HifiUltrasoundTestActivity";

  private Status status = Status.START;
  private boolean onPlotScreen = false;
  private boolean onInstruScreen = false;
  private TextView info;
  private Button playerButton;
  private Button recorderButton;
  private AudioTrack audioTrack;
  private LayoutInflater layoutInflater;
  private View popupView;
  private View instruView;
  private PopupWindow popupWindow;
  private PopupWindow instruWindow;
  private boolean micSupport = true;
  private boolean spkrSupport = true;

  @Override
  public void onBackPressed () {
    if (onPlotScreen) {
      popupWindow.dismiss();
      onPlotScreen = false;
      recorderButton.setEnabled(true);
    } else if (onInstruScreen) {
      instruWindow.dismiss();
      onInstruScreen = false;
      if (status == Status.PLAYER) {
        playerButton.setEnabled(spkrSupport);
      } else {
        recorderButton.setEnabled(micSupport);
      }
      if (status == Status.PLAYER) {
        getPassButton().setEnabled(true);
      }
    } else {
      super.onBackPressed();
    }
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.hifi_ultrasound);
    setInfoResources(R.string.hifi_ultrasound_speaker_test,
        R.string.hifi_ultrasound_speaker_test_info, -1);
    setPassFailButtonClickListeners();
    getPassButton().setEnabled(false);

    info = (TextView) findViewById(R.id.info_text);
    info.setMovementMethod(new ScrollingMovementMethod());
    info.setText(R.string.hifi_ultrasound_speaker_test_instruction1);

    AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    String micSupportString = audioManager.getProperty(
        AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND);
    String spkrSupportString = audioManager.getProperty(
        AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND);
    Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupportString);
    Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupportString);

    if (micSupportString == null) {
      micSupportString = "null";
    }
    if (spkrSupportString == null) {
      spkrSupportString = "null";
    }
    if (micSupportString.equalsIgnoreCase(getResources().getString(
        R.string.hifi_ultrasound_test_default_false_string))) {
      micSupport = false;
      getPassButton().setEnabled(true);
      info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_mic_no_support));
    }
    if (spkrSupportString.equalsIgnoreCase(getResources().getString(
        R.string.hifi_ultrasound_test_default_false_string))) {
      spkrSupport = false;
      info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_spkr_no_support));
    }

    layoutInflater = (LayoutInflater) getBaseContext().getSystemService(
        LAYOUT_INFLATER_SERVICE);
    popupView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup, null);
    popupWindow = new PopupWindow(
        popupView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    instruView = layoutInflater.inflate(R.layout.hifi_ultrasound_popup_instru, null);
    instruWindow = new PopupWindow(
        instruView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

    final AudioRecordHelper audioRecorder = AudioRecordHelper.getInstance();
    final int recordRate = audioRecorder.getSampleRate();

    recorderButton = (Button) findViewById(R.id.recorder_button);
    recorderButton.setEnabled(micSupport);
    recorderButton.setOnClickListener(new View.OnClickListener() {
      private WavAnalyzerTask wavAnalyzerTask = null;
      private void stopRecording() {
        audioRecorder.stop();
        wavAnalyzerTask = new WavAnalyzerTask(audioRecorder.getByte());
        wavAnalyzerTask.execute();
        status = Status.DONE;
      }
      @Override
      public void onClick(View v) {
        switch (status) {
          case START:
            info.append("Recording at " + recordRate + "Hz using ");
            final int source = audioRecorder.getAudioSource();
            switch (source) {
              case 1:
                info.append("MIC");
                break;
              case 6:
                info.append("VOICE_RECOGNITION");
                break;
              default:
                info.append("UNEXPECTED " + source);
                break;
            }
            info.append("\n");
            status = Status.RECORDING;
            playerButton.setEnabled(false);
            recorderButton.setEnabled(false);
            audioRecorder.start();

            final View finalV = v;
            new Thread() {
              @Override
              public void run() {
                Double recordingDuration_millis = new Double(1000 * (2.5
                    + Common.PREFIX_LENGTH_S
                    + Common.PAUSE_BEFORE_PREFIX_DURATION_S
                    + Common.PAUSE_AFTER_PREFIX_DURATION_S
                    + Common.PIP_NUM * (Common.PIP_DURATION_S + Common.PAUSE_DURATION_S)
                    * Common.REPETITIONS));
                Log.d(TAG, "Recording for " + recordingDuration_millis + "ms");
                try {
                  Thread.sleep(recordingDuration_millis.intValue());
                } catch (InterruptedException e) {
                  throw new RuntimeException(e);
                }
                runOnUiThread(new Runnable() {
                  @Override
                  public void run() {
                    stopRecording();
                  }
                });
              }
            }.start();

            break;

          case DONE:
            plotResponse(wavAnalyzerTask);
            break;

          default: break;
        }
      }
    });

    playerButton = (Button) findViewById(R.id.player_button);
    playerButton.setEnabled(spkrSupport);
    playerButton.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        recorderButton.setEnabled(false);
        status = Status.PLAYER;
        play();

        Button okButton = (Button)instruView.findViewById(R.id.ok);
        okButton.setOnClickListener(new Button.OnClickListener() {
          @Override
          public void onClick(View v) {
            instruWindow.dismiss();
            onInstruScreen = false;
            if (status == Status.PLAYER) {
              playerButton.setEnabled(spkrSupport);
            } else {
              recorderButton.setEnabled(micSupport);
            }
            getPassButton().setEnabled(true);
          }
        });
        TextView instruction = (TextView)instruView.findViewById(R.id.instru);
        instruction.setText(R.string.hifi_ultrasound_speaker_test_test_side);
        instruWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
        recorderButton.setEnabled(false);
        playerButton.setEnabled(false);
        onInstruScreen = true;
      }
    });
  }

  private void plotResponse(WavAnalyzerTask wavAnalyzerTask) {
    Button dismissButton = (Button)popupView.findViewById(R.id.dismiss);
    dismissButton.setOnClickListener(new Button.OnClickListener(){
      @Override
      public void onClick(View v) {
        popupWindow.dismiss();
        onPlotScreen = false;
        recorderButton.setEnabled(true);
      }});
    popupWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
    onPlotScreen = true;

    recorderButton.setEnabled(false);

    XYPlot plot = (XYPlot) popupView.findViewById(R.id.responseChart);
    plot.setDomainStep(XYStepMode.INCREMENT_BY_VAL, 2000);

    Double[] frequencies = new Double[Common.PIP_NUM];
    for (int i = 0; i < Common.PIP_NUM; i++) {
      frequencies[i] = new Double(Common.FREQUENCIES_ORIGINAL[i]);
    }

    if (wavAnalyzerTask != null) {

      double[][] power = wavAnalyzerTask.getPower();
      for(int i = 0; i < Common.REPETITIONS; i++) {
        Double[] powerWrap = new Double[Common.PIP_NUM];
        for (int j = 0; j < Common.PIP_NUM; j++) {
          powerWrap[j] = new Double(10 * Math.log10(power[j][i]));
        }
        XYSeries series = new SimpleXYSeries(
            Arrays.asList(frequencies),
            Arrays.asList(powerWrap),
            "");
        LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
        seriesFormat.configure(getApplicationContext(),
            R.xml.ultrasound_line_formatter_trials);
        seriesFormat.setPointLabelFormatter(null);
        plot.addSeries(series, seriesFormat);
      }

      double[] noiseDB = wavAnalyzerTask.getNoiseDB();
      Double[] noiseDBWrap = new Double[Common.PIP_NUM];
      for (int i = 0; i < Common.PIP_NUM; i++) {
        noiseDBWrap[i] = new Double(noiseDB[i]);
      }

      XYSeries noiseSeries = new SimpleXYSeries(
          Arrays.asList(frequencies),
          Arrays.asList(noiseDBWrap),
          "background noise");
      LineAndPointFormatter noiseSeriesFormat = new LineAndPointFormatter();
      noiseSeriesFormat.configure(getApplicationContext(),
          R.xml.ultrasound_line_formatter_noise);
      noiseSeriesFormat.setPointLabelFormatter(null);
      plot.addSeries(noiseSeries, noiseSeriesFormat);

      double[] dB = wavAnalyzerTask.getDB();
      Double[] dBWrap = new Double[Common.PIP_NUM];
      for (int i = 0; i < Common.PIP_NUM; i++) {
        dBWrap[i] = new Double(dB[i]);
      }

      XYSeries series = new SimpleXYSeries(
          Arrays.asList(frequencies),
          Arrays.asList(dBWrap),
          "median");
      LineAndPointFormatter seriesFormat = new LineAndPointFormatter();
      seriesFormat.configure(getApplicationContext(),
          R.xml.ultrasound_line_formatter_median);
      seriesFormat.setPointLabelFormatter(null);
      plot.addSeries(series, seriesFormat);

      Double[] passX = new Double[] {Common.MIN_FREQUENCY_HZ, Common.MAX_FREQUENCY_HZ};
      Double[] passY = new Double[] {wavAnalyzerTask.getThreshold(), wavAnalyzerTask.getThreshold()};
      XYSeries passSeries = new SimpleXYSeries(
          Arrays.asList(passX), Arrays.asList(passY), "passing");
      LineAndPointFormatter passSeriesFormat = new LineAndPointFormatter();
      passSeriesFormat.configure(getApplicationContext(),
          R.xml.ultrasound_line_formatter_pass);
      passSeriesFormat.setPointLabelFormatter(null);
      plot.addSeries(passSeries, passSeriesFormat);
    }
  }

  /**
   * Plays the generated pips.
   */
  private void play() {
    play(SoundGenerator.getInstance().getByte(), Common.PLAYING_SAMPLE_RATE_HZ);
  }

  /**
   * Plays the sound data.
   */
  private void play(byte[] data, int sampleRate) {
    if (audioTrack != null) {
      audioTrack.stop();
      audioTrack.release();
    }
    audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
        sampleRate, AudioFormat.CHANNEL_OUT_MONO,
        AudioFormat.ENCODING_PCM_16BIT, Math.max(data.length, AudioTrack.getMinBufferSize(
        sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT)),
        AudioTrack.MODE_STATIC);
    audioTrack.write(data, 0, data.length);
    audioTrack.play();
  }

  /**
   * AsyncTask class for the analyzing.
   */
  private class WavAnalyzerTask extends AsyncTask<Void, String, String>
      implements WavAnalyzer.Listener {

    private static final String TAG = "WavAnalyzerTask";
    WavAnalyzer wavAnalyzer;

    public WavAnalyzerTask(byte[] recording) {
      wavAnalyzer = new WavAnalyzer(recording, Common.RECORDING_SAMPLE_RATE_HZ,
          WavAnalyzerTask.this);
    }

    double[] getDB() {
      return wavAnalyzer.getDB();
    }

    double[][] getPower() {
      return wavAnalyzer.getPower();
    }

    double[] getNoiseDB() {
      return wavAnalyzer.getNoiseDB();
    }

    double getThreshold() {
      return wavAnalyzer.getThreshold();
    }

    @Override
    protected String doInBackground(Void... params) {
      boolean result = wavAnalyzer.doWork();
      if (result) {
        return getString(R.string.hifi_ultrasound_test_pass);
      }
      return getString(R.string.hifi_ultrasound_test_fail);
    }

    @Override
    protected void onPostExecute(String result) {
      info.append(result);
      recorderButton.setEnabled(true);
      recorderButton.setText(R.string.hifi_ultrasound_test_plot);

      Button okButton = (Button)instruView.findViewById(R.id.ok);
      okButton.setOnClickListener(new Button.OnClickListener() {
        @Override
        public void onClick(View v) {
          instruWindow.dismiss();
          onInstruScreen = false;
          if (status == HifiUltrasoundSpeakerTestActivity.Status.PLAYER) {
            playerButton.setEnabled(spkrSupport);
          } else {
            recorderButton.setEnabled(micSupport);
          }
        }
      });
      TextView instruction = (TextView) instruView.findViewById(R.id.instru);
      instruction.setText(R.string.hifi_ultrasound_speaker_test_reference_side);
      instruWindow.showAtLocation(info, Gravity.CENTER, 0, 0);
      recorderButton.setEnabled(false);
      playerButton.setEnabled(false);
      onInstruScreen = true;
    }

    @Override
    protected void onProgressUpdate(String... values) {
      for (String message : values) {
        info.append(message);
        Log.d(TAG, message);
      }
    }

    @Override
    public void sendMessage(String message) {
      publishProgress(message);
    }
  }
}
