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

package org.jline.utils;

import java.util.Collection;
import java.util.ArrayList;
import org.jline.terminal.Size;
import java.util.Objects;
import java.util.Collections;
import org.jline.terminal.impl.AbstractTerminal;
import java.util.Optional;
import java.util.List;
import org.jline.terminal.Terminal;

public class Status
{
    protected final Terminal terminal;
    protected final boolean supported;
    protected boolean suspended;
    protected AttributedString borderString;
    protected int border;
    protected Display display;
    protected List<AttributedString> lines;
    protected int scrollRegion;
    private final AttributedString ellipsis;
    
    public static Status getStatus(final Terminal terminal) {
        return getStatus(terminal, true);
    }
    
    public static Optional<Status> getExistingStatus(final Terminal terminal) {
        return Optional.ofNullable(getStatus(terminal, false));
    }
    
    public static Status getStatus(final Terminal terminal, final boolean create) {
        return (terminal instanceof AbstractTerminal) ? ((AbstractTerminal)terminal).getStatus(create) : null;
    }
    
    public Status(final Terminal terminal) {
        this.suspended = false;
        this.border = 0;
        this.lines = Collections.emptyList();
        this.ellipsis = new AttributedStringBuilder().append("\u2026", AttributedStyle.INVERSE).toAttributedString();
        this.terminal = Objects.requireNonNull(terminal, "terminal can not be null");
        this.supported = (terminal.getStringCapability(InfoCmp.Capability.change_scroll_region) != null && terminal.getStringCapability(InfoCmp.Capability.save_cursor) != null && terminal.getStringCapability(InfoCmp.Capability.restore_cursor) != null && terminal.getStringCapability(InfoCmp.Capability.cursor_address) != null && this.isValid(terminal.getSize()));
        if (this.supported) {
            this.display = new MovingCursorDisplay(terminal);
            this.resize();
            this.display.reset();
            this.scrollRegion = this.display.rows - 1;
        }
    }
    
    private boolean isValid(final Size size) {
        return size.getRows() > 0 && size.getRows() < 1000 && size.getColumns() > 0 && size.getColumns() < 1000;
    }
    
    public void close() {
        if (this.supported) {
            this.terminal.puts(InfoCmp.Capability.save_cursor, new Object[0]);
            this.terminal.puts(InfoCmp.Capability.change_scroll_region, 0, this.display.rows - 1);
            this.terminal.puts(InfoCmp.Capability.restore_cursor, new Object[0]);
            this.terminal.flush();
        }
    }
    
    public void setBorder(final boolean border) {
        this.border = (border ? 1 : 0);
    }
    
    public void resize() {
        this.resize(this.terminal.getSize());
    }
    
    public void resize(final Size size) {
        if (this.supported) {
            this.display.resize(size.getRows(), size.getColumns());
        }
    }
    
    public void reset() {
        if (this.supported) {
            this.display.reset();
            this.scrollRegion = this.display.rows;
            this.terminal.puts(InfoCmp.Capability.change_scroll_region, 0, this.scrollRegion);
        }
    }
    
    public void redraw() {
        if (this.suspended) {
            return;
        }
        this.update(this.lines);
    }
    
    public void hide() {
        this.update(Collections.emptyList());
    }
    
    public void update(final List<AttributedString> lines) {
        this.update(lines, true);
    }
    
