// 
// Decompiled by Procyon v0.6.0
// 

package org.jline.terminal.impl.exec;

import org.jline.nativ.JLineLibrary;
import org.jline.nativ.JLineNativeLoader;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;
import org.jline.terminal.TerminalBuilder;
import org.jline.utils.ExecHelper;
import java.io.FileDescriptor;
import org.jline.utils.Log;
import org.jline.terminal.impl.ExternalTerminal;
import org.jline.terminal.Size;
import org.jline.terminal.Attributes;
import java.io.OutputStream;
import java.io.InputStream;
import org.jline.terminal.impl.PosixSysTerminal;
import org.jline.utils.OSUtils;
import org.jline.terminal.Terminal;
import java.nio.charset.Charset;
import java.io.IOException;
import org.jline.terminal.spi.Pty;
import org.jline.terminal.spi.SystemStream;
import org.jline.terminal.spi.TerminalProvider;

public class ExecTerminalProvider implements TerminalProvider
{
    private static boolean warned;
    private static RedirectPipeCreator redirectPipeCreator;
    
    @Override
    public String name() {
        return "exec";
    }
    
    public Pty current(final SystemStream systemStream) throws IOException {
        if (!this.isSystemStream(systemStream)) {
            throw new IOException("Not a system stream: " + systemStream);
        }
        return ExecPty.current(this, systemStream);
    }
    
    @Override
    public Terminal sysTerminal(final String name, final String type, final boolean ansiPassThrough, final Charset encoding, final Charset inputEncoding, final Charset outputEncoding, final boolean nativeSignals, final Terminal.SignalHandler signalHandler, final boolean paused, final SystemStream systemStream) throws IOException {
        if (OSUtils.IS_WINDOWS) {
            return this.winSysTerminal(name, type, ansiPassThrough, encoding, inputEncoding, outputEncoding, outputEncoding, nativeSignals, signalHandler, paused, systemStream);
        }
        return this.posixSysTerminal(name, type, ansiPassThrough, encoding, inputEncoding, outputEncoding, outputEncoding, nativeSignals, signalHandler, paused, systemStream);
    }
    
    @Deprecated
    @Override
    public Terminal sysTerminal(final String name, final String type, final boolean ansiPassThrough, final Charset encoding, final Charset stdinEncoding, final Charset stdoutEncoding, final Charset stderrEncoding, final boolean nativeSignals, final Terminal.SignalHandler signalHandler, final boolean paused, final SystemStream systemStream) throws IOException {
        if (OSUtils.IS_WINDOWS) {
            return this.winSysTerminal(name, type, ansiPassThrough, encoding, stdinEncoding, stdoutEncoding, stderrEncoding, nativeSignals, signalHandler, paused, systemStream);
        }
        return this.posixSysTerminal(name, type, ansiPassThrough, encoding, stdinEncoding, stdoutEncoding, stderrEncoding, nativeSignals, signalHandler, paused, systemStream);
    }
    
    public Terminal winSysTerminal(final String name, final String type, final boolean ansiPassThrough, final Charset encoding, final boolean nativeSignals, final Terminal.SignalHandler signalHandler, final boolean paused, final SystemStream systemStream) throws IOException {
        return this.winSysTerminal(name, type, ansiPassThrough, encoding, encoding, encoding, encoding, nativeSignals, signalHandler, paused, systemStream);
    }
    
    public Terminal winSysTerminal(final String name, final String type, final boolean ansiPassThrough, final Charset encoding, final Charset stdinEncoding, final Charset stdoutEncoding, final Charset stderrEncoding, final boolean nativeSignals, final Terminal.SignalHandler signalHandler, final boolean paused, final SystemStream systemStream) throws IOException {
        if (OSUtils.IS_CYGWIN || OSUtils.IS_MSYSTEM) {
            final Pty pty = this.current(systemStream);
            final Charset outputEncoding = (systemStream == SystemStream.Error) ? stderrEncoding : stdoutEncoding;
            return new PosixSysTerminal(name, type, pty, encoding, stdinEncoding, outputEncoding, nativeSignals, signalHandler);
        }
        return null;
    }
    
    public Terminal posixSysTerminal(final String name, final String type, final boolean ansiPassThrough, final Charset encoding, final boolean nativeSignals, final Terminal.SignalHandler signalHandler, final boolean paused, final SystemStream systemStream) throws IOException {
        return this.posixSysTerminal(name, type, ansiPassThrough, encoding, encoding, encoding, encoding, nativeSignals, signalHandler, paused, systemStream);
    }
    
    public Terminal posixSysTerminal(final String name, final String type, final boolean ansiPassThrough, final Charset encoding, final Charset stdinEncoding, final Charset stdoutEncoding, final Charset stderrEncoding, final boolean nativeSignals, final Terminal.SignalHandler signalHandler, final boolean paused, final SystemStream systemStream) throws IOException {
        final Pty pty = this.current(systemStream);
        final Charset outputEncoding = (systemStream == SystemStream.Error) ? stderrEncoding : stdoutEncoding;
        return new PosixSysTerminal(name, type, pty, encoding, stdinEncoding, outputEncoding, nativeSignals, signalHandler);
    }
    
    @Override
    public Terminal newTerminal(final String name, final String type, final InputStream in, final OutputStream out, final Charset encoding, final Charset inputEncoding, final Charset outputEncoding, final Terminal.SignalHandler signalHandler, final boolean paused, final Attributes attributes, final Size size) throws IOException {
        return new ExternalTerminal(this, name, type, in, out, encoding, inputEncoding, outputEncoding, signalHandler, paused, attributes, size);
    }
    
