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

package org.jline.jansi;

import java.io.IOException;
import java.util.Collection;
import java.util.ArrayList;
import java.util.concurrent.Callable;

public class Ansi implements Appendable
{
    private static final char FIRST_ESC_CHAR = '\u001b';
    private static final char SECOND_ESC_CHAR = '[';
    public static final String DISABLE;
    private static Callable<Boolean> detector;
    private static final InheritableThreadLocal<Boolean> holder;
    private final StringBuilder builder;
    private final ArrayList<Integer> attributeOptions;
    
    public static void setDetector(final Callable<Boolean> detector) {
        if (detector == null) {
            throw new IllegalArgumentException();
        }
        Ansi.detector = detector;
    }
    
    public static boolean isDetected() {
        try {
            return Ansi.detector.call();
        }
        catch (final Exception e) {
            return true;
        }
    }
    
    public static void setEnabled(final boolean flag) {
        Ansi.holder.set(flag);
    }
    
    public static boolean isEnabled() {
        return Ansi.holder.get();
    }
    
    public static Ansi ansi() {
        if (isEnabled()) {
            return new Ansi();
        }
        return new NoAnsi();
    }
    
    public static Ansi ansi(final StringBuilder builder) {
        if (isEnabled()) {
            return new Ansi(builder);
        }
        return new NoAnsi(builder);
    }
    
    public static Ansi ansi(final int size) {
        if (isEnabled()) {
            return new Ansi(size);
        }
        return new NoAnsi(size);
    }
    
    public Ansi() {
        this(new StringBuilder(80));
    }
    
    public Ansi(final Ansi parent) {
        this(new StringBuilder(parent.builder));
        this.attributeOptions.addAll(parent.attributeOptions);
    }
    
    public Ansi(final int size) {
        this(new StringBuilder(size));
    }
    
    public Ansi(final StringBuilder builder) {
        this.attributeOptions = new ArrayList<Integer>(5);
        this.builder = builder;
    }
    
    public Ansi fg(final Color color) {
        this.attributeOptions.add(color.fg());
        return this;
    }
    
    public Ansi fg(final int color) {
        this.attributeOptions.add(38);
        this.attributeOptions.add(5);
        this.attributeOptions.add(color & 0xFF);
        return this;
    }
    
    public Ansi fgRgb(String hex) {
        if (hex.startsWith("#")) {
            hex = hex.substring(1);
        }
        return this.fgRgb(Integer.parseInt(hex, 16));
    }
    
    public Ansi fgRgb(final int color) {
        return this.fgRgb(color >> 16, color >> 8, color);
    }
    
    public Ansi fgRgb(final int r, final int g, final int b) {
        this.attributeOptions.add(38);
        this.attributeOptions.add(2);
        this.attributeOptions.add(r & 0xFF);
        this.attributeOptions.add(g & 0xFF);
        this.attributeOptions.add(b & 0xFF);
        return this;
    }
    
    public Ansi fgBlack() {
        return this.fg(Color.BLACK);
    }
    
    public Ansi fgBlue() {
        return this.fg(Color.BLUE);
    }
    
    public Ansi fgCyan() {
        return this.fg(Color.CYAN);
    }
    
    public Ansi fgDefault() {
        return this.fg(Color.DEFAULT);
    }
    
    public Ansi fgGreen() {
        return this.fg(Color.GREEN);
    }
    
    public Ansi fgMagenta() {
        return this.fg(Color.MAGENTA);
    }
    
    public Ansi fgRed() {
        return this.fg(Color.RED);
    }
    
    public Ansi fgYellow() {
        return this.fg(Color.YELLOW);
    }
    
    public Ansi bg(final Color color) {
        this.attributeOptions.add(color.bg());
        return this;
    }
    
    public Ansi bg(final int color) {
        this.attributeOptions.add(48);
        this.attributeOptions.add(5);
        this.attributeOptions.add(color & 0xFF);
        return this;
    }
    
