/*
 * Copyright (C) 2013 Koushik Dutta (@koush)
 *
 * 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.koushikdutta.superuser;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;

import com.koushikdutta.superuser.util.Settings;
import com.koushikdutta.superuser.util.StreamUtility;
import com.koushikdutta.superuser.util.SuHelper;
import com.koushikdutta.widgets.BetterListActivity;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class MainActivity extends BetterListActivity {
    public MainActivity() {
        super(PolicyFragment.class);
    }

    public PolicyFragmentInternal getFragment() {
        return (PolicyFragmentInternal)super.getFragment();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater mi = new MenuInflater(this);
        mi.inflate(R.menu.app, menu);
        MenuItem about = menu.findItem(R.id.about);
        about.setOnMenuItemClickListener(new OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                getFragment().setContent(new AboutFragment(), true, getString(R.string.about));
                return true;
            }
        });

        return super.onCreateOptionsMenu(menu);
    }

    private String getArch() {
        String prop = System.getProperty("os.arch");
        if (prop.contains("x86") || prop.contains("i686") || prop.contains("i386")) {
            return "x86";
        } else if (prop.contains("mips")) {
            return "mips";
        } else {
            return "armeabi";
        }
    }
    
    File extractSu() throws IOException, InterruptedException {
        ZipFile zf = new ZipFile(getPackageCodePath());
        ZipEntry su = zf.getEntry("assets/" + getArch() + "/su");
        InputStream zin = zf.getInputStream(su);
        File ret = getFileStreamPath("su");
        FileOutputStream fout = new FileOutputStream(ret);
        StreamUtility.copyStream(zin, fout);
        zin.close();
        zf.close();
        fout.close();
        return ret;
    }

    void doRecoveryInstall() {
        final ProgressDialog dlg = new ProgressDialog(this);
        dlg.setTitle(R.string.installing);
        dlg.setMessage(getString(R.string.installing_superuser));
        dlg.setIndeterminate(true);
        dlg.show();
        new Thread() {
            void doEntry(ZipOutputStream zout, String entryName, String dest) throws IOException {
                ZipFile zf = new ZipFile(getPackageCodePath());
                ZipEntry ze = zf.getEntry(entryName);
                zout.putNextEntry(new ZipEntry(dest));
                InputStream in;
                StreamUtility.copyStream(in = zf.getInputStream(ze), zout);
                zout.closeEntry();
                in.close();
                zf.close();
            }

            public void run() {
                try {
                    File zip = getFileStreamPath("superuser.zip");
                    ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(zip));
                    doEntry(zout, "assets/update-binary", "META-INF/com/google/android/update-binary");
                    doEntry(zout, "assets/install-recovery.sh", "install-recovery.sh");
                    zout.close();

                    ZipFile zf = new ZipFile(getPackageCodePath());
                    ZipEntry ze = zf.getEntry("assets/" + getArch() + "/reboot");
                    InputStream in;
                    FileOutputStream reboot;
                    StreamUtility.copyStream(in = zf.getInputStream(ze), reboot = openFileOutput("reboot", MODE_PRIVATE));
                    reboot.close();
                    in.close();

                    final File su = extractSu();

                    String command =
                            String.format("cat %s > /cache/superuser.zip\n", zip.getAbsolutePath()) +
                            String.format("cat %s > /cache/su\n", su.getAbsolutePath()) +
                            String.format("cat %s > /cache/Superuser.apk\n", getPackageCodePath()) +
                            "mkdir /cache/recovery\n" +
                            "echo '--update_package=CACHE:superuser.zip' > /cache/recovery/command\n" +
                            "chmod 644 /cache/superuser.zip\n" +
                            "chmod 644 /cache/recovery/command\n" +
                            "sync\n" +
                            String.format("chmod 755 %s\n", getFileStreamPath("reboot").getAbsolutePath()) +
                            "reboot recovery\n";
                    Process p = Runtime.getRuntime().exec("su");
                    p.getOutputStream().write(command.getBytes());
                    p.getOutputStream().close();
                    File rebootScript = getFileStreamPath("reboot.sh");
                    StreamUtility.writeFile(rebootScript, "reboot recovery ; " + getFileStreamPath("reboot").getAbsolutePath() + " recovery ;");
                    p.waitFor();
                    Runtime.getRuntime().exec(new String[] { "su", "-c", ". " + rebootScript.getAbsolutePath() });
                    if (p.waitFor() != 0)
                        throw new Exception("non zero result");
                }
                catch (Exception ex) {
                    ex.printStackTrace();
                    dlg.dismiss();

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                            builder.setPositiveButton(android.R.string.ok, null);
                            builder.setTitle(R.string.install);
                            builder.setMessage(R.string.install_error);
                            builder.create().show();
                        }
                    });
                }
            }
        }.start();
    }

    void doSystemInstall() {
        final ProgressDialog dlg = new ProgressDialog(this);
        dlg.setTitle(R.string.installing);
        dlg.setMessage(getString(R.string.installing_superuser));
        dlg.setIndeterminate(true);
        dlg.show();
        new Thread() {
            public void run() {
                boolean _error = false;
                try {
                    final File su = extractSu();
                    final String command =
                            "mount -orw,remount /system\n" +
                            "rm /system/xbin/su\n" +
                            "rm /system/bin/su\n" +
                            "rm /system/app/Supersu.*\n" +
                            "rm /system/app/superuser.*\n" +
                            "rm /system/app/supersu.*\n" +
                            "rm /system/app/SuperUser.*\n" +
                            "rm /system/app/SuperSU.*\n" +
                            String.format("cat %s > /system/xbin/su\n", su.getAbsolutePath()) +
                            "chmod 6755 /system/xbin/su\n" +
                            "ln -s /system/xbin/su /system/bin/su\n" +
                            "mount -oro,remount /system\n" +
                            "sync\n";
                    Process p = Runtime.getRuntime().exec("su");
                    p.getOutputStream().write(command.getBytes());
                    p.getOutputStream().close();
                    if (p.waitFor() != 0)
                        throw new Exception("non zero result");
                    SuHelper.checkSu(MainActivity.this);
                }
                catch (Exception ex) {
                    _error = true;
                    Log.e("Superuser", "error upgrading", ex);
                }
                dlg.dismiss();
                final boolean error = _error;
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                        builder.setPositiveButton(android.R.string.ok, null);
                        builder.setTitle(R.string.install);

                        if (error) {
                            builder.setMessage(R.string.install_error);
                        }
                        else {
                            builder.setMessage(R.string.install_success);
                        }
                        builder.create().show();
                    }
                });
            };
        }.start();
    }

    void doInstall() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.install);
        builder.setMessage(R.string.install_superuser_info);
        if (Build.VERSION.SDK_INT < 18) {
            builder.setPositiveButton(R.string.install, new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    doSystemInstall();
                }
            });
        }
        builder.setNegativeButton(android.R.string.cancel, null);
        builder.setNeutralButton(R.string.recovery_install, new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                doRecoveryInstall();
            }
        });
        builder.create().show();
    }

    private void saveWhatsNew() {
        Settings.setString(this, "whats_new", WHATS_NEW);
    }

    // this is intentionally not localized as it will change constantly.
    private static final String WHATS_NEW = "Added support for Android 4.3.";
    protected void doWhatsNew() {
        if (WHATS_NEW.equals(Settings.getString(this, "whats_new")))
            return;
        saveWhatsNew();
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.whats_new);
        builder.setIcon(R.drawable.ic_launcher);
        builder.setMessage(WHATS_NEW);
        builder.setPositiveButton(R.string.rate, new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent i = new Intent();
                i.setData(Uri.parse("market://details?id=com.koushikdutta.superuser"));
                startActivity(i);
            }
        });
        builder.setNegativeButton(android.R.string.cancel, null);
        builder.create().show();
    }

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

        if (Settings.getBoolean(this, "first_run", true)) {
            saveWhatsNew();
            Settings.setBoolean(this, "first_run", false);
        }

        final ProgressDialog dlg = new ProgressDialog(this);
        dlg.setTitle(R.string.superuser);
        dlg.setMessage(getString(R.string.checking_superuser));
        dlg.setIndeterminate(true);
        dlg.show();
        new Thread() {
            public void run() {
                boolean _error = false;
                try {
                    SuHelper.checkSu(MainActivity.this);
                }
                catch (Exception e) {
                    e.printStackTrace();
                    _error = true;
                }
                final boolean error = _error;
                dlg.dismiss();
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (error) {
                            doInstall();
                        }
                        else {
                            doWhatsNew();
                        }
                    }
                });
            };
        }.start();
    }
}
