package termutil

import (
	"fmt"
	"strconv"
	"strings"
)

func parseCSI(readChan chan MeasuredRune) (final rune, params []string, intermediate []rune, raw []rune) {
	var b MeasuredRune

	param := ""
	intermediate = []rune{}
CSI:
	for {
		b = <-readChan
		raw = append(raw, b.Rune)
		switch true {
		case b.Rune >= 0x30 && b.Rune <= 0x3F:
			param = param + string(b.Rune)
		case b.Rune > 0 && b.Rune <= 0x2F:
			intermediate = append(intermediate, b.Rune)
		case b.Rune >= 0x40 && b.Rune <= 0x7e:
			final = b.Rune
			break CSI
		}
	}

	unprocessed := strings.Split(param, ";")
	for _, par := range unprocessed {
		if par != "" {
			par = strings.TrimLeft(par, "0")
			if par == "" {
				par = "0"
			}
			params = append(params, par)
		}
	}

	return final, params, intermediate, raw
}

func (t *Terminal) handleCSI(readChan chan MeasuredRune) (renderRequired bool) {
	final, params, intermediate, raw := parseCSI(readChan)

	t.log("CSI P(%q) I(%q) %c", strings.Join(params, ";"), string(intermediate), final)

	for _, b := range intermediate {
		t.processRunes(MeasuredRune{
			Rune:  b,
			Width: 1,
		})
	}

	switch final {
	case 'c':
		return t.csiSendDeviceAttributesHandler(params)
	case 'd':
		return t.csiLinePositionAbsoluteHandler(params)
	case 'f':
		return t.csiCursorPositionHandler(params)
	case 'g':
		return t.csiTabClearHandler(params)
	case 'h':
		return t.csiSetModeHandler(params)
	case 'l':
		return t.csiResetModeHandler(params)
	case 'm':
		return t.sgrSequenceHandler(params)
	case 'n':
		return t.csiDeviceStatusReportHandler(params)
	case 'r':
		return t.csiSetMarginsHandler(params)
	case 't':
		return t.csiWindowManipulation(params)
	case 'A':
		return t.csiCursorUpHandler(params)
	case 'B':
		return t.csiCursorDownHandler(params)
	case 'C':
		return t.csiCursorForwardHandler(params)
	case 'D':
		return t.csiCursorBackwardHandler(params)
	case 'E':
		return t.csiCursorNextLineHandler(params)
	case 'F':
		return t.csiCursorPrecedingLineHandler(params)
	case 'G':
		return t.csiCursorCharacterAbsoluteHandler(params)
	case 'H':
		return t.csiCursorPositionHandler(params)
	case 'J':
		return t.csiEraseInDisplayHandler(params)
	case 'K':
		return t.csiEraseInLineHandler(params)
	case 'L':
		return t.csiInsertLinesHandler(params)
	case 'M':
		return t.csiDeleteLinesHandler(params)
	case 'P':
		return t.csiDeleteHandler(params)
	case 'S':
		return t.csiScrollUpHandler(params)
	case 'T':
		return t.csiScrollDownHandler(params)
	case 'X':
		return t.csiEraseCharactersHandler(params)
	case '@':
		return t.csiInsertBlankCharactersHandler(params)
	case 'p': // reset handler
		if string(intermediate) == "!" {
			return t.csiSoftResetHandler(params)
		}
		return false
	default:
		// TODO review this:
		// if this is an unknown CSI sequence, write it to stdout as we can't handle it?
		//_ = t.writeToRealStdOut(append([]rune{0x1b, '['}, raw...)...)
		_ = raw
		t.log("UNKNOWN CSI P(%s) I(%s) %c", strings.Join(params, ";"), string(intermediate), final)
		return false
	}

}

type WindowState uint8

const (
	StateUnknown WindowState = iota
	StateMinimised
	StateNormal
	StateMaximised
)