    public Ansi bgRgb(String hex) {
        if (hex.startsWith("#")) {
            hex = hex.substring(1);
        }
        return this.bgRgb(Integer.parseInt(hex, 16));
    }
    
    public Ansi bgRgb(final int color) {
        return this.bgRgb(color >> 16, color >> 8, color);
    }
    
    public Ansi bgRgb(final int r, final int g, final int b) {
        this.attributeOptions.add(48);
        this.attributeOptions.add(2);
        this.attributeOptions.add(r & 0xFF);
        this.attributeOptions.add(g & 0xFF);
        this.attributeOptions.add(b & 0xFF);
        return this;
    }
    
    public Ansi bgCyan() {
        return this.bg(Color.CYAN);
    }
    
    public Ansi bgDefault() {
        return this.bg(Color.DEFAULT);
    }
    
    public Ansi bgGreen() {
        return this.bg(Color.GREEN);
    }
    
    public Ansi bgMagenta() {
        return this.bg(Color.MAGENTA);
    }
    
    public Ansi bgRed() {
        return this.bg(Color.RED);
    }
    
    public Ansi bgYellow() {
        return this.bg(Color.YELLOW);
    }
    
    public Ansi fgBright(final Color color) {
        this.attributeOptions.add(color.fgBright());
        return this;
    }
    
    public Ansi fgBrightBlack() {
        return this.fgBright(Color.BLACK);
    }
    
    public Ansi fgBrightBlue() {
        return this.fgBright(Color.BLUE);
    }
    
    public Ansi fgBrightCyan() {
        return this.fgBright(Color.CYAN);
    }
    
    public Ansi fgBrightDefault() {
        return this.fgBright(Color.DEFAULT);
    }
    
    public Ansi fgBrightGreen() {
        return this.fgBright(Color.GREEN);
    }
    
    public Ansi fgBrightMagenta() {
        return this.fgBright(Color.MAGENTA);
    }
    
    public Ansi fgBrightRed() {
        return this.fgBright(Color.RED);
    }
    
    public Ansi fgBrightYellow() {
        return this.fgBright(Color.YELLOW);
    }
    
    public Ansi bgBright(final Color color) {
        this.attributeOptions.add(color.bgBright());
        return this;
    }
    
    public Ansi bgBrightCyan() {
        return this.bgBright(Color.CYAN);
    }
    
    public Ansi bgBrightDefault() {
        return this.bgBright(Color.DEFAULT);
    }
    
    public Ansi bgBrightGreen() {
        return this.bgBright(Color.GREEN);
    }
    
    public Ansi bgBrightMagenta() {
        return this.bgBright(Color.MAGENTA);
    }
    
    public Ansi bgBrightRed() {
        return this.bgBright(Color.RED);
    }
    
    public Ansi bgBrightYellow() {
        return this.bgBright(Color.YELLOW);
    }
    
    public Ansi a(final Attribute attribute) {
        this.attributeOptions.add(attribute.value());
        return this;
    }
    
    public Ansi cursor(final int row, final int column) {
        return this.appendEscapeSequence('H', Math.max(1, row), Math.max(1, column));
    }
    
    public Ansi cursorToColumn(final int x) {
        return this.appendEscapeSequence('G', Math.max(1, x));
    }
    
    public Ansi cursorUp(final int y) {
        return (y > 0) ? this.appendEscapeSequence('A', y) : ((y < 0) ? this.cursorDown(-y) : this);
    }
    
    public Ansi cursorDown(final int y) {
        return (y > 0) ? this.appendEscapeSequence('B', y) : ((y < 0) ? this.cursorUp(-y) : this);
    }
    
    public Ansi cursorRight(final int x) {
        return (x > 0) ? this.appendEscapeSequence('C', x) : ((x < 0) ? this.cursorLeft(-x) : this);
    }
    
    public Ansi cursorLeft(final int x) {
        return (x > 0) ? this.appendEscapeSequence('D', x) : ((x < 0) ? this.cursorRight(-x) : this);
    }
    
