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

package org.jline.console.impl;

import org.jline.utils.AttributedCharSequence;
import java.nio.file.LinkOption;
import org.jline.utils.OSUtils;
import java.io.File;
import java.nio.file.Paths;
import org.jline.reader.ParsedLine;
import org.jline.utils.AttributedStringBuilder;
import org.jline.reader.Parser;
import java.util.Collection;
import org.jline.utils.AttributedString;
import java.io.BufferedReader;
import java.util.Iterator;
import java.nio.file.Path;
import java.io.IOException;
import org.jline.utils.Log;
import java.nio.file.Files;
import org.jline.reader.LineReader;
import java.util.ArrayList;
import java.util.HashMap;
import org.jline.builtins.Styles;
import java.util.function.Supplier;
import java.util.List;
import java.util.Map;
import org.jline.console.SystemRegistry;
import org.jline.builtins.SyntaxHighlighter;
import org.jline.utils.StyleResolver;
import org.jline.reader.impl.DefaultHighlighter;

public class SystemHighlighter extends DefaultHighlighter
{
    private StyleResolver resolver;
    private static final String REGEX_COMMENT_LINE = "\\s*#.*";
    private static final String READER_COLORS = "READER_COLORS";
    protected final SyntaxHighlighter commandHighlighter;
    protected final SyntaxHighlighter argsHighlighter;
    protected final SyntaxHighlighter langHighlighter;
    protected final SystemRegistry systemRegistry;
    protected final Map<String, FileHighlightCommand> fileHighlight;
    protected final Map<String, SyntaxHighlighter> specificHighlighter;
    protected int commandIndex;
    private final List<Supplier<Boolean>> externalHighlightersRefresh;
    
    public SystemHighlighter(final SyntaxHighlighter commandHighlighter, final SyntaxHighlighter argsHighlighter, final SyntaxHighlighter langHighlighter) {
        this.resolver = Styles.lsStyle();
        this.fileHighlight = new HashMap<String, FileHighlightCommand>();
        this.specificHighlighter = new HashMap<String, SyntaxHighlighter>();
        this.externalHighlightersRefresh = new ArrayList<Supplier<Boolean>>();
        this.commandHighlighter = commandHighlighter;
        this.argsHighlighter = argsHighlighter;
        this.langHighlighter = langHighlighter;
        this.systemRegistry = SystemRegistry.get();
    }
    
    public void setSpecificHighlighter(final String command, final SyntaxHighlighter highlighter) {
        this.specificHighlighter.put(command, highlighter);
    }
    
    @Override
    public void refresh(final LineReader lineReader) {
        Path currentTheme = null;
        if (this.commandHighlighter != null) {
            this.commandHighlighter.refresh();
            currentTheme = this.compareThemes(this.commandHighlighter, currentTheme);
        }
        if (this.argsHighlighter != null) {
            this.argsHighlighter.refresh();
            currentTheme = this.compareThemes(this.argsHighlighter, currentTheme);
        }
        if (this.langHighlighter != null) {
            this.langHighlighter.refresh();
            currentTheme = this.compareThemes(this.langHighlighter, currentTheme);
        }
        for (final SyntaxHighlighter sh : this.specificHighlighter.values()) {
            sh.refresh();
            currentTheme = this.compareThemes(sh, currentTheme);
        }
        if (currentTheme != null) {
            try (final BufferedReader reader = Files.newBufferedReader(currentTheme)) {
                final Map<String, String> tokens = new HashMap<String, String>();
                String line;
                while ((line = reader.readLine()) != null) {
                    final String[] parts = line.trim().split("\\s+", 2);
                    if (parts[0].matches("[A-Z_]+") && parts.length == 2) {
                        tokens.put(parts[0], parts[1]);
                    }
                }
                final SystemRegistry registry = SystemRegistry.get();
                registry.setConsoleOption("NANORC_THEME", tokens);
                final Map<String, String> readerColors = registry.consoleOption("READER_COLORS", new HashMap());
                final Styles.StyleCompiler styleCompiler = new Styles.StyleCompiler(readerColors);
                for (final String key : readerColors.keySet()) {
                    lineReader.setVariable(key, styleCompiler.getStyle(key));
                }
                for (final Supplier<Boolean> refresh : this.externalHighlightersRefresh) {
                    refresh.get();
                }
                this.resolver = Styles.lsStyle();
            }
            catch (final IOException e) {
                Log.warn(e.getMessage());
            }
        }
    }
    