type WindowManipulator interface {
	State() WindowState
	Minimise()
	Maximise()
	Restore()
	SetTitle(title string)
	Position() (int, int)
	SizeInPixels() (int, int)
	CellSizeInPixels() (int, int)
	SizeInChars() (int, int)
	ResizeInPixels(int, int)
	ResizeInChars(int, int)
	ScreenSizeInPixels() (int, int)
	ScreenSizeInChars() (int, int)
	Move(x, y int)
	IsFullscreen() bool
	SetFullscreen(enabled bool)
	GetTitle() string
	SaveTitleToStack()
	RestoreTitleFromStack()
	ReportError(err error)
}

func (t *Terminal) csiWindowManipulation(params []string) (renderRequired bool) {

	if t.windowManipulator == nil {
		return false
	}

	for i := 0; i < len(params); i++ {
		switch params[i] {
		case "1":
			t.windowManipulator.Restore()
		case "2":
			t.windowManipulator.Minimise()
		case "3": //move window
			if i+2 >= len(params) {
				return false
			}
			x, _ := strconv.Atoi(params[i+1])
			y, _ := strconv.Atoi(params[i+2])
			i += 2
			t.windowManipulator.Move(x, y)
		case "4": //resize h,w
			w, h := t.windowManipulator.SizeInPixels()
			if i+1 < len(params) {
				h, _ = strconv.Atoi(params[i+1])
				i++
			}
			if i+2 < len(params) {
				w, _ = strconv.Atoi(params[i+2])
				i++
			}
			sw, sh := t.windowManipulator.ScreenSizeInPixels()
			if w == 0 {
				w = sw
			}
			if h == 0 {
				h = sh
			}
			t.windowManipulator.ResizeInPixels(w, h)
		case "8":
			// resize in rows, cols
			w, h := t.windowManipulator.SizeInChars()
			if i+1 < len(params) {
				h, _ = strconv.Atoi(params[i+1])
				i++
			}
			if i+2 < len(params) {
				w, _ = strconv.Atoi(params[i+2])
				i++
			}
			sw, sh := t.windowManipulator.ScreenSizeInChars()
			if w == 0 {
				w = sw
			}
			if h == 0 {
				h = sh
			}
			t.windowManipulator.ResizeInChars(w, h)
		case "9":
			if i+1 >= len(params) {
				return false
			}
			switch params[i+1] {
			case "0":
				t.windowManipulator.Restore()
			case "1":
				t.windowManipulator.Maximise()
			case "2":
				w, _ := t.windowManipulator.SizeInPixels()
				_, sh := t.windowManipulator.ScreenSizeInPixels()
				t.windowManipulator.ResizeInPixels(w, sh)
			case "3":
				_, h := t.windowManipulator.SizeInPixels()
				sw, _ := t.windowManipulator.ScreenSizeInPixels()
				t.windowManipulator.ResizeInPixels(sw, h)
			}
			i++
		case "10":
			if i+1 >= len(params) {
				return false
			}
			switch params[i+1] {
			case "0":
				t.windowManipulator.SetFullscreen(false)
			case "1":
				t.windowManipulator.SetFullscreen(true)
			case "2":
				// toggle
				t.windowManipulator.SetFullscreen(!t.windowManipulator.IsFullscreen())
			}
			i++

		case "11":
			if t.windowManipulator.State() != StateMinimised {
				t.WriteToPty([]byte("\x1b[1t"))
			} else {
				t.WriteToPty([]byte("\x1b[2t"))
			}
		case "13":
			if i < len(params)-1 {
				i++
			}
			x, y := t.windowManipulator.Position()
			t.WriteToPty([]byte(fmt.Sprintf("\x1b[3;%d;%dt", x, y)))
		case "14":
			if i < len(params)-1 {
				i++
			}
			w, h := t.windowManipulator.SizeInPixels()
			t.WriteToPty([]byte(fmt.Sprintf("\x1b[4;%d;%dt", h, w)))
		case "15":
			w, h := t.windowManipulator.ScreenSizeInPixels()
			t.WriteToPty([]byte(fmt.Sprintf("\x1b[5;%d;%dt", h, w)))
		case "16":
			w, h := t.windowManipulator.CellSizeInPixels()
			t.WriteToPty([]byte(fmt.Sprintf("\x1b[6;%d;%dt", h, w)))
		case "18":
			w, h := t.windowManipulator.SizeInChars()
			t.WriteToPty([]byte(fmt.Sprintf("\x1b[8;%d;%dt", h, w)))
		case "19":
			w, h := t.windowManipulator.ScreenSizeInChars()
			t.WriteToPty([]byte(fmt.Sprintf("\x1b[9;%d;%dt", h, w)))
		case "20":
			t.WriteToPty([]byte(fmt.Sprintf("\x1b]L%s\x1b\\", t.windowManipulator.GetTitle())))
		case "21":
			t.WriteToPty([]byte(fmt.Sprintf("\x1b]l%s\x1b\\", t.windowManipulator.GetTitle())))
		case "22":
			if i < len(params)-1 {
				i++
			}
			t.windowManipulator.SaveTitleToStack()
		case "23":
			if i < len(params)-1 {
				i++
			}
			t.windowManipulator.RestoreTitleFromStack()
		}
	}

	return true
}