    public Ansi cursorMove(final int x, final int y) {
        return this.cursorRight(x).cursorDown(y);
    }
    
    public Ansi cursorDownLine() {
        return this.appendEscapeSequence('E');
    }
    
    public Ansi cursorDownLine(final int n) {
        return (n < 0) ? this.cursorUpLine(-n) : this.appendEscapeSequence('E', n);
    }
    
    public Ansi cursorUpLine() {
        return this.appendEscapeSequence('F');
    }
    
    public Ansi cursorUpLine(final int n) {
        return (n < 0) ? this.cursorDownLine(-n) : this.appendEscapeSequence('F', n);
    }
    
    public Ansi eraseScreen() {
        return this.appendEscapeSequence('J', Erase.ALL.value());
    }
    
    public Ansi eraseScreen(final Erase kind) {
        return this.appendEscapeSequence('J', kind.value());
    }
    
    public Ansi eraseLine() {
        return this.appendEscapeSequence('K');
    }
    
    public Ansi eraseLine(final Erase kind) {
        return this.appendEscapeSequence('K', kind.value());
    }
    
    public Ansi scrollUp(final int rows) {
        if (rows == Integer.MIN_VALUE) {
            return this.scrollDown(Integer.MAX_VALUE);
        }
        return (rows > 0) ? this.appendEscapeSequence('S', rows) : ((rows < 0) ? this.scrollDown(-rows) : this);
    }
    
    public Ansi scrollDown(final int rows) {
        if (rows == Integer.MIN_VALUE) {
            return this.scrollUp(Integer.MAX_VALUE);
        }
        return (rows > 0) ? this.appendEscapeSequence('T', rows) : ((rows < 0) ? this.scrollUp(-rows) : this);
    }
    
    public Ansi saveCursorPosition() {
        this.saveCursorPositionSCO();
        return this.saveCursorPositionDEC();
    }
    
    public Ansi saveCursorPositionSCO() {
        return this.appendEscapeSequence('s');
    }
    
    public Ansi saveCursorPositionDEC() {
        this.builder.append('\u001b');
        this.builder.append('7');
        return this;
    }
    
    public Ansi restoreCursorPosition() {
        this.restoreCursorPositionSCO();
        return this.restoreCursorPositionDEC();
    }
    
    public Ansi restoreCursorPositionSCO() {
        return this.appendEscapeSequence('u');
    }
    
    public Ansi restoreCursorPositionDEC() {
        this.builder.append('\u001b');
        this.builder.append('8');
        return this;
    }
    
    public Ansi reset() {
        return this.a(Attribute.RESET);
    }
    
    public Ansi bold() {
        return this.a(Attribute.INTENSITY_BOLD);
    }
    
    public Ansi boldOff() {
        return this.a(Attribute.INTENSITY_BOLD_OFF);
    }
    
    public Ansi a(final String value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final boolean value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final char value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final char[] value, final int offset, final int len) {
        this.flushAttributes();
        this.builder.append(value, offset, len);
        return this;
    }
    
    public Ansi a(final char[] value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final CharSequence value, final int start, final int end) {
        this.flushAttributes();
        this.builder.append(value, start, end);
        return this;
    }
    
