package vtclean

import (
	"bytes"
	"regexp"
	"strconv"
)

// regex based on ECMA-48:
// 1. optional:
//    one of [ or ]
//    any amount of 0x30-0x3f
//    any amount of 0x20-0x2f
// 3. exactly one 0x40-0x7e
var vt100re = regexp.MustCompile(`^\033([\[\]]([0-9:;<=>\?]*)([!"#$%&'()*+,\-./]*))?([@A-Z\[\]^_\x60a-z{|}~])`)
var vt100exc = regexp.MustCompile(`^\033(\[[^a-zA-Z0-9@\?]+|[\(\)]).`)

// this is to handle the RGB escape generated by `tput initc 1 500 500 500`
var vt100long = regexp.MustCompile(`^\033](\d+);([^\033]+)\033\\`)

func Clean(line string, color bool) string {
	var edit = newLineEdit(len(line))
	lineb := []byte(line)

	hadColor := false
	for i := 0; i < len(lineb); {
		c := lineb[i]
		switch c {
		case '\r':
			edit.MoveAbs(0)
		case '\b':
			edit.Move(-1)
		case '\033':
			// set terminal title
			if bytes.HasPrefix(lineb[i:], []byte("\x1b]0;")) {
				pos := bytes.Index(lineb[i:], []byte("\a"))
				if pos != -1 {
					i += pos + 1
					continue
				}
			}
			if m := vt100long.Find(lineb[i:]); m != nil {
				i += len(m)
			} else if m := vt100exc.Find(lineb[i:]); m != nil {
				i += len(m)
			} else if m := vt100re.FindSubmatch(lineb[i:]); m != nil {
				i += len(m[0])
				num := string(m[2])
				n, err := strconv.Atoi(num)
				if err != nil || n > 10000 {
					n = 1
				}
				switch m[4][0] {
				case 'm':
					if color {
						hadColor = true
						edit.Vt100(m[0])
					}
				case '@':
					edit.Insert(bytes.Repeat([]byte{' '}, n))
				case 'G':
					edit.MoveAbs(n)
				case 'C':
					edit.Move(n)
				case 'D':
					edit.Move(-n)
				case 'P':
					edit.Delete(n)
				case 'K':
					switch num {
					case "", "0":
						edit.ClearRight()
					case "1":
						edit.ClearLeft()
					case "2":
						edit.Clear()
					}
				}
			} else {
				i += 1
			}
			continue
		default:
			if c == '\n' || c == '\t' || c >= ' ' {
				edit.Write([]byte{c})
			}
		}
		i += 1
	}
	out := edit.Bytes()
	if hadColor {
		out = append(out, []byte("\033[0m")...)
	}
	return string(out)
}