// CSI c
// Send Device Attributes (Primary/Secondary/Tertiary DA)
func (t *Terminal) csiSendDeviceAttributesHandler(params []string) (renderRequired bool) {

	// we are VT100
	// for DA1 we'll respond ?1;2
	// for DA2 we'll respond >0;0;0
	response := "?1;2"
	if len(params) > 0 && len(params[0]) > 0 && params[0][0] == '>' {
		response = ">0;0;0"
	}

	// write response to source pty
	t.WriteToPty([]byte("\x1b[" + response + "c"))
	return false
}

// CSI n
// Device Status Report (DSR)
func (t *Terminal) csiDeviceStatusReportHandler(params []string) (renderRequired bool) {

	if len(params) == 0 {
		return false
	}

	switch params[0] {
	case "5":
		t.WriteToPty([]byte("\x1b[0n")) // everything is cool
	case "6": // report cursor position
		t.WriteToPty([]byte(fmt.Sprintf(
			"\x1b[%d;%dR",
			t.GetActiveBuffer().CursorLine()+1,
			t.GetActiveBuffer().CursorColumn()+1,
		)))
	}

	return false
}

// CSI A
// Cursor Up Ps Times (default = 1) (CUU)
func (t *Terminal) csiCursorUpHandler(params []string) (renderRequired bool) {
	distance := 1
	if len(params) > 0 {
		var err error
		distance, err = strconv.Atoi(params[0])
		if err != nil || distance < 1 {
			distance = 1
		}
	}
	t.GetActiveBuffer().movePosition(0, -int16(distance))
	return true
}

// CSI B
// Cursor Down Ps Times (default = 1) (CUD)
func (t *Terminal) csiCursorDownHandler(params []string) (renderRequired bool) {
	distance := 1
	if len(params) > 0 {
		var err error
		distance, err = strconv.Atoi(params[0])
		if err != nil || distance < 1 {
			distance = 1
		}
	}

	t.GetActiveBuffer().movePosition(0, int16(distance))
	return true
}

// CSI C
// Cursor Forward Ps Times (default = 1) (CUF)
func (t *Terminal) csiCursorForwardHandler(params []string) (renderRequired bool) {
	distance := 1
	if len(params) > 0 {
		var err error
		distance, err = strconv.Atoi(params[0])
		if err != nil || distance < 1 {
			distance = 1
		}
	}

	t.GetActiveBuffer().movePosition(int16(distance), 0)
	return true
}

// CSI D
// Cursor Backward Ps Times (default = 1) (CUB)
func (t *Terminal) csiCursorBackwardHandler(params []string) (renderRequired bool) {
	distance := 1
	if len(params) > 0 {
		var err error
		distance, err = strconv.Atoi(params[0])
		if err != nil || distance < 1 {
			distance = 1
		}
	}

	t.GetActiveBuffer().movePosition(-int16(distance), 0)
	return true
}

// CSI E
// Cursor Next Line Ps Times (default = 1) (CNL)
func (t *Terminal) csiCursorNextLineHandler(params []string) (renderRequired bool) {

	distance := 1
	if len(params) > 0 {
		var err error
		distance, err = strconv.Atoi(params[0])
		if err != nil || distance < 1 {
			distance = 1
		}
	}

	t.GetActiveBuffer().movePosition(0, int16(distance))
	t.GetActiveBuffer().setPosition(0, t.GetActiveBuffer().CursorLine())
	return true
}