    @Deprecated
    @Override
    public Terminal newTerminal(final String name, final String type, final InputStream in, final OutputStream out, final Charset encoding, final Charset stdinEncoding, final Charset stdoutEncoding, final Charset stderrEncoding, final Terminal.SignalHandler signalHandler, final boolean paused, final Attributes attributes, final Size size) throws IOException {
        return new ExternalTerminal(this, name, type, in, out, encoding, stdinEncoding, stdoutEncoding, signalHandler, paused, attributes, size);
    }
    
    @Override
    public boolean isSystemStream(final SystemStream stream) {
        try {
            return this.isPosixSystemStream(stream) || this.isWindowsSystemStream(stream);
        }
        catch (final Throwable t) {
            return false;
        }
    }
    
    public boolean isWindowsSystemStream(final SystemStream stream) {
        return this.systemStreamName(stream) != null;
    }
    
    public boolean isPosixSystemStream(final SystemStream stream) {
        try {
            final Process p = new ProcessBuilder(new String[] { OSUtils.TEST_COMMAND, "-t", Integer.toString(stream.ordinal()) }).inheritIO().start();
            return p.waitFor() == 0;
        }
        catch (final Throwable t) {
            Log.debug("ExecTerminalProvider failed 'test -t' for " + stream, t);
            return false;
        }
    }
    
    @Override
    public String systemStreamName(final SystemStream stream) {
        try {
            final ProcessBuilder.Redirect input = (stream == SystemStream.Input) ? ProcessBuilder.Redirect.INHERIT : newDescriptor((stream == SystemStream.Output) ? FileDescriptor.out : FileDescriptor.err);
            final Process p = new ProcessBuilder(new String[] { OSUtils.TTY_COMMAND }).redirectInput(input).start();
            final String result = ExecHelper.waitAndCapture(p);
            if (p.exitValue() == 0) {
                return result.trim();
            }
        }
        catch (final Throwable t) {
            if ("java.lang.reflect.InaccessibleObjectException".equals(t.getClass().getName()) && !ExecTerminalProvider.warned) {
                Log.warn("The ExecTerminalProvider requires the JVM options: '--add-opens java.base/java.lang=ALL-UNNAMED'");
                ExecTerminalProvider.warned = true;
            }
        }
        return null;
    }
    
    @Override
    public int systemStreamWidth(final SystemStream stream) {
        try (final ExecPty pty = new ExecPty(this, stream, null)) {
            return pty.getSize().getColumns();
        }
        catch (final Throwable t) {
            return -1;
        }
    }
    
    protected static ProcessBuilder.Redirect newDescriptor(final FileDescriptor fd) {
        if (ExecTerminalProvider.redirectPipeCreator == null) {
            final String str = System.getProperty("org.jline.terminal.exec.redirectPipeCreationMode", TerminalBuilder.PROP_REDIRECT_PIPE_CREATION_MODE_DEFAULT);
            final String[] modes = str.split(",");
            final IllegalStateException ise = new IllegalStateException("Unable to create RedirectPipe");
            for (final String mode : modes) {
                try {
                    final String s = mode;
                    switch (s) {
                        case "native": {
                            ExecTerminalProvider.redirectPipeCreator = new NativeRedirectPipeCreator();
                            break;
                        }
                        case "reflection": {
                            ExecTerminalProvider.redirectPipeCreator = new ReflectionRedirectPipeCreator();
                            break;
                        }
                    }
                }
                catch (final Throwable t) {
                    ise.addSuppressed(t);
                }
                if (ExecTerminalProvider.redirectPipeCreator != null) {
                    break;
                }
            }
            if (ExecTerminalProvider.redirectPipeCreator == null) {
                throw ise;
            }
        }
        return ExecTerminalProvider.redirectPipeCreator.newRedirectPipe(fd);
    }
    
    @Override
    public String toString() {
        return "TerminalProvider[" + this.name() + "]";
    }
    
    static class ReflectionRedirectPipeCreator implements RedirectPipeCreator
    {
        private final Constructor<ProcessBuilder.Redirect> constructor;
        private final Field fdField;
        
        ReflectionRedirectPipeCreator() throws Exception {
            final Class<?> rpi = Class.forName("java.lang.ProcessBuilder$RedirectPipeImpl");
            (this.constructor = (Constructor<ProcessBuilder.Redirect>)rpi.getDeclaredConstructor((Class<?>[])new Class[0])).setAccessible(true);
            (this.fdField = rpi.getDeclaredField("fd")).setAccessible(true);
        }
        
        @Override
        public ProcessBuilder.Redirect newRedirectPipe(final FileDescriptor fd) {
            try {
                final ProcessBuilder.Redirect input = this.constructor.newInstance(new Object[0]);
                this.fdField.set(input, fd);
                return input;
            }
            catch (final ReflectiveOperationException e) {
                throw new IllegalStateException(e);
            }
        }
    }
    
    static class NativeRedirectPipeCreator implements RedirectPipeCreator
    {
        public NativeRedirectPipeCreator() {
            JLineNativeLoader.initialize();
        }
        
        @Override
        public ProcessBuilder.Redirect newRedirectPipe(final FileDescriptor fd) {
            return JLineLibrary.newRedirectPipe(fd);
        }
    }
    
    interface RedirectPipeCreator
    {
        ProcessBuilder.Redirect newRedirectPipe(final FileDescriptor p0);
    }
}
