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 func(string) string = 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.
*/
type Line struct {
	Text         string              // Text to be displayed
	DefaultColor string              // Default Color to use
	RenderF      func(string) string // Render function (displays string with colors)
	UpdateF      func() string       // 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
}

func (l *Line) LineLength(text *string) {
	var length int
	if l.Width == 0 {
		return
	}

	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
}