/*
 * libjingle
 * Copyright 2014 Google Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 *     this list of conditions and the following disclaimer in the documentation
 *     and/or other materials provided with the distribution.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.appspot.apprtc;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.EditorInfo;
import android.webkit.URLUtil;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;

import org.json.JSONArray;
import org.json.JSONException;
import org.webrtc.MediaCodecVideoEncoder;

import java.util.ArrayList;
import java.util.Random;

/**
 * Handles the initial setup where the user selects which room to join.
 */
public class ConnectActivity extends Activity {

  public static final String EXTRA_ROOMNAME = "org.appspot.apprtc.ROOMNAME";
  public static final String EXTRA_LOOPBACK = "org.appspot.apprtc.LOOPBACK";
  public static final String EXTRA_CMDLINE = "org.appspot.apprtc.CMDLINE";
  public static final String EXTRA_RUNTIME = "org.appspot.apprtc.RUNTIME";
  public static final String EXTRA_BITRATE = "org.appspot.apprtc.BITRATE";
  public static final String EXTRA_VIDEOCODEC = "org.appspot.apprtc.VIDEOCODEC";
  public static final String EXTRA_HWCODEC = "org.appspot.apprtc.HWCODEC";
  private static final String TAG = "ConnectActivity";
  private static final int CONNECTION_REQUEST = 1;
  private static boolean commandLineRun = false;

  private ImageButton addRoomButton;
  private ImageButton removeRoomButton;
  private ImageButton connectButton;
  private ImageButton connectLoopbackButton;
  private EditText roomEditText;
  private ListView roomListView;
  private SharedPreferences sharedPref;
  private String keyprefResolution;
  private String keyprefFps;
  private String keyprefBitrateType;
  private String keyprefBitrateValue;
  private String keyprefVideoCodec;
  private String keyprefHwCodecAcceleration;
  private String keyprefCpuUsageDetection;
  private String keyprefRoomServerUrl;
  private String keyprefRoom;
  private String keyprefRoomList;
  private ArrayList<String> roomList;
  private ArrayAdapter<String> adapter;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Get setting keys.
    PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
    sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
    keyprefResolution = getString(R.string.pref_resolution_key);
    keyprefFps = getString(R.string.pref_fps_key);
    keyprefBitrateType = getString(R.string.pref_startbitrate_key);
    keyprefBitrateValue = getString(R.string.pref_startbitratevalue_key);
    keyprefVideoCodec = getString(R.string.pref_videocodec_key);
    keyprefHwCodecAcceleration = getString(R.string.pref_hwcodec_key);
    keyprefCpuUsageDetection = getString(R.string.pref_cpu_usage_detection_key);
    keyprefRoomServerUrl = getString(R.string.pref_room_server_url_key);
    keyprefRoom = getString(R.string.pref_room_key);
    keyprefRoomList = getString(R.string.pref_room_list_key);

    setContentView(R.layout.activity_connect);

