import java.lang.Process;
import java.io.*;

public class HyperProcess extends Process implements ProcessListener
{
    volatile InputStream stdin;
    volatile OutputStream stdout;
    volatile InputStream stderr;
    volatile public static HyperLinkedList processList = new HyperLinkedList();

    volatile IOStreamHandler stderrHandler;
    volatile IOStreamHandler stdinHandler;
    volatile IOStreamExitHandler exitHandler;
    volatile Process parentProcess;
    volatile ProcessLink link;
    int id;
    volatile static int stringSpace = 512;
    volatile static int totalProcesses = 0;
    volatile static String[] processString = new String[stringSpace];
    static char fileSeparator = System.getProperty("file.separator").charAt(0);

    HyperProcess()
    {
        stdin = System.in;
        stdout = System.out;
        stderr = System.in;  // Not ideal, but makes it compile
        id = -1;
    }

    HyperProcess(String tExecString)
    {
        Process tParentProcess = null;
        try
        {
            tParentProcess = Runtime.getRuntime().exec(tExecString);
            parentProcess = tParentProcess;
        }
        catch (IOException ioex)
        {
            return;
        }
        stdin = tParentProcess.getInputStream();
        stdout = tParentProcess.getOutputStream();
        stderr = tParentProcess.getErrorStream();
        link = new ProcessLink(this);
        synchronized (processList)
        {
            processList.dlStore(link);
            processList.notifyAll();
        }
        stderrHandler = new IOStreamHandler(stderr, stdout);
        stdinHandler = new IOStreamHandler(stdin, stdout);
        exitHandler = new IOStreamExitHandler();
        stdinHandler.start();
        stderrHandler.start();
        exitHandler.start();
        stdinHandler.ioStreamTask.setName("HyperProcess.stdin");
        stderrHandler.ioStreamTask.setName("HyperProcess.stderr");
    }

    public static HyperProcess exec(String tExecString)
    {
        return new HyperProcess(tExecString);
    }

    public void setStringSpace(int tInt)
    {
        stringSpace = tInt;
        processString = new String[stringSpace];
    }

    public static String[] ps()
    {
        int tInt = 0;
        Process p;
        String[] tString = processString;
        try
        {
            if (fileSeparator == '/')
            {
                p = Runtime.getRuntime().exec("ps -e");
            }
            else
            {
                p = Runtime.getRuntime().exec(System.getenv("windir") + "\\system32\\" + "tasklist.exe");
            }
            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
            if (input == null)
            {
                System.exit(1);
            }
            while ((tString[tInt++] = input.readLine()) != null) {}
            input.close();
        }
        catch (Exception err)
        {
            err.printStackTrace();
        }
        tString[tInt] = null;
        return tString;
    }

    public void destroy()
    {
        stdinHandler.destroy();
        stderrHandler.destroy();
        synchronized (processList)
        {
            processList.dlDelete(link);
            processList.notifyAll();
        }
        synchronized (this)
        {
            notifyAll();
        }
    }

    public int exitValue()
    {
        return 1;
    }

    public InputStream getErrorStream()
    {
        return stderr;
    }

    public InputStream getInputStream()
    {
        return stdin;
    }

    public OutputStream getOutputStream()
    {
        return stdout;
    }

    public int waitFor()
    {
        synchronized (this)
        {
            try
            {
                wait();
            }
            catch (InterruptedException ie)
            {
            }
            notifyAll();
        }
        return 0;
    }

    public void onStart() {}

    public void onStop() {}

    class ProcessLink extends Linkable
    {
        HyperProcess parentProcess;

        ProcessLink(HyperProcess tProcess)
        {
            parentProcess = tProcess;
        }
    }

    class IOStreamHandler implements Runnable
    {
        volatile Thread ioStreamTask;
        volatile InputStream ios_stdin;
        volatile OutputStream ios_stdout;

        IOStreamHandler(InputStream tIn, OutputStream tOut)
        {
            ios_stdin = tIn;
            ios_stdout = tOut;
            ioStreamTask = new Thread(this);
        }

        public void start()
        {
            Thread tIOStreamTask = ioStreamTask;
            if (tIOStreamTask != null)
            {
                ioStreamTask = null;
                tIOStreamTask.interrupt();
            }
            ioStreamTask = new Thread(this);
            ioStreamTask.start();
        }

        public void stop()
        {
            Thread tIOStreamTask = ioStreamTask;
            ioStreamTask = null;
            if (tIOStreamTask != null)
            {
                tIOStreamTask.interrupt();
            }
        }

       public void run()
    {
        InputStreamReader inputStreamReader = new InputStreamReader(ios_stdin);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String line;
        int inInt=0;
        try
        {
            while (Thread.currentThread() == ioStreamTask)
            {
                // Read line from the process's stdout
                while ((line = bufferedReader.readLine()) != null)
                   System.out.println(line);
                while (ios_stdin.available() > 0 && (inInt = ios_stdin.read()) != -1)
                {
                    ios_stdout.write(inInt);
                    ios_stdout.flush();
                }
                
                // Check if the pipe is closed
                if (inInt == -1)
                {
                    System.out.println("EXEC: read -1 - The pipe is being closed");
                    break; // Exit the loop if the pipe is closed
                }
            }
        }
        catch (IOException ioex)
        {
            if (!Thread.currentThread().isInterrupted())
            {
                System.err.println("IO Error in stream handling: " + ioex.getMessage());
                ioex.printStackTrace();
            }
        }
        finally
        {
            try
            {
                ios_stdin.close();
                ios_stdout.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }

        void destroy()
        {
            Thread tThread = ioStreamTask;
            ioStreamTask = null;
            tThread.interrupt();
        }
    }

    class IOStreamExitHandler implements Runnable
    {
        Thread exitThread;

        IOStreamExitHandler() {}

        public void start()
        {
            exitThread = new Thread(this);
            exitThread.start();
        }

        public void stop()
        {
            Thread tThread = exitThread;
            exitThread = null;
            if (tThread != null)
            {
                tThread.interrupt();
            }
        }

        public void run()
        {
            if (parentProcess != null)
            {
                try
                {
                    parentProcess.waitFor();
                }
                catch (InterruptedException ie)
                {
                }
            }
            destroy();
        }
    }

    public static void main(String[] args)
    {
        int tLen, tZ = 0;
        String tStringRef = null;
        String[] tString = HyperProcess.ps();
        tLen = tString.length;
        System.out.println("\33[33m** Processes **\33[0m");
        if (tLen > 0)
        {
            while ((tStringRef = tString[tZ]) != null)
            {
                System.out.println(tString[tZ++]);
            }
        }
        else
        {
            System.out.println("no processes");
        }
        System.out.println("\33[33m/** Processes **\33[0m\r\n");
        System.out.println("\33[31mHyperProcess.exec(\33[31m"+args[0]+"\33[31m)\33[0m");
        HyperProcess.exec(args[0]);
    }
    public interface ProcessListener
    {
        public void onStart();
        public void onStop();
    }
}