    public void addExternalHighlighterRefresh(final Supplier<Boolean> refresh) {
        this.externalHighlightersRefresh.add(refresh);
    }
    
    private Path compareThemes(final SyntaxHighlighter highlighter, final Path currentTheme) {
        Path out;
        if (currentTheme != null) {
            final Path theme = highlighter.getCurrentTheme();
            try {
                if (theme != null && !Files.isSameFile(theme, currentTheme)) {
                    Log.warn("Multiple nanorc themes are in use!");
                }
            }
            catch (final Exception e) {
                Log.warn(e.getMessage());
            }
            out = currentTheme;
        }
        else {
            out = highlighter.getCurrentTheme();
        }
        return out;
    }
    
    @Override
    public AttributedString highlight(final LineReader reader, final String buffer) {
        return this.doDefaultHighlight(reader) ? super.highlight(reader, buffer) : this.systemHighlight(reader, buffer);
    }
    
    public void addFileHighlight(final String... commands) {
        for (final String c : commands) {
            this.fileHighlight.put(c, new FileHighlightCommand());
        }
    }
    
    public void addFileHighlight(final String command, final String subcommand, final Collection<String> fileOptions) {
        this.fileHighlight.put(command, new FileHighlightCommand(subcommand, fileOptions));
    }
    
    private boolean doDefaultHighlight(final LineReader reader) {
        final String search = reader.getSearchTerm();
        return (search != null && !search.isEmpty()) || reader.getRegionActive() != LineReader.RegionType.NONE || this.errorIndex > -1 || this.errorPattern != null;
    }
    
    protected AttributedString systemHighlight(final LineReader reader, final String buffer) {
        final Parser parser = reader.getParser();
        final ParsedLine pl = parser.parse(buffer, 0, Parser.ParseContext.SPLIT_LINE);
        String command = pl.words().isEmpty() ? "" : parser.getCommand(pl.words().get(0));
        command = (command.startsWith("!") ? "!" : command);
        this.commandIndex = buffer.indexOf(command) + command.length();
        AttributedString out;
        if (buffer.trim().isEmpty()) {
            out = new AttributedStringBuilder().append(buffer).toAttributedString();
        }
        else if (this.specificHighlighter.containsKey(command)) {
            final AttributedStringBuilder asb = new AttributedStringBuilder();
            if (this.commandHighlighter == null) {
                asb.append(this.specificHighlighter.get(command).reset().highlight(buffer));
            }
            else {
                this.highlightCommand(buffer.substring(0, this.commandIndex), asb);
                asb.append(this.specificHighlighter.get(command).reset().highlight(buffer.substring(this.commandIndex)));
            }
            out = asb.toAttributedString();
        }
        else if (this.fileHighlight.containsKey(command)) {
            final FileHighlightCommand fhc = this.fileHighlight.get(command);
            if (!fhc.hasFileOptions()) {
                out = this.doFileArgsHighlight(reader, buffer, pl.words(), fhc);
            }
            else {
                out = this.doFileOptsHighlight(reader, buffer, pl.words(), fhc);
            }
        }
        else if (this.systemRegistry.isCommandOrScript(command) || this.systemRegistry.isCommandAlias(command) || command.isEmpty() || buffer.matches("\\s*#.*")) {
            out = this.doCommandHighlight(buffer);
        }
        else if (this.langHighlighter != null) {
            out = this.langHighlighter.reset().highlight(buffer);
        }
        else {
            out = new AttributedStringBuilder().append(buffer).toAttributedString();
        }
        return out;
    }
    