    public void update(List<AttributedString> lines, final boolean flush) {
        if (!this.supported) {
            return;
        }
        this.lines = new ArrayList<AttributedString>(lines);
        if (this.suspended) {
            return;
        }
        lines = new ArrayList<AttributedString>(lines);
        final int rows = this.display.rows;
        final int columns = this.display.columns;
        if (this.border == 1 && !lines.isEmpty() && rows > 1) {
            lines.add(0, this.getBorderString(columns));
        }
        for (int i = 0; i < lines.size(); ++i) {
            AttributedString str = lines.get(i);
            if (str.columnLength() > columns) {
                str = new AttributedStringBuilder(columns).append(lines.get(i).columnSubSequence(0, columns - this.ellipsis.columnLength())).append(this.ellipsis).toAttributedString();
            }
            else if (str.columnLength() < columns) {
                str = new AttributedStringBuilder(columns).append(str).append(' ', columns - str.columnLength()).toAttributedString();
            }
            lines.set(i, str);
        }
        final List<AttributedString> oldLines = this.display.oldLines;
        final int newScrollRegion = this.display.rows - 1 - lines.size();
        if (newScrollRegion < this.scrollRegion) {
            this.terminal.puts(InfoCmp.Capability.save_cursor, new Object[0]);
            this.terminal.puts(InfoCmp.Capability.cursor_address, this.scrollRegion, 0);
            for (int j = newScrollRegion; j < this.scrollRegion; ++j) {
                this.terminal.puts(InfoCmp.Capability.cursor_down, new Object[0]);
            }
            this.terminal.puts(InfoCmp.Capability.change_scroll_region, 0, newScrollRegion);
            this.terminal.puts(InfoCmp.Capability.restore_cursor, new Object[0]);
            for (int j = newScrollRegion; j < this.scrollRegion; ++j) {
                this.terminal.puts(InfoCmp.Capability.cursor_up, new Object[0]);
            }
            this.scrollRegion = newScrollRegion;
        }
        else if (newScrollRegion > this.scrollRegion) {
            this.terminal.puts(InfoCmp.Capability.save_cursor, new Object[0]);
            this.terminal.puts(InfoCmp.Capability.change_scroll_region, 0, newScrollRegion);
            this.terminal.puts(InfoCmp.Capability.restore_cursor, new Object[0]);
            this.scrollRegion = newScrollRegion;
        }
        final List<AttributedString> toDraw = new ArrayList<AttributedString>(lines);
        final int nbToDraw = toDraw.size();
        final int nbOldLines = oldLines.size();
        if (nbOldLines > nbToDraw) {
            this.terminal.puts(InfoCmp.Capability.save_cursor, new Object[0]);
            this.terminal.puts(InfoCmp.Capability.cursor_address, this.display.rows - nbOldLines, 0);
            for (int k = 0; k < nbOldLines - nbToDraw; ++k) {
                this.terminal.puts(InfoCmp.Capability.clr_eol, new Object[0]);
                if (k < nbOldLines - nbToDraw - 1) {
                    this.terminal.puts(InfoCmp.Capability.cursor_down, new Object[0]);
                }
                oldLines.remove(0);
            }
            this.terminal.puts(InfoCmp.Capability.restore_cursor, new Object[0]);
        }
        this.display.update(lines, -1, flush);
    }
    
    private AttributedString getBorderString(final int columns) {
        if (this.borderString == null || this.borderString.length() != columns) {
            final char borderChar = '\u2500';
            final AttributedStringBuilder bb = new AttributedStringBuilder();
            for (int i = 0; i < columns; ++i) {
                bb.append(borderChar);
            }
            this.borderString = bb.toAttributedString();
        }
        return this.borderString;
    }
    
    public void suspend() {
        if (!this.suspended) {
            this.suspended = true;
        }
    }
    
    public void restore() {
        if (this.suspended) {
            this.suspended = false;
            this.update(this.lines);
        }
    }
    
    public int size() {
        return this.size(this.lines);
    }
    
    private int size(final List<?> lines) {
        final int l = lines.size();
        return (l > 0) ? (l + this.border) : 0;
    }
    
    @Override
    public String toString() {
        return "Status[supported=" + this.supported + ']';
    }
    
    static class MovingCursorDisplay extends Display
    {
        protected int firstLine;
        
        public MovingCursorDisplay(final Terminal terminal) {
            super(terminal, false);
        }
        
        @Override
        public void update(final List<AttributedString> newLines, final int targetCursorPos, final boolean flush) {
            this.cursorPos = -1;
            this.firstLine = this.rows - newLines.size();
            super.update(newLines, targetCursorPos, flush);
            if (this.cursorPos != -1) {
                this.terminal.puts(InfoCmp.Capability.restore_cursor, new Object[0]);
                if (flush) {
                    this.terminal.flush();
                }
            }
        }
        
        @Override
        protected void moveVisualCursorTo(final int targetPos, final List<AttributedString> newLines) {
            this.initCursor();
            super.moveVisualCursorTo(targetPos, newLines);
        }
        
        @Override
        protected int moveVisualCursorTo(final int i1) {
            this.initCursor();
            return super.moveVisualCursorTo(i1);
        }
        
        void initCursor() {
            if (this.cursorPos == -1) {
                this.terminal.puts(InfoCmp.Capability.save_cursor, new Object[0]);
                this.terminal.puts(InfoCmp.Capability.cursor_address, this.firstLine, 0);
                this.cursorPos = 0;
            }
        }
    }
}