// CSI F
// Cursor Preceding Line Ps Times (default = 1) (CPL)
func (t *Terminal) csiCursorPrecedingLineHandler(params []string) (renderRequired bool) {

	distance := 1
	if len(params) > 0 {
		var err error
		distance, err = strconv.Atoi(params[0])
		if err != nil || distance < 1 {
			distance = 1
		}
	}
	t.GetActiveBuffer().movePosition(0, -int16(distance))
	t.GetActiveBuffer().setPosition(0, t.GetActiveBuffer().CursorLine())
	return true
}

// CSI G
// Cursor Horizontal Absolute  [column] (default = [row,1]) (CHA)
func (t *Terminal) csiCursorCharacterAbsoluteHandler(params []string) (renderRequired bool) {
	distance := 1
	if len(params) > 0 {
		var err error
		distance, err = strconv.Atoi(params[0])
		if err != nil || params[0] == "" {
			distance = 1
		}
	}

	t.GetActiveBuffer().setPosition(uint16(distance-1), t.GetActiveBuffer().CursorLine())
	return true
}

func parseCursorPosition(params []string) (x, y int) {
	x, y = 1, 1
	if len(params) >= 1 {
		var err error
		if params[0] != "" {
			y, err = strconv.Atoi(string(params[0]))
			if err != nil || y < 1 {
				y = 1
			}
		}
	}
	if len(params) >= 2 {
		if params[1] != "" {
			var err error
			x, err = strconv.Atoi(string(params[1]))
			if err != nil || x < 1 {
				x = 1
			}
		}
	}
	return x, y
}

// CSI f
// Horizontal and Vertical Position [row;column] (default = [1,1]) (HVP)
// AND
// CSI H
// Cursor Position [row;column] (default = [1,1]) (CUP)
func (t *Terminal) csiCursorPositionHandler(params []string) (renderRequired bool) {
	x, y := parseCursorPosition(params)
	t.GetActiveBuffer().setPosition(uint16(x-1), uint16(y-1))
	return true
}

// CSI S
// Scroll up Ps lines (default = 1) (SU), VT420, ECMA-48
func (t *Terminal) csiScrollUpHandler(params []string) (renderRequired bool) {
	distance := 1
	if len(params) > 1 {
		return false
	}
	if len(params) == 1 {
		var err error
		distance, err = strconv.Atoi(params[0])
		if err != nil || distance < 1 {
			distance = 1
		}
	}
	t.GetActiveBuffer().areaScrollUp(uint16(distance))
	return true
}

// CSI @
// Insert Ps (Blank) Character(s) (default = 1) (ICH)
func (t *Terminal) csiInsertBlankCharactersHandler(params []string) (renderRequired bool) {
	count := 1
	if len(params) > 1 {
		return false
	}
	if len(params) == 1 {
		var err error
		count, err = strconv.Atoi(params[0])
		if err != nil || count < 1 {
			count = 1
		}
	}

	t.GetActiveBuffer().insertBlankCharacters(count)
	return true
}

// CSI L
// Insert Ps Line(s) (default = 1) (IL)
func (t *Terminal) csiInsertLinesHandler(params []string) (renderRequired bool) {
	count := 1
	if len(params) > 1 {
		return false
	}
	if len(params) == 1 {
		var err error
		count, err = strconv.Atoi(params[0])
		if err != nil || count < 1 {
			count = 1
		}
	}

	t.GetActiveBuffer().insertLines(count)
	return true
}

// CSI M
// Delete Ps Line(s) (default = 1) (DL)
func (t *Terminal) csiDeleteLinesHandler(params []string) (renderRequired bool) {
	count := 1
	if len(params) > 1 {
		return false
	}
	if len(params) == 1 {
		var err error
		count, err = strconv.Atoi(params[0])
		if err != nil || count < 1 {
			count = 1
		}
	}

	t.GetActiveBuffer().deleteLines(count)
	return true
}