    public Ansi a(final CharSequence value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final double value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final float value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final int value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final long value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final Object value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi a(final StringBuffer value) {
        this.flushAttributes();
        this.builder.append(value);
        return this;
    }
    
    public Ansi newline() {
        this.flushAttributes();
        this.builder.append(System.getProperty("line.separator"));
        return this;
    }
    
    public Ansi format(final String pattern, final Object... args) {
        this.flushAttributes();
        this.builder.append(String.format(pattern, args));
        return this;
    }
    
    public Ansi apply(final Consumer fun) {
        fun.apply(this);
        return this;
    }
    
    public Ansi render(final String text) {
        this.a(AnsiRenderer.render(text));
        return this;
    }
    
    public Ansi render(final String text, final Object... args) {
        this.a(String.format(AnsiRenderer.render(text), args));
        return this;
    }
    
    @Override
    public String toString() {
        this.flushAttributes();
        return this.builder.toString();
    }
    
    private Ansi appendEscapeSequence(final char command) {
        this.flushAttributes();
        this.builder.append('\u001b');
        this.builder.append('[');
        this.builder.append(command);
        return this;
    }
    
    private Ansi appendEscapeSequence(final char command, final int option) {
        this.flushAttributes();
        this.builder.append('\u001b');
        this.builder.append('[');
        this.builder.append(option);
        this.builder.append(command);
        return this;
    }
    
    private Ansi appendEscapeSequence(final char command, final Object... options) {
        this.flushAttributes();
        return this._appendEscapeSequence(command, options);
    }
    
    private void flushAttributes() {
        if (this.attributeOptions.isEmpty()) {
            return;
        }
        if (this.attributeOptions.size() == 1 && this.attributeOptions.get(0) == 0) {
            this.builder.append('\u001b');
            this.builder.append('[');
            this.builder.append('m');
        }
        else {
            this._appendEscapeSequence('m', this.attributeOptions.toArray());
        }
        this.attributeOptions.clear();
    }
    
    private Ansi _appendEscapeSequence(final char command, final Object... options) {
        this.builder.append('\u001b');
        this.builder.append('[');
        for (int size = options.length, i = 0; i < size; ++i) {
            if (i != 0) {
                this.builder.append(';');
            }
            if (options[i] != null) {
                this.builder.append(options[i]);
            }
        }
        this.builder.append(command);
        return this;
    }
    
    @Override
    public Ansi append(final CharSequence csq) {
        this.builder.append(csq);
        return this;
    }
    
    @Override
    public Ansi append(final CharSequence csq, final int start, final int end) {
        this.builder.append(csq, start, end);
        return this;
    }
    
    @Override
    public Ansi append(final char c) {
        this.builder.append(c);
        return this;
    }
    
    static {
        DISABLE = Ansi.class.getName() + ".disable";
        Ansi.detector = (() -> !Boolean.getBoolean(Ansi.DISABLE));
        holder = new InheritableThreadLocal<Boolean>() {
            @Override
            protected Boolean initialValue() {
                return Ansi.isDetected();
            }
        };
    }
    
    public enum Color
    {
        BLACK(0, "BLACK"), 
        RED(1, "RED"), 
        GREEN(2, "GREEN"), 
        YELLOW(3, "YELLOW"), 
        BLUE(4, "BLUE"), 
        MAGENTA(5, "MAGENTA"), 
        CYAN(6, "CYAN"), 
        WHITE(7, "WHITE"), 
        DEFAULT(9, "DEFAULT");
        
        private final int value;
        private final String name;
        
        private Color(final int index, final String name) {
            this.value = index;
            this.name = name;
        }
        
        @Override
        public String toString() {
            return this.name;
        }
        
        public int value() {
            return this.value;
        }
        
        public int fg() {
            return this.value + 30;
        }
        
        public int bg() {
            return this.value + 40;
        }
        
        public int fgBright() {
            return this.value + 90;
        }
        
        public int bgBright() {
            return this.value + 100;
        }
    }
    
    public enum Attribute
    {
        RESET(0, "RESET"), 
        INTENSITY_BOLD(1, "INTENSITY_BOLD"), 
        INTENSITY_FAINT(2, "INTENSITY_FAINT"), 
        ITALIC(3, "ITALIC_ON"), 
        UNDERLINE(4, "UNDERLINE_ON"), 
        BLINK_SLOW(5, "BLINK_SLOW"), 
        BLINK_FAST(6, "BLINK_FAST"), 
        NEGATIVE_ON(7, "NEGATIVE_ON"), 
        CONCEAL_ON(8, "CONCEAL_ON"), 
        STRIKETHROUGH_ON(9, "STRIKETHROUGH_ON"), 
        UNDERLINE_DOUBLE(21, "UNDERLINE_DOUBLE"), 
        INTENSITY_BOLD_OFF(22, "INTENSITY_BOLD_OFF"), 
        ITALIC_OFF(23, "ITALIC_OFF"), 
        UNDERLINE_OFF(24, "UNDERLINE_OFF"), 
        BLINK_OFF(25, "BLINK_OFF"), 
        NEGATIVE_OFF(27, "NEGATIVE_OFF"), 
        CONCEAL_OFF(28, "CONCEAL_OFF"), 
        STRIKETHROUGH_OFF(29, "STRIKETHROUGH_OFF");
        
        private final int value;
        private final String name;
        
        private Attribute(final int index, final String name) {
            this.value = index;
            this.name = name;
        }
        
        @Override
        public String toString() {
            return this.name;
        }
        
        public int value() {
            return this.value;
        }
    }
    
    public enum Erase
    {
        FORWARD(0, "FORWARD"), 
        BACKWARD(1, "BACKWARD"), 
        ALL(2, "ALL");
        
        private final int value;
        private final String name;
        
        private Erase(final int index, final String name) {
            this.value = index;
            this.name = name;
        }
        
        @Override
        public String toString() {
            return this.name;
        }
        
        public int value() {
            return this.value;
        }
    }
    
    private static class NoAnsi extends Ansi
    {
        public NoAnsi() {
        }
        
        public NoAnsi(final int size) {
            super(size);
        }
        
        public NoAnsi(final StringBuilder builder) {
            super(builder);
        }
        
        @Override
        public Ansi fg(final Color color) {
            return this;
        }
        
        @Override
        public Ansi bg(final Color color) {
            return this;
        }
        
        @Override
        public Ansi fgBright(final Color color) {
            return this;
        }
        
        @Override
        public Ansi bgBright(final Color color) {
            return this;
        }
        
        @Override
        public Ansi fg(final int color) {
            return this;
        }
        
        @Override
        public Ansi fgRgb(final int r, final int g, final int b) {
            return this;
        }
        
        @Override
        public Ansi bg(final int color) {
            return this;
        }
        
        @Override
        public Ansi bgRgb(final int r, final int g, final int b) {
            return this;
        }
        
        @Override
        public Ansi a(final Attribute attribute) {
            return this;
        }
        
        @Override
        public Ansi cursor(final int row, final int column) {
            return this;
        }
        
        @Override
        public Ansi cursorToColumn(final int x) {
            return this;
        }
        
        @Override
        public Ansi cursorUp(final int y) {
            return this;
        }
        
        @Override
        public Ansi cursorRight(final int x) {
            return this;
        }
        
        @Override
        public Ansi cursorDown(final int y) {
            return this;
        }
        
        @Override
        public Ansi cursorLeft(final int x) {
            return this;
        }
        
        @Override
        public Ansi cursorDownLine() {
            return this;
        }
        
        @Override
        public Ansi cursorDownLine(final int n) {
            return this;
        }
        
        @Override
        public Ansi cursorUpLine() {
            return this;
        }
        
        @Override
        public Ansi cursorUpLine(final int n) {
            return this;
        }
        
        @Override
        public Ansi eraseScreen() {
            return this;
        }
        
        @Override
        public Ansi eraseScreen(final Erase kind) {
            return this;
        }
        
        @Override
        public Ansi eraseLine() {
            return this;
        }
        
        @Override
        public Ansi eraseLine(final Erase kind) {
            return this;
        }
        
        @Override
        public Ansi scrollUp(final int rows) {
            return this;
        }
        
        @Override
        public Ansi scrollDown(final int rows) {
            return this;
        }
        
        @Override
        public Ansi saveCursorPosition() {
            return this;
        }
        
        @Override
        public Ansi restoreCursorPosition() {
            return this;
        }
        
        @Override
        public Ansi reset() {
            return this;
        }
    }
    
    @FunctionalInterface
    public interface Consumer
    {
        void apply(final Ansi p0);
    }
}
