package goterm

import (
	"bytes"
	"regexp"
	"strings"
	_ "unicode/utf8"
)

const DEFAULT_BORDER = "- │ ┌ ┐ └ ┘"

// Box allows you to create independent parts of screen, with its own buffer and borders.
// Can be used for creating modal windows
//
// Generates boxes likes this:
// ┌--------┐
// │hello   │
// │world   │
// │        │
// └--------┘
//
type Box struct {
	Buf *bytes.Buffer

	Width  int
	Height int

	// To get even padding: PaddingX ~= PaddingY*4
	PaddingX int
	PaddingY int

	// Should contain 6 border pieces separated by spaces
	//
	// Example border:
	//   "- │ ┌ ┐ └ ┘"
	Border string

	Flags int // Not used now
}

// Create new Box.
// Width and height can be relative:
//
//    // Create box with 50% with of current screen and 10 lines height
//    box := tm.NewBox(50|tm.PCT, 10, 0)
//
func NewBox(width, height int, flags int) *Box {
	width, height = GetXY(width, height)

	box := new(Box)
	box.Buf = new(bytes.Buffer)
	box.Width = width
	box.Height = height
	box.Border = DEFAULT_BORDER
	box.PaddingX = 1
	box.PaddingY = 0
	box.Flags = flags

	return box
}

func (b *Box) Write(p []byte) (int, error) {
	return b.Buf.Write(p)
}

var ANSI_RE = regexp.MustCompile(`\\0\d+\[\d+(?:;\d+)?m`)

// String renders Box
func (b *Box) String() (out string) {
	borders := strings.Split(b.Border, " ")
	lines := strings.Split(b.Buf.String(), "\n")

	// Border + padding
	prefix := borders[1] + strings.Repeat(" ", b.PaddingX)
	suffix := strings.Repeat(" ", b.PaddingX) + borders[1]

	offset := b.PaddingY + 1 // 1 is border width

	// Content width without borders and padding
	contentWidth := b.Width - (b.PaddingX+1)*2
	for y := 0; y < b.Height; y++ {
		var line string

		switch {
		// Draw borders for first line
		case y == 0:
			line = borders[2] + strings.Repeat(borders[0], b.Width-2) + borders[3]

		// Draw borders for last line
		case y == (b.Height - 1):
			line = borders[4] + strings.Repeat(borders[0], b.Width-2) + borders[5]

		// Draw top and bottom padding
		case y <= b.PaddingY || y >= (b.Height-b.PaddingY):
			line = borders[1] + strings.Repeat(" ", b.Width-2) + borders[1]

		// Render content
		default:
			if len(lines) > y-offset {
				line = lines[y-offset]
			} else {
				line = ""
			}

			r := []rune(line)

			lastAnsii := ""
			withoutAnsii := []rune{}
			withOffset := []rune{}
			i := 0

			for {
				if i >= len(r) {
					break
				}

				if r[i] == 27 {
					lastAnsii = ""
					withOffset = append(withOffset, r[i])
					lastAnsii += string(r[i])
					i++
					for {

						i++
						if i > len(r) {
							break
						}

						withOffset = append(withOffset, r[i])
						lastAnsii += string(r[i])

						if r[i] == 'm' {
							i++
							break
						}
					}
				}

				if i >= len(r) {
					break
				}

				withoutAnsii = append(withoutAnsii, r[i])

				if len(withoutAnsii) <= contentWidth {
					withOffset = append(withOffset, r[i])
				}

				i++
			}

			if len(withoutAnsii) > contentWidth {
				// If line is too large limit it
				line = string(withOffset)
			} else {
				// If line is too small enlarge it by adding spaces
				line += strings.Repeat(" ", contentWidth-len(withoutAnsii))
			}

			if lastAnsii != "" {
				line += RESET
			}

			line = prefix + line + suffix
		}

		// Don't add newline for last element
		if y != b.Height-1 {
			line += "\n"
		}

		out += line
	}

	return out
}
