package term

import (
	
	
	
)

var logWriterDetail = false

// Writer represents the output to a terminal.
type Writer interface {
	// Buffer returns the current buffer.
	Buffer() *Buffer
	// ResetBuffer resets the current buffer.
	ResetBuffer()
	// UpdateBuffer updates the terminal display to reflect current buffer.
	UpdateBuffer(bufNoti, buf *Buffer, fullRefresh bool) error
	// ClearScreen clears the terminal screen and places the cursor at the top
	// left corner.
	ClearScreen()
	// ShowCursor shows the cursor.
	ShowCursor()
	// HideCursor hides the cursor.
	HideCursor()
}

// writer renders the editor UI.
type writer struct {
	file   io.Writer
	curBuf *Buffer
}

// NewWriter returns a Writer that writes VT100 sequences to the given io.Writer.
func ( io.Writer) Writer {
	return &writer{, &Buffer{}}
}

func ( *writer) () *Buffer {
	return .curBuf
}

func ( *writer) () {
	.curBuf = &Buffer{}
}

// deltaPos calculates the escape sequence needed to move the cursor from one
// position to another. It use relative movements to move to the destination
// line and absolute movement to move to the destination column.
func (,  Pos) []byte {
	 := new(bytes.Buffer)
	if .Line < .Line {
		// move down
		fmt.Fprintf(, "\033[%dB", .Line-.Line)
	} else if .Line > .Line {
		// move up
		fmt.Fprintf(, "\033[%dA", .Line-.Line)
	}
	fmt.Fprint(, "\r")
	if .Col > 0 {
		fmt.Fprintf(, "\033[%dC", .Col)
	}
	return .Bytes()
}

const (
	hideCursor = "\033[?25l"
	showCursor = "\033[?25h"
)

// UpdateBuffer updates the terminal display to reflect current buffer.
func ( *writer) (,  *Buffer,  bool) error {
	if .Width != .curBuf.Width && .curBuf.Lines != nil {
		// Width change, force full refresh
		.curBuf.Lines = nil
		 = true
	}

	 := new(bytes.Buffer)

	.WriteString(hideCursor)

	// Rewind cursor
	if  := .curBuf.Dot.Line;  > 0 {
		fmt.Fprintf(, "\033[%dA", )
	}
	.WriteString("\r")

	if  {
		// Erase from here. We may be in the top right corner of the screen; if
		// we simply do an erase here, tmux will save the current screen in the
		// scrollback buffer (presumably as a heuristics to detect full-screen
		// applications), but that is not something we want. So we write a space
		// first, and then erase, before rewinding back.
		//
		// Source code for tmux behavior:
		// https://github.com/tmux/tmux/blob/5f5f029e3b3a782dc616778739b2801b00b17c0e/screen-write.c#L1139
		.WriteString(" \033[J\r")
	}

	// style of last written cell.
	 := ""

	 := func( string) {
		if  !=  {
			fmt.Fprintf(, "\033[0;%sm", )
			 = 
		}
	}

	 := func( []Cell) {
		for ,  := range  {
			(.Style)
			.WriteString(.Text)
		}
	}

	if  != nil {
		if logWriterDetail {
			logger.Printf("going to write %d lines of notifications", len(.Lines))
		}

		// Write notifications
		for ,  := range .Lines {
			()
			("")
			.WriteString("\033[K\n")
		}
		// TODO(xiaq): This is hacky; try to improve it.
		if len(.curBuf.Lines) > 0 {
			.curBuf.Lines = .curBuf.Lines[1:]
		}
	}

	if logWriterDetail {
		logger.Printf("going to write %d lines, oldBuf had %d", len(.Lines), len(.curBuf.Lines))
	}

	for ,  := range .Lines {
		if  > 0 {
			.WriteString("\n")
		}
		var  int // First column where buf and oldBuf differ
		// No need to update current line
		if ! &&  < len(.curBuf.Lines) {
			var  bool
			if ,  = CompareCells(, .curBuf.Lines[]);  {
				continue
			}
		}
		// Move to the first differing column if necessary.
		 := CellsWidth([:])
		if  != 0 {
			fmt.Fprintf(, "\033[%dC", )
		}
		// Erase the rest of the line if necessary.
		if ! &&  < len(.curBuf.Lines) &&  < len(.curBuf.Lines[]) {
			("")
			.WriteString("\033[K")
		}
		([:])
	}
	if len(.curBuf.Lines) > len(.Lines) && ! {
		// If the old buffer is higher, erase old content.
		// Note that we cannot simply write \033[J, because if the cursor is
		// just over the last column -- which is precisely the case if we have a
		// rprompt, \033[J will also erase the last column.
		("")
		.WriteString("\n\033[J\033[A")
	}
	("")
	 := .Cursor()
	.Write(deltaPos(, .Dot))

	// Show cursor.
	.WriteString(showCursor)

	if logWriterDetail {
		logger.Printf("going to write %q", .String())
	}

	,  := .file.Write(.Bytes())
	if  != nil {
		return 
	}

	.curBuf = 
	return nil
}

func ( *writer) () {
	fmt.Fprint(.file, hideCursor)
}

func ( *writer) () {
	fmt.Fprint(.file, showCursor)
}

func ( *writer) () {
	fmt.Fprint(.file,
		"\033[H",  // move cursor to the top left corner
		"\033[2J", // clear entire buffer
	)
}