    protected AttributedString doFileOptsHighlight(final LineReader reader, final String buffer, final List<String> words, final FileHighlightCommand fhc) {
        final AttributedStringBuilder asb = new AttributedStringBuilder();
        if (this.commandIndex < 0) {
            this.highlightCommand(buffer, asb);
        }
        else {
            this.highlightCommand(buffer.substring(0, this.commandIndex), asb);
            if (!fhc.isSubcommand() || (words.size() > 2 && fhc.getSubcommand().equals(words.get(1)))) {
                boolean subCommand = fhc.isSubcommand();
                int idx = buffer.indexOf(words.get(0)) + words.get(0).length();
                boolean fileOption = false;
                for (int i = 1; i < words.size(); ++i) {
                    final int nextIdx = buffer.substring(idx).indexOf(words.get(i)) + idx;
                    for (int j = idx; j < nextIdx; ++j) {
                        asb.append(buffer.charAt(j));
                    }
                    final String word = words.get(i);
                    if (subCommand) {
                        subCommand = false;
                        this.highlightArgs(word, asb);
                    }
                    else if (word.contains("=") && fhc.getFileOptions().contains(word.substring(0, word.indexOf("=")))) {
                        this.highlightArgs(word.substring(0, word.indexOf("=") + 1), asb);
                        this.highlightFileArg(reader, word.substring(word.indexOf("=") + 1), asb);
                    }
                    else if (fhc.getFileOptions().contains(word)) {
                        this.highlightArgs(word, asb);
                        fileOption = true;
                    }
                    else if (fileOption) {
                        this.highlightFileArg(reader, word, asb);
                    }
                    else {
                        this.highlightArgs(word, asb);
                        fileOption = false;
                    }
                    idx = nextIdx + word.length();
                }
            }
            else {
                this.highlightArgs(buffer.substring(this.commandIndex), asb);
            }
        }
        return asb.toAttributedString();
    }
    
    protected AttributedString doFileArgsHighlight(final LineReader reader, final String buffer, final List<String> words, final FileHighlightCommand fhc) {
        final AttributedStringBuilder asb = new AttributedStringBuilder();
        if (this.commandIndex < 0) {
            this.highlightCommand(buffer, asb);
        }
        else {
            this.highlightCommand(buffer.substring(0, this.commandIndex), asb);
            if (!fhc.isSubcommand() || (words.size() > 2 && fhc.getSubcommand().equals(words.get(1)))) {
                boolean subCommand = fhc.isSubcommand();
                int idx = buffer.indexOf(words.get(0)) + words.get(0).length();
                for (int i = 1; i < words.size(); ++i) {
                    final int nextIdx = buffer.substring(idx).indexOf(words.get(i)) + idx;
                    for (int j = idx; j < nextIdx; ++j) {
                        asb.append(buffer.charAt(j));
                    }
                    if (subCommand) {
                        subCommand = false;
                        this.highlightArgs(words.get(i), asb);
                    }
                    else {
                        this.highlightFileArg(reader, words.get(i), asb);
                        idx = nextIdx + words.get(i).length();
                    }
                }
            }
            else {
                this.highlightArgs(buffer.substring(this.commandIndex), asb);
            }
        }
        return asb.toAttributedString();
    }
    
    protected AttributedString doCommandHighlight(final String buffer) {
        AttributedString out;
        if (this.commandHighlighter != null || this.argsHighlighter != null) {
            final AttributedStringBuilder asb = new AttributedStringBuilder();
            if (this.commandIndex < 0 || buffer.matches("\\s*#.*")) {
                this.highlightCommand(buffer, asb);
            }
            else {
                this.highlightCommand(buffer.substring(0, this.commandIndex), asb);
                this.highlightArgs(buffer.substring(this.commandIndex), asb);
            }
            out = asb.toAttributedString();
        }
        else {
            out = new AttributedStringBuilder().append(buffer).toAttributedString();
        }
        return out;
    }
    
