package mappu.uninstaller;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;

import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;


public class MainActivity extends ActionBarActivity {

    // ArrayList<String> listItems=new ArrayList<String>();
    SimpleAdapter adapter;
    ArrayList<Map<String,String>> items = new ArrayList<Map<String,String>>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        setupListClickHandler();

        switchToMessageView("Loading apps...");
        populateAppList();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // show menu when menu button is pressed
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu_main, menu);
        return true;
    }

    void redrawWorld() {
        // has no effect
        findViewById(android.R.id.content).invalidate();
    }

    void switchToMessageView(String sz) {
        findViewById(R.id.rlList).setVisibility(View.INVISIBLE);
        ((TextView)findViewById(R.id.txMessage)).setText(sz);
        findViewById(R.id.rlMessage).setVisibility(View.VISIBLE);
        redrawWorld();
    }

    void switchToListView() {
        findViewById(R.id.rlMessage).setVisibility(View.INVISIBLE);
        findViewById(R.id.rlList).setVisibility(View.VISIBLE);
        redrawWorld();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {

            case R.id.mnuRefresh: {
                switchToMessageView("Loading apps...");
                populateAppList();
            } break;

        }

        return true;
    }

    void setupListClickHandler() {
        ListView appList = (ListView) findViewById(R.id.appList);

        // final MainActivity that = this;

        appList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);

                String title = MainActivity.this.items.get(position).get("title");
                final String packagename = MainActivity.this.items.get(position).get("data");

                builder.setTitle(title);
                builder.setItems(
                        new String[]{
                                "Intent UNINSTALL_PACKAGE", // 0
                                "Intent DELETE",            // 1
                                "pm uninstall",             // 2
                                "pm uninstall (ROOT)",      // 3
                                "Remove files (ROOT)",      // 4
                                "Cancel"                    // 5
                        },
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int which) {
                                switch (which) {
                                    case 0:
                                        MainActivity.this.Uninstall_PMRequest(packagename);
                                        break;

                                    case 1:
                                        MainActivity.this.Uninstall_PMImmediate(packagename);
                                        break;

                                    case 2:
                                        MainActivity.this.Uninstall_PMCLI(packagename);
                                        break;

                                    case 3:
                                        MainActivity.this.Uninstall_PMCLI_root(packagename);
                                        break;

                                    case 4:
                                        MainActivity.this.Uninstall_DeleteFiles(packagename);
                                        break;

                                    case 5:
                                        // Cancel
                                        break;
                                }
                            }
                        }
                );

                builder.create().show();

            }
        });
    }

    void populateAppList() {
        findViewById(android.R.id.content).postDelayed(new Runnable() {
            @Override
            public void run() {
                populateAppList__Real();
            }
        }, 100 /* anything longer than 16ms frame refresh */);
    }

    void populateAppList__Real() {
        ListView appList = (ListView) findViewById(R.id.appList);

        PackageManager pm = getPackageManager();
        items = new ArrayList<Map<String,String>>();

        for(ApplicationInfo appinfo : pm.getInstalledApplications(PackageManager.GET_META_DATA)) {
            Map<String, String> item = new HashMap<String, String>();
            item.put("title", appinfo.loadLabel(pm).toString());
            item.put("data", appinfo.packageName);
            items.add(item);
        }

        // Sort
        Collections.sort(items, new Comparator<Map<String, String>>() {
            @Override
            public int compare(Map<String, String> o1, Map<String, String> o2) {
                int c1 = o1.get("title").compareToIgnoreCase(o2.get("title"));
                if (c1 == 0) {
                    return o1.get("data").compareToIgnoreCase(o2.get("data"));
                } else {
                    return c1;
                }
            }
        });

        this.adapter = new SimpleAdapter(
            this,
            items,
            android.R.layout.simple_list_item_2,
            new String[] {"title", "data"},
            new int[] {android.R.id.text1, android.R.id.text2}
        );

        appList.setAdapter(adapter);
        adapter.notifyDataSetChanged();

        switchToListView();
    }

    public void Uninstall_PMRequest(String packagename) {
        makeToast("UNINSTALL_PACKAGE for " + packagename);
        Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
        intent.setData(Uri.parse("package:" + packagename));
        startActivity(intent);
    }

    public void Uninstall_PMImmediate(String packagename) {
        makeToast("ACTION_DELETE for " + packagename);
        Intent intent = new Intent(Intent.ACTION_DELETE);
        intent.setData(Uri.parse("package:" + packagename));
        startActivity(intent);
    }

    public void Uninstall_PMCLI(String packagename) {
        runCommand("pm uninstall " + escapeshellarg(packagename));
    }

    public void Uninstall_PMCLI_root(String packagename) {
        runCommandAsRoot("pm uninstall " + escapeshellarg(packagename));
    }

    public void Uninstall_DeleteFiles(String packagename) {
        runCommandAsRoot("rm -r /data/app/" + escapeshellarg(packagename) + "*/ ; rm -r /data/data/" + escapeshellarg(packagename) + "/ ; sync");
        makeToast("Reboot is recommended.");
    }

    public void runCommand(String shellcmd) {
        switchToMessageView("Running command, please wait...");

        Process process = null;

        try {
            process = Runtime.getRuntime().exec(shellcmd);
            process.waitFor();

        } catch (IOException e) {
            makeToast("IOException running command");
            return;
        } catch (InterruptedException e) {
            makeToast("InterruptedException running command");
            return;
        }

        // process is guaranteed non-null at this point

        int exitcode = process.exitValue();
        if (exitcode == 0) {
            makeToast("Command succeeded.");
        } else {
            makeToast("Command failed (error " + exitcode + ")");
        }

        populateAppList(); // refresh
    }

    public void runCommandAsRoot(String...strings) {
        switchToMessageView("Running command, please wait...");

        Process process = null;

        try{
            process = Runtime.getRuntime().exec("su");
            DataOutputStream outputStream = new DataOutputStream(process.getOutputStream());

            for (String s : strings) {
                outputStream.writeBytes(s+"\n");
                outputStream.flush();
            }

            outputStream.writeBytes("exit\n");
            outputStream.flush();

            process.waitFor();

            outputStream.close();

        } catch(IOException e) {
            makeToast("IOException running command");
            return;

        } catch (InterruptedException e) {
            makeToast("InterruptedException running command");
            return;

        }

        // process is guaranteed non-null at this point

        int exitcode = process.exitValue();
        if (exitcode == 0) {
            makeToast("Command succeeded.");
        } else {
            makeToast("Command failed (error " + exitcode + ")");
        }

        populateAppList(); // refresh
    }

    public static String escapeshellarg(String str) {
        return "'"+ str.replace("'", "'\"'\"'") + "'";
    }

    public void makeToast(String message) {
        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
    }

}