// CSI T
// Scroll down Ps lines (default = 1) (SD), VT420
func (t *Terminal) csiScrollDownHandler(params []string) (renderRequired bool) {
	distance := 1
	if len(params) > 1 {
		return false
	}
	if len(params) == 1 {
		var err error
		distance, err = strconv.Atoi(params[0])
		if err != nil || distance < 1 {
			distance = 1
		}
	}
	t.GetActiveBuffer().areaScrollDown(uint16(distance))
	return true
}

// CSI r
// Set Scrolling Region [top;bottom] (default = full size of window) (DECSTBM), VT100
func (t *Terminal) csiSetMarginsHandler(params []string) (renderRequired bool) {
	top := 1
	bottom := int(t.GetActiveBuffer().ViewHeight())

	if len(params) > 2 {
		return false
	}

	if len(params) > 0 {
		var err error
		top, err = strconv.Atoi(params[0])
		if err != nil || top < 1 {
			top = 1
		}

		if len(params) > 1 {
			var err error
			bottom, err = strconv.Atoi(params[1])
			if err != nil || bottom > int(t.GetActiveBuffer().ViewHeight()) || bottom < 1 {
				bottom = int(t.GetActiveBuffer().ViewHeight())
			}
		}
	}
	top--
	bottom--

	t.activeBuffer.setVerticalMargins(uint(top), uint(bottom))
	t.GetActiveBuffer().setPosition(0, 0)
	return true
}

// CSI X
// Erase Ps Character(s) (default = 1) (ECH)
func (t *Terminal) csiEraseCharactersHandler(params []string) (renderRequired bool) {
	count := 1
	if len(params) > 0 {
		var err error
		count, err = strconv.Atoi(params[0])
		if err != nil || count < 1 {
			count = 1
		}
	}

	t.GetActiveBuffer().eraseCharacters(count)
	return true
}

// CSI l
// Reset Mode (RM)
func (t *Terminal) csiResetModeHandler(params []string) (renderRequired bool) {
	return t.csiSetModes(params, false)
}

// CSI h
// Set Mode (SM)
func (t *Terminal) csiSetModeHandler(params []string) (renderRequired bool) {
	return t.csiSetModes(params, true)
}

func (t *Terminal) csiSetModes(modes []string, enabled bool) bool {
	if len(modes) == 0 {
		return false
	}
	if len(modes) == 1 {
		return t.csiSetMode(modes[0], enabled)
	}
	// should we propagate DEC prefix?
	const decPrefix = '?'
	isDec := len(modes[0]) > 0 && modes[0][0] == decPrefix

	var render bool

	// iterate through params, propagating DEC prefix to subsequent elements
	for i, v := range modes {
		updatedMode := v
		if i > 0 && isDec {
			updatedMode = string(decPrefix) + v
		}
		render = t.csiSetMode(updatedMode, enabled) || render
	}

	return render
}

func parseModes(mode string) []string {

	var output []string

	if mode == "" {
		return nil
	}
	var prefix string
	if mode[0] == '?' {
		prefix = "?"
		mode = mode[1:]
	}

	for len(mode) > 4 {
		output = append(output, prefix+mode[:4])
		mode = mode[4:]
	}

	output = append(output, prefix+mode)
	return output
}

