package door

import (
	"log"
	"strings"
	"unicode"
)

/*
door.Line - Display a line of text

Example:

	    var basicLine door.Line = {Text: "Welcome",
		    DefaultColor: door.Color("BRIGHT YELLOW"),
		}

	    d.Write(basicLine.Output() + door.Reset + door.CRNL)

This outputs Welcome in Bright/Bold Yellow.

Example Render:

	    var renderAlphaDigit ColorRender = func(text string) string {
			var r door.Render{Text: text}
			var alpha string = door.ColorText("BOLD YELLOW")
			var digit string = door.ColorText("BOLD GREEN")
			var other string = door.Reset
			for _, letter := range text {
				if unicode.IsAlpha(letter) {
					r.Append(alpha, 1)
				} else {
					if unicode.IsDigit(letter) {
						r.Append(digit, 1)
					} else {
						r.Append(other, 1)
					}
				}
			}
			return r.Result
		}
		var renderLine door.Line = {Text: "Render - 12345",
			RenderF: renderAlphaDigit,
		}
		d.Write(renderLine.Output() + door.Reset + door.CRNL)

This outputs "Render" in Yellow, "12345" in Green, and " - " in White.

Example Update:

		var updateTime() string {
			var now time.Time = time.Now()
			var result string = now.Format("3:04:05 PM")
	        return result
		}
		var timeLine door.Line = {Text: updateTime(),
			UpdateF: updateTime,
		}
		d.Write(timeLine.Output() + door.CRNL)
		time.Sleep(time.Second)

		// Check timeLine for update
		if timeLine.Update() {
			// Yes, there's an update to the time, output it.
			d.Write(timeLine.Output() + door.CRNL)
		}

		if timeLine.Update() {
			// This isn't called.  There were no changes.
			d.Write(timeLine.Output() + door.CRNL)
		}

This outputs the Current time in 12 hour format.  It pauses for a second,
and outputs the new time.
*/

/*
Line of text to display.
*/
type Line struct {
	Text         string      // Text to be displayed
	DefaultColor string      // Default Color to use
	RenderF      ColorRender // Render function (displays string with colors)
	UpdateF      Updater     // Update function updates the text
	Width        int         // Line length
}

/*
Line Update - This calls the UpdateF if present.

Returns true if the line has been updated, and there's an Update function.
*/
func (l *Line) Update() bool {
	if l.UpdateF == nil {
		return false
	}
	var NewText string = l.UpdateF()
	l.LineLength(&NewText)
	if NewText != l.Text {
		l.Text = NewText
		return true
	}
	return false
}

// If a line Width has been set, make sure we match it.
func (l *Line) LineLength(text *string) {
	if l.Width == 0 {
		return
	}
	var length int = StringLen(*text)
	/*
		if Unicode {
			length = len([]rune(*text))
		} else {
			length = len([]byte(*text))
		}
	*/
	if length > l.Width {
		log.Printf("ERROR: Line Width %d: Have %d\n", l.Width, length)
	} else {
		*text += strings.Repeat(" ", l.Width-length)
	}
}

/*
Line Output - returns a string with ANSI Color codes.

If there is no RenderF, we use the DefaultColor.  Otherwise we pass the text
to RenderF and return what it returns.
*/
func (l *Line) Output() string {
	l.LineLength(&l.Text)
	if l.RenderF == nil {
		return l.DefaultColor + l.Text
	} else {
		return l.RenderF(l.Text)
	}
}

/*
door.Render - Helper for Line RenderF (Render Function)

Example:

	// RenderStatus - KEY_COLOR[key] COLON_COLOR[:] VALUE_COLOR[value]
	func RenderStatus(text string) string {
		var r door.Render = Render{Line: text}
		var bool inValue = false
		var key string = door.ColorText("BLUE")
		var colon string = door.ColorText("BRIGHT WHITE")
		var value string = door.ColorText("MAGENTA")

		for _, letter := range text {
			if letter == ':' {
				r.Append(colon, 1)
				inValue = true
			} else {
				if inValue {
					r.Append(value, 1)
				} else {
					r.Append(key, 1)
				}
			}
		}
		return r.Result
	}
*/
type Render struct {
	Line      string // Original Text
	Result    string // Output Result
	Pos       int    // Current Position
	LastColor string // LastColor code sent
}

/*
Render.Append - Output len number of characters in the color.

This uses Render.LastColor to tell if we've already sent that color before.
*/
func (r *Render) Append(color string, len int) {
	if color != r.LastColor {
		r.LastColor = color
		r.Result += color
	}

	if Unicode {
		// Treat unicode as []rune.
		r.Result += string([]rune(r.Line)[r.Pos : r.Pos+len])
	} else {
		r.Result += r.Line[r.Pos : r.Pos+len]
	}
	r.Pos += len
}

// RenderBlueYellow - Uppercase is Bold Blue, everything else is Yellow.
// This is an example of using the door.Render routines.
func RenderBlueYellow(text string) string {
	var r Render = Render{Line: text}

	var blue string = ColorText("BOLD BLUE")
	var yellow string = ColorText("BOLD YELLOW")

	for _, letter := range text {
		if unicode.IsUpper(letter) {
			r.Append(blue, 1)
		} else {
			r.Append(yellow, 1)
		}
	}
	return r.Result
}