package cview

import (
	
	
	
	
	
)

// The states of the ANSI escape code parser.
const (
	ansiText = iota
	ansiEscape
	ansiSubstring
	ansiControlSequence
)

// ansi is a io.Writer which translates ANSI escape codes into cview color
// tags.
type ansi struct {
	io.Writer

	// Reusable buffers.
	buffer                        *bytes.Buffer // The entire output text of one Write().
	csiParameter, csiIntermediate *bytes.Buffer // Partial CSI strings.
	attributes                    string        // The buffer's current text attributes (a tview attribute string).

	// The current state of the parser. One of the ansi constants.
	state int
}

// ANSIWriter returns an io.Writer which translates any ANSI escape codes
// written to it into cview color tags. Other escape codes don't have an effect
// and are simply removed. The translated text is written to the provided
// writer.
func ( io.Writer) io.Writer {
	return &ansi{
		Writer:          ,
		buffer:          new(bytes.Buffer),
		csiParameter:    new(bytes.Buffer),
		csiIntermediate: new(bytes.Buffer),
		state:           ansiText,
	}
}

// Write parses the given text as a string of runes, translates ANSI escape
// codes to color tags and writes them to the output writer.
func ( *ansi) ( []byte) (int, error) {
	defer func() {
		.buffer.Reset()
	}()

	for ,  := range string() {
		switch .state {

		// We just entered an escape sequence.
		case ansiEscape:
			switch  {
			case '[': // Control Sequence Introducer.
				.csiParameter.Reset()
				.csiIntermediate.Reset()
				.state = ansiControlSequence
			case 'c': // Reset.
				fmt.Fprint(.buffer, "[-:-:-]")
				.state = ansiText
			case 'P', ']', 'X', '^', '_': // Substrings and commands.
				.state = ansiSubstring
			default: // Ignore.
				.state = ansiText
			}

		// CSI Sequences.
		case ansiControlSequence:
			switch {
			case  >= 0x30 &&  <= 0x3f: // Parameter bytes.
				if ,  := .csiParameter.WriteRune();  != nil {
					return 0, 
				}
			case  >= 0x20 &&  <= 0x2f: // Intermediate bytes.
				if ,  := .csiIntermediate.WriteRune();  != nil {
					return 0, 
				}
			case  >= 0x40 &&  <= 0x7e: // Final byte.
				switch  {
				case 'E': // Next line.
					,  := strconv.Atoi(.csiParameter.String())
					if  == 0 {
						 = 1
					}
					fmt.Fprint(.buffer, strings.Repeat("\n", ))
				case 'm': // Select Graphic Rendition.
					var ,  string
					 := .csiParameter.String()
					 := strings.Split(, ";")
					if len() == 0 || len() == 1 && [0] == "0" {
						// Reset.
						.attributes = ""
						if ,  := .buffer.WriteString("[-:-:-]");  != nil {
							return 0, 
						}
						break
					}
					 := func( int) string {
						if  < 0 ||  > 15 {
							return "black"
						}
						return []string{
							"black",
							"maroon",
							"green",
							"olive",
							"navy",
							"purple",
							"teal",
							"silver",
							"gray",
							"red",
							"lime",
							"yellow",
							"blue",
							"fuchsia",
							"aqua",
							"white",
						}[]
					}
				:
					for ,  := range  {
						switch  {
						case "1", "01":
							if strings.IndexRune(.attributes, 'b') < 0 {
								.attributes += "b"
							}
						case "2", "02":
							if strings.IndexRune(.attributes, 'd') < 0 {
								.attributes += "d"
							}
						case "3", "03":
							if strings.IndexRune(.attributes, 'i') < 0 {
								.attributes += "i"
							}
						case "4", "04":
							if strings.IndexRune(.attributes, 'u') < 0 {
								.attributes += "u"
							}
						case "5", "05":
							if strings.IndexRune(.attributes, 'l') < 0 {
								.attributes += "l"
							}
						case "7", "07":
							if strings.IndexRune(.attributes, 'r') < 0 {
								.attributes += "r"
							}
						case "9", "09":
							if strings.IndexRune(.attributes, 's') < 0 {
								.attributes += "s"
							}
						case "22":
							if  := strings.IndexRune(.attributes, 'b');  >= 0 {
								.attributes = .attributes[:] + .attributes[+1:]
							}
							if  := strings.IndexRune(.attributes, 'd');  >= 0 {
								.attributes = .attributes[:] + .attributes[+1:]
							}
						case "24":
							if  := strings.IndexRune(.attributes, 'u');  >= 0 {
								.attributes = .attributes[:] + .attributes[+1:]
							}
						case "25":
							if  := strings.IndexRune(.attributes, 'l');  >= 0 {
								.attributes = .attributes[:] + .attributes[+1:]
							}
						case "30", "31", "32", "33", "34", "35", "36", "37":
							,  := strconv.Atoi()
							 = ( - 30)
						case "39":
							 = "-"
						case "40", "41", "42", "43", "44", "45", "46", "47":
							,  := strconv.Atoi()
							 = ( - 40)
						case "49":
							 = "-"
						case "90", "91", "92", "93", "94", "95", "96", "97":
							,  := strconv.Atoi()
							 = ( - 82)
						case "100", "101", "102", "103", "104", "105", "106", "107":
							,  := strconv.Atoi()
							 = ( - 92)
						case "38", "48":
							var  string
							if len() > +1 {
								if [+1] == "5" && len() > +2 { // 8-bit colors.
									,  := strconv.Atoi([+2])
									if  <= 15 {
										 = ()
									} else if  <= 231 {
										 := ( - 16) / 36
										 := (( - 16) / 6) % 6
										 := ( - 16) % 6
										 = fmt.Sprintf("#%02x%02x%02x", 255*/5, 255*/5, 255*/5)
									} else if  <= 255 {
										 := 255 * ( - 232) / 23
										 = fmt.Sprintf("#%02x%02x%02x", , , )
									}
								} else if [+1] == "2" && len() > +4 { // 24-bit colors.
									,  := strconv.Atoi([+2])
									,  := strconv.Atoi([+3])
									,  := strconv.Atoi([+4])
									 = fmt.Sprintf("#%02x%02x%02x", , , )
								}
							}
							if len() > 0 {
								if  == "38" {
									 = 
								} else {
									 = 
								}
							}
							break 
						}
					}
					var  string
					if len(.attributes) > 0 {
						 = ":"
					}
					if len() > 0 || len() > 0 || len(.attributes) > 0 {
						fmt.Fprintf(.buffer, "[%s:%s%s%s]", , , , .attributes)
					}
				}
				.state = ansiText
			default: // Undefined byte.
				.state = ansiText // Abort CSI.
			}

			// We just entered a substring/command sequence.
		case ansiSubstring:
			if  == 27 { // Most likely the end of the substring.
				.state = ansiEscape
			} // Ignore all other characters.

			// "ansiText" and all others.
		default:
			if  == 27 {
				// This is the start of an escape sequence.
				.state = ansiEscape
			} else {
				// Just a regular rune. Send to buffer.
				if ,  := .buffer.WriteRune();  != nil {
					return 0, 
				}
			}
		}
	}

	// Write buffer to target writer.
	,  := .buffer.WriteTo(.Writer)
	if  != nil {
		return int(), 
	}
	return len(), nil
}

// TranslateANSI replaces ANSI escape sequences found in the provided string
// with cview's color tags and returns the resulting string.
func ( string) string {
	var  bytes.Buffer
	 := ANSIWriter(&)
	.Write([]byte())
	return .String()
}