    roomEditText = (EditText) findViewById(R.id.room_edittext);
    roomEditText.setOnEditorActionListener(
      new TextView.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(
            TextView textView, int i, KeyEvent keyEvent) {
          if (i == EditorInfo.IME_ACTION_DONE) {
            addRoomButton.performClick();
            return true;
          }
          return false;
        }
    });
    roomEditText.requestFocus();

    roomListView = (ListView) findViewById(R.id.room_listview);
    roomListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

    addRoomButton = (ImageButton) findViewById(R.id.add_room_button);
    addRoomButton.setOnClickListener(addRoomListener);
    removeRoomButton = (ImageButton) findViewById(R.id.remove_room_button);
    removeRoomButton.setOnClickListener(removeRoomListener);
    connectButton = (ImageButton) findViewById(R.id.connect_button);
    connectButton.setOnClickListener(connectListener);
    connectLoopbackButton =
        (ImageButton) findViewById(R.id.connect_loopback_button);
    connectLoopbackButton.setOnClickListener(connectListener);

    // If an implicit VIEW intent is launching the app, go directly to that URL.
    final Intent intent = getIntent();
    if ("android.intent.action.VIEW".equals(intent.getAction())
        && !commandLineRun) {
      commandLineRun = true;
      boolean loopback = intent.getBooleanExtra(EXTRA_LOOPBACK, false);
      int runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0);
      String room = sharedPref.getString(keyprefRoom, "");
      roomEditText.setText(room);
      connectToRoom(loopback, runTimeMs);
      return;
    }
  }

  @Override
  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.connect_menu, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    // Handle presses on the action bar items.
    if (item.getItemId() == R.id.action_settings) {
      Intent intent = new Intent(this, SettingsActivity.class);
      startActivity(intent);
      return true;
    } else {
      return super.onOptionsItemSelected(item);
    }
  }

  @Override
  public void onPause() {
    super.onPause();
    String room = roomEditText.getText().toString();
    String roomListJson = new JSONArray(roomList).toString();
    SharedPreferences.Editor editor = sharedPref.edit();
    editor.putString(keyprefRoom, room);
    editor.putString(keyprefRoomList, roomListJson);
    editor.commit();
  }

  @Override
  public void onResume() {
    super.onResume();
    String room = sharedPref.getString(keyprefRoom, "");
    roomEditText.setText(room);
    roomList = new ArrayList<String>();
    String roomListJson = sharedPref.getString(keyprefRoomList, null);
    if (roomListJson != null) {
      try {
        JSONArray jsonArray = new JSONArray(roomListJson);
        for (int i = 0; i < jsonArray.length(); i++) {
          roomList.add(jsonArray.get(i).toString());
        }
      } catch (JSONException e) {
        Log.e(TAG, "Failed to load room list: " + e.toString());
      }
    }
    adapter = new ArrayAdapter<String>(
        this, android.R.layout.simple_list_item_1, roomList);
    roomListView.setAdapter(adapter);
    if (adapter.getCount() > 0) {
      roomListView.requestFocus();
      roomListView.setItemChecked(0, true);
    }
  }

  @Override
  protected void onActivityResult(
      int requestCode, int resultCode, Intent data) {
    if (requestCode == CONNECTION_REQUEST && commandLineRun) {
      Log.d(TAG, "Return: " + resultCode);
      setResult(resultCode);
      finish();
    }
  }

  private final OnClickListener connectListener = new OnClickListener() {
    @Override
    public void onClick(View view) {
      boolean loopback = false;
      if (view.getId() == R.id.connect_loopback_button) {
        loopback = true;
      }
      commandLineRun = false;
      connectToRoom(loopback, 0);
    }
  };

  private String appendQueryParameter(String url, String parameter) {
    String newUrl = url;
    if (newUrl.contains("?")) {
      newUrl += "&" + parameter;
    } else {
      newUrl += "?" + parameter;
    }
    return newUrl;
  }

  private void connectToRoom(boolean loopback, int runTimeMs) {
    // Get room name (random for loopback).
    String roomName;
    if (loopback) {
      roomName = Integer.toString((new Random()).nextInt(100000000));
    } else {
      roomName = getSelectedItem();
      if (roomName == null) {
        roomName = roomEditText.getText().toString();
      }
    }

    String url = sharedPref.getString(
        keyprefRoomServerUrl,
        getString(R.string.pref_room_server_url_default));
    url = WebSocketRTCClient.getGAEConnectionUrl(url, roomName);

    // Get default video codec.
    String videoCodec = sharedPref.getString(keyprefVideoCodec,
        getString(R.string.pref_videocodec_default));

    // Check HW codec flag.
    boolean hwCodec = sharedPref.getBoolean(keyprefHwCodecAcceleration,
        Boolean.valueOf(getString(R.string.pref_hwcodec_default)));

    // Add video resolution constraints.
    String parametersResolution = null;
    String parametersFps = null;
    String resolution = sharedPref.getString(keyprefResolution,
        getString(R.string.pref_resolution_default));
    String[] dimensions = resolution.split("[ x]+");
    if (dimensions.length == 2) {
      try {
        int maxWidth = Integer.parseInt(dimensions[0]);
        int maxHeight = Integer.parseInt(dimensions[1]);
        if (maxWidth > 0 && maxHeight > 0) {
          parametersResolution = "minHeight=" + maxHeight + ",maxHeight="
              + maxHeight + ",minWidth=" + maxWidth + ",maxWidth=" + maxWidth;
        }
      } catch (NumberFormatException e) {
        Log.e(TAG, "Wrong video resolution setting: " + resolution);
      }
    }

    // Add camera fps constraints.
    String fps = sharedPref.getString(keyprefFps,
        getString(R.string.pref_fps_default));
    String[] fpsValues = fps.split("[ x]+");
    if (fpsValues.length == 2) {
      try {
        int cameraFps = Integer.parseInt(fpsValues[0]);
        if (cameraFps > 0) {
          parametersFps = "minFrameRate=" + cameraFps
              + ",maxFrameRate=" + cameraFps;
        }
      } catch (NumberFormatException e) {
        Log.e(TAG, "Wrong camera fps setting: " + fps);
      }
    }

    // Modify connection URL.
    if (parametersResolution != null || parametersFps != null) {
      String urlVideoParameters = "video=";
      if (parametersResolution != null) {
        urlVideoParameters += parametersResolution;
        if (parametersFps != null) {
          urlVideoParameters += ",";
        }
      }
      if (parametersFps != null) {
        urlVideoParameters += parametersFps;
      }
      url = appendQueryParameter(url, urlVideoParameters);
    } else {
      if (hwCodec && MediaCodecVideoEncoder.isPlatformSupported()) {
        url = appendQueryParameter(url, "hd=true");
      }
    }

    // Get start bitrate.
    int startBitrate = 0;
    String bitrateTypeDefault = getString(R.string.pref_startbitrate_default);
    String bitrateType = sharedPref.getString(
        keyprefBitrateType, bitrateTypeDefault);
    if (!bitrateType.equals(bitrateTypeDefault)) {
      String bitrateValue = sharedPref.getString(keyprefBitrateValue,
          getString(R.string.pref_startbitratevalue_default));
      startBitrate = Integer.parseInt(bitrateValue);
    }

    // Test if CpuOveruseDetection should be disabled. By default is on.
    boolean cpuOveruseDetection = sharedPref.getBoolean(
        keyprefCpuUsageDetection,
        Boolean.valueOf(
            getString(R.string.pref_cpu_usage_detection_default)));
    if (!cpuOveruseDetection) {
      url = appendQueryParameter(url, "googCpuOveruseDetection=false");
    }

    // Start AppRTCDemo activity.
    Log.d(TAG, "Connecting to room " + roomName + " at URL " + url);
    if (validateUrl(url)) {
      Uri uri = Uri.parse(url);
      Intent intent = new Intent(this, AppRTCDemoActivity.class);
      intent.setData(uri);
      intent.putExtra(EXTRA_ROOMNAME, roomName);
      intent.putExtra(EXTRA_LOOPBACK, loopback);
      intent.putExtra(EXTRA_CMDLINE, commandLineRun);
      intent.putExtra(EXTRA_RUNTIME, runTimeMs);
      intent.putExtra(EXTRA_BITRATE, startBitrate);
      intent.putExtra(EXTRA_VIDEOCODEC, videoCodec);
      intent.putExtra(EXTRA_HWCODEC, hwCodec);
      startActivityForResult(intent, CONNECTION_REQUEST);
    }
  }

  private boolean validateUrl(String url) {
    if (URLUtil.isHttpsUrl(url) || URLUtil.isHttpUrl(url)) {
      return true;
    }

    new AlertDialog.Builder(this)
        .setTitle(getText(R.string.invalid_url_title))
        .setMessage(getString(R.string.invalid_url_text, url))
        .setCancelable(false)
        .setNeutralButton(R.string.ok, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
              dialog.cancel();
            }
          }).create().show();
    return false;
  }

  private final OnClickListener addRoomListener = new OnClickListener() {
    @Override
    public void onClick(View view) {
      String newRoom = roomEditText.getText().toString();
      if (newRoom.length() > 0 && !roomList.contains(newRoom)) {
        adapter.add(newRoom);
        adapter.notifyDataSetChanged();
      }
    }
  };

  private final OnClickListener removeRoomListener = new OnClickListener() {
    @Override
    public void onClick(View view) {
      String selectedRoom = getSelectedItem();
      if (selectedRoom != null) {
        adapter.remove(selectedRoom);
        adapter.notifyDataSetChanged();
      }
    }
  };

  private String getSelectedItem() {
    int position = AdapterView.INVALID_POSITION;
    if (roomListView.getCheckedItemCount() > 0 && adapter.getCount() > 0) {
      position = roomListView.getCheckedItemPosition();
      if (position >= adapter.getCount()) {
        position = AdapterView.INVALID_POSITION;
      }
    }
    if (position != AdapterView.INVALID_POSITION) {
      return adapter.getItem(position);
    } else {
      return null;
    }
  }

}
