package com.jediterm.terminal.display;
import com.google.common.collect.Lists;
import com.jediterm.terminal.StyledTextConsumer;
import com.jediterm.terminal.TextStyle;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Holds styled characters lines
*/
public class LinesBuffer {
private static final Logger LOG = Logger.getLogger(LinesBuffer.class);
private static final int BUFFER_MAX_LINES = 1000;
private ArrayList<TerminalLine> myLines = Lists.newArrayList();
public LinesBuffer() {
}
public synchronized String getLines() {
final StringBuilder sb = new StringBuilder();
boolean first = true;
for (TerminalLine line : myLines) {
if (!first) {
sb.append("\n");
}
sb.append(line.getText());
first = false;
}
return sb.toString();
}
public synchronized void addNewLine(@NotNull TextStyle style, @NotNull CharBuffer characters) {
addNewLine(new TerminalLine.TextEntry(style, characters));
}
private synchronized void addNewLine(@NotNull TerminalLine.TextEntry entry) {
addLine(new TerminalLine(entry));
}
private synchronized void addLine(@NotNull TerminalLine line) {
if (myLines.size() > BUFFER_MAX_LINES) {
removeTopLines(1);
}
myLines.add(line);
}
public synchronized int getLineCount() {
return myLines.size();
}
public synchronized void removeTopLines(int count) {
myLines = Lists.newArrayList(myLines.subList(count, myLines.size()));
}
public String getLineText(int row) {
TerminalLine line = getLine(row);
return line.getText();
}
public synchronized void insertLines(int y, int count, int lastLine) {
LinesBuffer tail = new LinesBuffer();
if (lastLine < getLineCount() - 1) {
moveBottomLinesTo(getLineCount() - lastLine - 1, tail);
}
LinesBuffer head = new LinesBuffer();
if (y > 0) {
moveTopLinesTo(y, head);
}
for (int i = 0; i < count; i++) {
head.addNewLine(TextStyle.EMPTY, CharBuffer.EMPTY);
}
head.moveBottomLinesTo(head.getLineCount(), this);
removeBottomLines(count);
tail.moveTopLinesTo(tail.getLineCount(), this);
}
public synchronized LinesBuffer deleteLines(int y, int count, int lastLine) {
LinesBuffer tail = new LinesBuffer();
if (lastLine < getLineCount() - 1) {
moveBottomLinesTo(getLineCount() - lastLine - 1, tail);
}
LinesBuffer head = new LinesBuffer();
if (y > 0) {
moveTopLinesTo(y, head);
}
int toRemove = Math.min(count, getLineCount());
LinesBuffer removed = new LinesBuffer();
moveTopLinesTo(toRemove, removed);
head.moveBottomLinesTo(head.getLineCount(), this);
for (int i = 0; i < toRemove; i++) {
addNewLine(TextStyle.EMPTY, CharBuffer.EMPTY);
}
tail.moveTopLinesTo(tail.getLineCount(), this);
return removed;
}
public synchronized void writeString(int x, int y, String str, @NotNull TextStyle style) {
TerminalLine line = getLine(y);
line.writeString(x, str, style);
}
public synchronized void clearLines(int startRow, int endRow) {
for (int i = startRow; i <= endRow; i++) {
if (i < myLines.size()) {
TerminalLine line = myLines.get(i);
line.clear();
}
else {
break;
}
}
}
public synchronized void clearAll() {
myLines.clear();
}
public synchronized void deleteCharacters(int x, int y, int count) {
TerminalLine line = getLine(y);
line.deleteCharacters(x, count);
}
public synchronized void insertBlankCharacters(final int x, final int y, final int count, final int maxLen) {
TerminalLine line = getLine(y);
line.insertBlankCharacters(x, count, maxLen);
}
public synchronized void clearArea(int leftX, int topY, int rightX, int bottomY, @NotNull TextStyle style) {
for (int y = topY; y < bottomY; y++) {
TerminalLine line = getLine(y);
line.clearArea(leftX, rightX, style);
}
}
interface TextEntryProcessor {
/**
* @return true to remove entry
*/
void process(int x, int y, @NotNull TerminalLine.TextEntry entry);
}
public synchronized void processLines(final int yStart,
final int yCount,
@NotNull final StyledTextConsumer consumer,
final int startRow) {
iterateLines(yStart, yCount, new TextEntryProcessor() {
@Override
public void process(int x, int y, @NotNull TerminalLine.TextEntry entry) {
consumer.consume(x, y, entry.getStyle(), entry.getText(), startRow);
}
}, startRow);
}
public synchronized void processLines(final int yStart, final int yCount, @NotNull final StyledTextConsumer consumer) {
processLines(yStart, yCount, consumer, -getLineCount());
}
public synchronized void iterateLines(final int firstLine, final int count, @NotNull TextEntryProcessor processor, int startRow) {
int y = startRow-1;
int x;
for (TerminalLine line : myLines) {
y++;
if (y < firstLine) {
continue;
}
x = 0;
Iterator<TerminalLine.TextEntry> it = line.entriesIterator();
while (it.hasNext()) {
TerminalLine.TextEntry textEntry = it.next();
if (y >= firstLine + count) {
break;
}
processor.process(x, y, textEntry);
x += textEntry.getText().length();
}
}
}
public synchronized void moveTopLinesTo(int count, final @NotNull LinesBuffer buffer) {
count = Math.min(count, getLineCount());
buffer.addLines(myLines.subList(0, count));
removeTopLines(count);
}
public synchronized void addLines(@NotNull List<TerminalLine> lines) {
myLines.addAll(lines);
}
@NotNull
public synchronized TerminalLine getLine(int row) {
if (row >= getLineCount()) {
for (int i = getLineCount(); i <= row; i++) {
addLine(TerminalLine.createEmpty());
}
}
return myLines.get(row);
}
public synchronized void moveBottomLinesTo(int count, final @NotNull LinesBuffer buffer) {
count = Math.min(count, getLineCount());
buffer.addLinesFirst(myLines.subList(getLineCount() - count, getLineCount()));
removeBottomLines(count);
}
private synchronized void addLinesFirst(@NotNull List<TerminalLine> lines) {
List<TerminalLine> list = Lists.newArrayList(lines);
list.addAll(myLines);
myLines = Lists.newArrayList(list);
}
private synchronized void removeBottomLines(int count) {
myLines = Lists.newArrayList(myLines.subList(0, getLineCount() - count));
}
}