func (t *Terminal) csiSetMode(modes string, enabled bool) bool {

	for _, modeStr := range parseModes(modes) {

		switch modeStr {
		case "4":
			t.activeBuffer.modes.ReplaceMode = !enabled
		case "20":
			t.activeBuffer.modes.LineFeedMode = false
		case "?1":
			t.activeBuffer.modes.ApplicationCursorKeys = enabled
		case "?3":
			if t.windowManipulator != nil {
				if enabled {
					// DECCOLM - COLumn mode, 132 characters per line
					t.windowManipulator.ResizeInChars(132, int(t.activeBuffer.viewHeight))
				} else {
					// DECCOLM - 80 characters per line (erases screen)
					t.windowManipulator.ResizeInChars(80, int(t.activeBuffer.viewHeight))
				}
				t.activeBuffer.clear()
			}
		case "?5": // DECSCNM
			t.activeBuffer.modes.ScreenMode = enabled
		case "?6":
			// DECOM
			t.activeBuffer.modes.OriginMode = enabled
		case "?7":
			// auto-wrap mode
			//DECAWM
			t.activeBuffer.modes.AutoWrap = enabled
		case "?9":
			if enabled {
				//terminal.logger.Infof("Turning on X10 mouse mode")
				t.activeBuffer.mouseMode = (MouseModeX10)
			} else {
				//terminal.logger.Infof("Turning off X10 mouse mode")
				t.activeBuffer.mouseMode = (MouseModeNone)
			}
		case "?12", "?13":
			t.activeBuffer.modes.BlinkingCursor = enabled
		case "?25":
			t.activeBuffer.modes.ShowCursor = enabled
		case "?47", "?1047":
			if enabled {
				t.useAltBuffer()
			} else {
				t.useMainBuffer()
			}
		case "?1000": // ?10061000 seen from htop
			// enable mouse tracking
			// 1000 refers to ext mode for extended mouse click area - otherwise only x <= 255-31
			if enabled {
				t.activeBuffer.mouseMode = (MouseModeVT200)
			} else {
				t.activeBuffer.mouseMode = (MouseModeNone)
			}
		case "?1002":
			if enabled {
				//terminal.logger.Infof("Turning on Button Event mouse mode")
				t.activeBuffer.mouseMode = (MouseModeButtonEvent)
			} else {
				//terminal.logger.Infof("Turning off Button Event mouse mode")
				t.activeBuffer.mouseMode = (MouseModeNone)
			}
		case "?1003":
			if enabled {
				t.activeBuffer.mouseMode = MouseModeAnyEvent
			} else {
				t.activeBuffer.mouseMode = MouseModeNone
			}
		case "?1005":
			if enabled {
				t.activeBuffer.mouseExtMode = MouseExtUTF
			} else {
				t.activeBuffer.mouseExtMode = MouseExtNone
			}

		case "?1006":
			if enabled {
				//.logger.Infof("Turning on SGR ext mouse mode")
				t.activeBuffer.mouseExtMode = MouseExtSGR
			} else {
				//terminal.logger.Infof("Turning off SGR ext mouse mode")
				t.activeBuffer.mouseExtMode = (MouseExtNone)
			}
		case "?1015":
			if enabled {
				//terminal.logger.Infof("Turning on URXVT ext mouse mode")
				t.activeBuffer.mouseExtMode = (MouseExtURXVT)
			} else {
				//terminal.logger.Infof("Turning off URXVT ext mouse mode")
				t.activeBuffer.mouseExtMode = (MouseExtNone)
			}
		case "?1048":
			if enabled {
				t.GetActiveBuffer().saveCursor()
			} else {
				t.GetActiveBuffer().restoreCursor()
			}
		case "?1049":
			if enabled {
				t.useAltBuffer()
			} else {
				t.useMainBuffer()
			}
		case "?2004":
			t.activeBuffer.modes.BracketedPasteMode = enabled
		case "?80":
			t.activeBuffer.modes.SixelScrolling = enabled
		default:
			t.log("Unsupported CSI mode %s = %t", modeStr, enabled)
		}
	}
	return false
}

// CSI d
// Line Position Absolute  [row] (default = [1,column]) (VPA)
func (t *Terminal) csiLinePositionAbsoluteHandler(params []string) (renderRequired bool) {
	row := 1
	if len(params) > 0 {
		var err error
		row, err = strconv.Atoi(params[0])
		if err != nil || row < 1 {
			row = 1
		}
	}

	t.GetActiveBuffer().setPosition(t.GetActiveBuffer().CursorColumn(), uint16(row-1))

	return true
}

// CSI P
// Delete Ps Character(s) (default = 1) (DCH)
func (t *Terminal) csiDeleteHandler(params []string) (renderRequired bool) {
	n := 1
	if len(params) >= 1 {
		var err error
		n, err = strconv.Atoi(params[0])
		if err != nil || n < 1 {
			n = 1
		}
	}

	t.GetActiveBuffer().deleteChars(n)
	return true
}

// CSI g
// tab clear (TBC)
func (t *Terminal) csiTabClearHandler(params []string) (renderRequired bool) {
	n := "0"
	if len(params) > 0 {
		n = params[0]
	}
	switch n {
	case "0", "":
		t.activeBuffer.tabClearAtCursor()
	case "3":
		t.activeBuffer.tabReset()
	default:
		return false
	}

	return true
}

