package net.xndroid.utils;

import net.xndroid.AppModel;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

public class ShellUtils {
    private static final boolean USE_ROOT = true;
    private static String sBasePath;
    public static String sBusyBox;
    private static Process sProcess;
    private static OutputStreamWriter sInStream;
    private static InputStreamReader sOutStream;
    private static InputStreamReader sErrStream;
    private static int sTaskID = 0;
    private static boolean sRoot = false;
    /**
     * stderr of the last command,null if success
     * */
    static public String stdErr;


    public static boolean isRoot()
    {
        return sRoot;
    }

    private static void checkRoot() {
        sRoot = false;
        char[] buff = new char[1024];
        try {
            Process process = Runtime.getRuntime().exec("su");
            OutputStreamWriter output = new OutputStreamWriter(process.getOutputStream());
            InputStreamReader input = new InputStreamReader(process.getInputStream());
            String testStr = "ROOT_TEST";
            output.write("echo " + testStr + "\n");
            output.flush();
            output.write("exit\n");
            output.flush();
            process.waitFor();
            int count = input.read(buff);
            if (count > 0) {
                if (new String(buff, 0, count).startsWith(testStr))
                    sRoot = true;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private static void start()
    {
        if(sProcess != null)
            return;
        LogUtils.i("ShellUtils start, root=" + sRoot);
        try {
            sProcess = Runtime.getRuntime().exec(sRoot ? "su" : "sh");
            sInStream = new OutputStreamWriter(sProcess.getOutputStream());
            sOutStream = new InputStreamReader(sProcess.getInputStream());
            sErrStream = new InputStreamReader(sProcess.getErrorStream());
        } catch (IOException e) {
            stdErr = "init LogUnit fail:" + e.toString();
            LogUtils.e(stdErr, e);
            AppModel.fatalError(stdErr);
        }

        LogUtils.i("ShellUtils is ready");
    }

    public static void init(String basePath)
    {
        sBasePath = basePath;
        sBusyBox = basePath + "/busybox";
        if(USE_ROOT)
            checkRoot();
        start();
    }

    public static void close()
    {
        if(sProcess!=null) {
            try {
                sInStream.close();
                sOutStream.close();
                sErrStream.close();
            }catch(IOException e){
                e.printStackTrace();
            }
            sProcess.destroy();
            sProcess = null;
            sInStream = null;
            sOutStream = null;
            sErrStream = null;
        }

    }

    static public String exec(String cmd)
    {
        return exec(cmd, true);
    }


    //remember synchronized for multi-thread call
    static synchronized private String exec(String cmd, boolean wait)
    {
        LogUtils.i((sRoot?">>># ":">>>~ ") + cmd);
        StringBuffer strBuff = new StringBuffer();
        stdErr = null;
        String finishFlag = "~~~SHELL_TASK_"+sTaskID+"_FINISHED~~~";
        sTaskID++;
        int flagLength = finishFlag.length();
        String task = cmd+"\necho "+finishFlag+"\n";//remember '\n'
        try {
            int count ;
            sInStream.write(task);
            sInStream.flush();
            if(!wait)
                return "";
            char[] buff = new char[1024*64];
            while (true) {
                count = sOutStream.read(buff);
                if (count > 0) {
                    strBuff.append(buff, 0, count);
                    //LogUtils.defaultLogWrite("debug","<SHDEBUG>" + strBuff.toString());
                    int searchBegin = strBuff.length() - count - flagLength;
                    if(searchBegin<0)
                        searchBegin = 0;
                    if(strBuff.indexOf(finishFlag,searchBegin)>=0)
                        break;
                }
            }
            if(sErrStream.ready()) {
                count = sErrStream.read(buff);
                if (count > 0) {
                    stdErr = new String(buff, 0, count);
                    LogUtils.e("ShErr> " + stdErr);
                }
            }
            strBuff.delete(strBuff.length() - flagLength - 1,strBuff.length());
            String outStr = strBuff.toString();
            LogUtils.i("ShOut> " + outStr);
            return outStr;
        } catch (IOException e) {
            LogUtils.e("command readwrite fail:" + e.getMessage(), e);
            stdErr = "exec fail:"+e.toString();
            return "";
        }
    }

    static public String execBusybox(String cmd){
        return exec(sBusyBox + " " + cmd);
    }

}