    private void highlightFileArg(final LineReader reader, final String arg, final AttributedStringBuilder asb) {
        if (arg.startsWith("-")) {
            this.highlightArgs(arg, asb);
        }
        else {
            final String separator = reader.isSet(LineReader.Option.USE_FORWARD_SLASH) ? "/" : Paths.get(System.getProperty("user.dir"), new String[0]).getFileSystem().getSeparator();
            final StringBuilder sb = new StringBuilder();
            try {
                final Path path = new File(arg).toPath();
                final Iterator<Path> iterator = path.iterator();
                if (OSUtils.IS_WINDOWS && arg.matches("^[A-Za-z]:.*$")) {
                    if (arg.length() == 2) {
                        sb.append(arg);
                        asb.append(arg);
                    }
                    else if (arg.charAt(2) == separator.charAt(0)) {
                        sb.append(arg.substring(0, 3));
                        asb.append(arg.substring(0, 3));
                    }
                }
                if (arg.startsWith(separator)) {
                    sb.append(separator);
                    asb.append(separator);
                }
                while (iterator.hasNext()) {
                    sb.append(iterator.next());
                    this.highlightFile(new File(sb.toString()).toPath(), asb);
                    if (iterator.hasNext()) {
                        sb.append(separator);
                        asb.append(separator);
                    }
                }
                if (arg.length() > 2 && !arg.matches("^[A-Za-z]:" + separator) && arg.endsWith(separator)) {
                    asb.append(separator);
                }
            }
            catch (final Exception e) {
                asb.append(arg);
            }
        }
    }
    
    private void highlightFile(final Path path, final AttributedStringBuilder asb) {
        final AttributedStringBuilder sb = new AttributedStringBuilder();
        final String name = path.getFileName().toString();
        final int idx = name.lastIndexOf(".");
        final String type = (idx != -1) ? (".*" + name.substring(idx)) : null;
        if (Files.isSymbolicLink(path)) {
            sb.styled(this.resolver.resolve(".ln"), name);
        }
        else if (Files.isDirectory(path, new LinkOption[0])) {
            sb.styled(this.resolver.resolve(".di"), name);
        }
        else if (Files.isExecutable(path) && !OSUtils.IS_WINDOWS) {
            sb.styled(this.resolver.resolve(".ex"), name);
        }
        else if (type != null && this.resolver.resolve(type).getStyle() != 0L) {
            sb.styled(this.resolver.resolve(type), name);
        }
        else if (Files.isRegularFile(path, new LinkOption[0])) {
            sb.styled(this.resolver.resolve(".fi"), name);
        }
        else {
            sb.append(name);
        }
        asb.append(sb);
    }
    
    private void highlightArgs(final String args, final AttributedStringBuilder asb) {
        if (this.argsHighlighter != null) {
            asb.append(this.argsHighlighter.reset().highlight(args));
        }
        else {
            asb.append(args);
        }
    }
    
    private void highlightCommand(final String command, final AttributedStringBuilder asb) {
        if (this.commandHighlighter != null) {
            asb.append(this.commandHighlighter.reset().highlight(command));
        }
        else {
            asb.append(command);
        }
    }
    
    protected static class FileHighlightCommand
    {
        private final String subcommand;
        private final List<String> fileOptions;
        
        public FileHighlightCommand() {
            this(null, new ArrayList<String>());
        }
        
        public FileHighlightCommand(final String subcommand, final Collection<String> fileOptions) {
            this.fileOptions = new ArrayList<String>();
            this.subcommand = subcommand;
            this.fileOptions.addAll(fileOptions);
        }
        
        public boolean isSubcommand() {
            return this.subcommand != null;
        }
        
        public boolean hasFileOptions() {
            return !this.fileOptions.isEmpty();
        }
        
        public String getSubcommand() {
            return this.subcommand;
        }
        
        public List<String> getFileOptions() {
            return this.fileOptions;
        }
    }
}
