/*
 * Copyright (C) 2017 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.googlecode.android_scripting.activity;

import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import com.googlecode.android_scripting.Constants;
import com.googlecode.android_scripting.Log;
import com.googlecode.android_scripting.R;
import com.googlecode.android_scripting.interpreter.InterpreterProcess;
import com.googlecode.android_scripting.service.ScriptingLayerService;

import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.connectbot.ConsoleActivity;

/**
 * An activity that allows to monitor running scripts.
 *
 */
public class ScriptProcessMonitor extends ListActivity {

  private final static int UPDATE_INTERVAL_SECS = 1;

  private final Timer mTimer = new Timer();

  private volatile ScriptingLayerService mService;

  private ScriptListAdapter mUpdater;
  private List<InterpreterProcess> mProcessList;
  private ScriptMonitorAdapter mAdapter;
  private boolean mIsConnected = false;

  private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      mService = ((ScriptingLayerService.LocalBinder) service).getService();
      mUpdater = new ScriptListAdapter();
      mTimer.scheduleAtFixedRate(mUpdater, 0, UPDATE_INTERVAL_SECS * 1000);
      mIsConnected = true;
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
      mService = null;
      mIsConnected = false;
      mProcessList = null;
      mAdapter.notifyDataSetChanged();
    }
  };

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    bindService(new Intent(this, ScriptingLayerService.class), mConnection, 0);
    CustomizeWindow.requestCustomTitle(this, "Script Monitor", R.layout.script_monitor);
    mAdapter = new ScriptMonitorAdapter();
    setListAdapter(mAdapter);
    registerForContextMenu(getListView());
  }

  @Override
  public void onPause() {
    super.onPause();
    if (mUpdater != null) {
      mUpdater.cancel();
    }
    mTimer.purge();
  }

  @Override
  public void onResume() {
    super.onResume();
    if (mIsConnected) {
      try {
        mUpdater = new ScriptListAdapter();
        mTimer.scheduleAtFixedRate(mUpdater, 0, UPDATE_INTERVAL_SECS * 1000);
      } catch (IllegalStateException e) {
        Log.e(e.getMessage(), e);
      }
    }
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    mTimer.cancel();
    unbindService(mConnection);
  }

  @Override
  protected void onListItemClick(ListView list, View view, int position, long id) {
    final InterpreterProcess script = (InterpreterProcess) list.getItemAtPosition(position);
    Intent intent = new Intent(this, ConsoleActivity.class);
    intent.putExtra(Constants.EXTRA_PROXY_PORT, script.getPort());
    startActivity(intent);
  }

  @Override
  public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
    menu.add(Menu.NONE, 0, Menu.NONE, "Stop");
  }

  @Override
  public boolean onContextItemSelected(MenuItem item) {
    AdapterView.AdapterContextMenuInfo info;
    try {
      info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
    } catch (ClassCastException e) {
      Log.e("Bad menuInfo", e);
      return false;
    }

    InterpreterProcess script = mAdapter.getItem(info.position);
    if (script == null) {
      Log.v("No script selected.");
      return false;
    }
    Intent intent = new Intent(ScriptProcessMonitor.this, ScriptingLayerService.class);
    intent.setAction(Constants.ACTION_KILL_PROCESS);
    intent.putExtra(Constants.EXTRA_PROXY_PORT, script.getPort());
    Log.i(String.format("Killing process from ScriptProcessMonitor: %s", intent.toUri(0)));
    startService(intent);

    return true;
  }

  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {
    menu.clear();
    // TODO(damonkohler): How could mProcessList ever be null?
    if (mProcessList != null && !mProcessList.isEmpty()) {
      menu.add(Menu.NONE, 0, Menu.NONE, R.string.stop_all).setIcon(
          android.R.drawable.ic_menu_close_clear_cancel);
    }
    return super.onPrepareOptionsMenu(menu);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    Intent intent = new Intent(this, ScriptingLayerService.class);
    intent.setAction(Constants.ACTION_KILL_ALL);
    startService(intent);
    return true;
  }

  private class ScriptListAdapter extends TimerTask {
    private int mmExpectedModCount = 0;
    private volatile List<InterpreterProcess> mmList;

    @Override
    public void run() {
      if (mService == null) {
        mmList.clear();
        mTimer.cancel();
      } else {
        int freshModCount = mService.getModCount();
        if (freshModCount != mmExpectedModCount) {
          mmExpectedModCount = freshModCount;
          mmList = mService.getScriptProcessesList();
        }
      }
      runOnUiThread(new Runnable() {
        public void run() {
          mProcessList = mUpdater.getFreshProcessList();
          mAdapter.notifyDataSetChanged();
        }
      });
    }

    private List<InterpreterProcess> getFreshProcessList() {
      return mmList;
    }
  }

  private class ScriptMonitorAdapter extends BaseAdapter {

    @Override
    public int getCount() {
      if (mProcessList == null) {
        return 0;
      }
      return mProcessList.size();
    }

    @Override
    public InterpreterProcess getItem(int position) {
      return mProcessList.get(position);
    }

    @Override
    public long getItemId(int position) {
      return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
      View itemView;
      if (convertView == null) {
        LayoutInflater inflater =
            (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        itemView = inflater.inflate(R.layout.script_monitor_list_item, parent, false);
      } else {
        itemView = convertView;
      }
      InterpreterProcess process = mProcessList.get(position);
      ((TextView) itemView.findViewById(R.id.process_title)).setText(process.getName());
      ((TextView) itemView.findViewById(R.id.process_age)).setText(process.getUptime());
      ((TextView) itemView.findViewById(R.id.process_details)).setText(process.getHost() + ":"
          + process.getPort());
      ((TextView) itemView.findViewById(R.id.process_status)).setText("PID " + process.getPid());
      return itemView;
    }
  }
}