// CSI J
// Erase in Display (ED), VT100
func (t *Terminal) csiEraseInDisplayHandler(params []string) (renderRequired bool) {
	n := "0"
	if len(params) > 0 {
		n = params[0]
	}

	switch n {
	case "0", "":
		t.GetActiveBuffer().eraseDisplayFromCursor()
	case "1":
		t.GetActiveBuffer().eraseDisplayToCursor()
	case "2", "3":
		t.GetActiveBuffer().eraseDisplay()
	default:
		return false
	}

	return true
}

// CSI K
// Erase in Line (EL), VT100
func (t *Terminal) csiEraseInLineHandler(params []string) (renderRequired bool) {

	n := "0"
	if len(params) > 0 {
		n = params[0]
	}

	switch n {
	case "0", "": //erase adter cursor
		t.GetActiveBuffer().eraseLineFromCursor()
	case "1": // erase to cursor inclusive
		t.GetActiveBuffer().eraseLineToCursor()
	case "2": // erase entire
		t.GetActiveBuffer().eraseLine()
	default:
		return false
	}
	return true
}

// CSI m
// Character Attributes (SGR)
func (t *Terminal) sgrSequenceHandler(params []string) bool {

	if len(params) == 0 {
		params = []string{"0"}
	}

	for i := range params {

		p := strings.Replace(strings.Replace(params[i], "[", "", -1), "]", "", -1)

		switch p {
		case "00", "0", "":
			attr := t.GetActiveBuffer().getCursorAttr()
			*attr = CellAttributes{}
		case "1", "01":
			t.GetActiveBuffer().getCursorAttr().bold = true
		case "2", "02":
			t.GetActiveBuffer().getCursorAttr().dim = true
		case "3", "03":
			t.GetActiveBuffer().getCursorAttr().italic = true
		case "4", "04":
			t.GetActiveBuffer().getCursorAttr().underline = true
		case "5", "05":
			t.GetActiveBuffer().getCursorAttr().blink = true
		case "7", "07":
			t.GetActiveBuffer().getCursorAttr().inverse = true
		case "8", "08":
			t.GetActiveBuffer().getCursorAttr().hidden = true
		case "9", "09":
			t.GetActiveBuffer().getCursorAttr().strikethrough = true
		case "21":
			t.GetActiveBuffer().getCursorAttr().bold = false
		case "22":
			t.GetActiveBuffer().getCursorAttr().dim = false
		case "23":
			t.GetActiveBuffer().getCursorAttr().italic = false
		case "24":
			t.GetActiveBuffer().getCursorAttr().underline = false
		case "25":
			t.GetActiveBuffer().getCursorAttr().blink = false
		case "27":
			t.GetActiveBuffer().getCursorAttr().inverse = false
		case "28":
			t.GetActiveBuffer().getCursorAttr().hidden = false
		case "29":
			t.GetActiveBuffer().getCursorAttr().strikethrough = false
		case "38": // set foreground
			t.GetActiveBuffer().getCursorAttr().fgColour, _ = t.theme.ColourFromAnsi(params[i+1:], false)
			return false
		case "48": // set background
			t.GetActiveBuffer().getCursorAttr().bgColour, _ = t.theme.ColourFromAnsi(params[i+1:], true)
			return false
		case "39":
			t.GetActiveBuffer().getCursorAttr().fgColour = t.theme.DefaultForeground()
		case "49":
			t.GetActiveBuffer().getCursorAttr().bgColour = t.theme.DefaultBackground()
		default:
			bi, err := strconv.Atoi(p)
			if err != nil {
				return false
			}
			i := byte(bi)
			switch true {
			case i >= 30 && i <= 37, i >= 90 && i <= 97:
				t.GetActiveBuffer().getCursorAttr().fgColour = t.theme.ColourFrom4Bit(i)
			case i >= 40 && i <= 47, i >= 100 && i <= 107:
				t.GetActiveBuffer().getCursorAttr().bgColour = t.theme.ColourFrom4Bit(i)
			}

		}
	}

	return false
}

func (t *Terminal) csiSoftResetHandler(params []string) bool {
	t.reset()
	return true
}
