package term

import (
	
	

	
)

// Cell is an indivisible unit on the screen. It is not necessarily 1 column
// wide.
type Cell struct {
	Text  string
	Style string
}

// Pos is a line/column position.
type Pos struct {
	Line, Col int
}

// CellsWidth returns the total width of a Cell slice.
func ( []Cell) int {
	 := 0
	for ,  := range  {
		 += wcwidth.Of(.Text)
	}
	return 
}

// CompareCells returns whether two Cell slices are equal, and when they are
// not, the first index at which they differ.
func (,  []Cell) (bool, int) {
	for ,  := range  {
		if  >= len() ||  != [] {
			return false, 
		}
	}
	if len() < len() {
		return false, len()
	}
	return true, 0
}

// Buffer reflects a continuous range of lines on the terminal.
//
// The Unix terminal API provides only awkward ways of querying the terminal
// Buffer, so we keep an internal reflection and do one-way synchronizations
// (Buffer -> terminal, and not the other way around). This requires us to
// exactly match the terminal's idea of the width of characters (wcwidth) and
// where to insert soft carriage returns, so there could be bugs.
type Buffer struct {
	Width int
	// Lines the content of the buffer.
	Lines Lines
	// Dot is what the user perceives as the cursor.
	Dot Pos
}

// Lines stores multiple lines.
type Lines [][]Cell

// Line stores a single line.
type Line []Cell

// NewBuffer builds a new buffer, with one empty line.
func ( int) *Buffer {
	return &Buffer{Width: , Lines: [][]Cell{make([]Cell, 0, )}}
}

// Col returns the column the cursor is in.
func ( *Buffer) () int {
	return CellsWidth(.Lines[len(.Lines)-1])
}

// Cursor returns the current position of the cursor.
func ( *Buffer) () Pos {
	return Pos{len(.Lines) - 1, .Col()}
}

// BuffersHeight computes the combined height of a number of buffers.
func ( ...*Buffer) ( int) {
	for ,  := range  {
		if  != nil {
			 += len(.Lines)
		}
	}
	return
}

// TrimToLines trims a buffer to the lines [low, high).
func ( *Buffer) (,  int) {
	if  < 0 {
		 = 0
	}
	if  > len(.Lines) {
		 = len(.Lines)
	}
	for  := 0;  < ; ++ {
		.Lines[] = nil
	}
	for  := ;  < len(.Lines); ++ {
		.Lines[] = nil
	}
	.Lines = .Lines[:]
	.Dot.Line -= 
	if .Dot.Line < 0 {
		.Dot.Line = 0
	}
}

// Extend adds all lines from b2 to the bottom of this buffer. If moveDot is
// true, the dot is updated to match the dot of b2.
func ( *Buffer) ( *Buffer,  bool) {
	if  != nil && .Lines != nil {
		if  {
			.Dot.Line = .Dot.Line + len(.Lines)
			.Dot.Col = .Dot.Col
		}
		.Lines = append(.Lines, .Lines...)
	}
}

// ExtendRight extends bb to the right. It pads each line in b to be b.Width and
// appends the corresponding line in b2 to it, making new lines when b2 has more
// lines than bb.
func ( *Buffer) ( *Buffer) {
	 := 0
	 := .Width
	.Width += .Width
	for ;  < len(.Lines) &&  < len(.Lines); ++ {
		if  := CellsWidth(.Lines[]);  <  {
			.Lines[] = append(.Lines[], makeSpacing(-)...)
		}
		.Lines[] = append(.Lines[], .Lines[]...)
	}
	for ;  < len(.Lines); ++ {
		 := append(makeSpacing(), .Lines[]...)
		.Lines = append(.Lines, )
	}
}

// Buffer returns itself.
func ( *Buffer) () *Buffer { return  }

// TTYString returns a string for representing the buffer on the terminal.
func ( *Buffer) () string {
	if  == nil {
		return "nil"
	}
	 := new(strings.Builder)
	fmt.Fprintf(, "Width = %d, Dot = (%d, %d)\n", .Width, .Dot.Line, .Dot.Col)
	// Top border
	.WriteString("┌" + strings.Repeat("─", .Width) + "┐\n")
	for ,  := range .Lines {
		// Left border
		.WriteRune('│')
		// Content
		 := ""
		 := 0
		for ,  := range  {
			if .Style !=  {
				switch {
				case  == "":
					.WriteString("\033[" + .Style + "m")
				case .Style == "":
					.WriteString("\033[m")
				default:
					.WriteString("\033[;" + .Style + "m")
				}
				 = .Style
			}
			.WriteString(.Text)
			 += wcwidth.Of(.Text)
		}
		if  != "" {
			.WriteString("\033[m")
		}
		if  < .Width {
			.WriteString("$" + strings.Repeat(" ", .Width--1))
		}
		// Right border and newline
		.WriteString("│\n")
	}
	// Bottom border
	.WriteString("└" + strings.Repeat("─", .Width) + "┘\n")
	return .String()
}