package door

import (
	"log"
	"strings"
)

type BorderStyle int

const (
	NONE BorderStyle = iota
	SINGLE
	DOUBLE
	DOUBLE_SINGLE
	SINGLE_DOUBLE
)

type Panel struct {
	X           int
	Y           int
	Width       int
	Style       BorderStyle
	BorderColor string
	Lines       []Line
	Title       string
	TitleColor  string
	TitleOffset int
}

/*
func (p *Panel) Range(withBorder bool) (sx, sy, ex, ey int) {
	sx = p.X
	sy = p.Y
	ex = p.X + p.Width
	ey = p.Y + len(p.Lines)
	if p.Style > 0 {
		if withBorder {
			ex += 2
			ey += 2
		} else {
			sx += 1
			sy += 1
		}
	}
	return
}
*/

func (p *Panel) Length() int {
	return len(p.Lines)
}

func (p *Panel) RightBottomPos() (rx, by int) {
	rx = p.X + p.Width
	by = p.Y + len(p.Lines)
	if p.HasBorder() {
		rx += 2
		by += 2
	}
	return
}

func (p *Panel) HasBorder() bool {
	return int(p.Style) > 0
}

/*
Is X,Y within the Panel?
*/
func (p *Panel) Within(x, y int) (bool, int, int) {
	if x < p.X || y < p.Y {
		return false, 0, 0
	}
	mx, my := p.RightBottomPos()
	if x > mx || y > my {
		return false, 0, 0
	}
	// Ok, it must be within:
	return true, x - p.X, y - p.Y
}

/*
// Panel Clicked?
// If onBorder == true, returns true, 0 for border click.
// Otherwise true, line clicked on (starting with 1)/
func (p *Panel) Clicked(m Mouse, onBorder bool) (bool, int) {
	var sx, sy, ex, ey = p.Range(onBorder)
	var mx = int(m.X)
	var my = int(m.Y)

	if (mx >= sx) && (mx <= ex) {
		if (my >= sy) && (my <= ey) {
			if onBorder {
				if my == sy || my == ey {
					return true, 0
				}
				return true, my - sy
			} else {
				return true, (my - sy) + 1
			}
		}
	}
	return false, 0
}
*/

// Clear out the panel
func (p *Panel) Clear() string {
	var style int = int(p.Style)
	var output string
	var row int = p.Y
	var blank string

	if style > 0 {
		blank = strings.Repeat(" ", p.Width+2)
		output += Goto(p.X, row) + blank
		row++
	} else {
		blank = strings.Repeat(" ", p.Width)
	}

	for _ = range p.Lines {
		output += Goto(p.X, row) + blank
		row++
	}

	if style > 0 {
		output += Goto(p.X, row) + blank
	}
	return output
}

// Output the panel
func (p *Panel) Output() string {
	var style int = int(p.Style)
	var box_style *BoxStyle
	var output string

	if style > 0 {
		box_style = &BOXES[style-1]
	}

	var row int = p.Y
	if style > 0 {
		// Top line / border
		output += Goto(p.X, row) + p.BorderColor + box_style.Top_Left
		if p.Title != "" {
			if p.TitleOffset+len(p.Title) > p.Width {
				log.Panicf("Panel (not wide enough) Width %d : Title size %d + offset %d = %d\n",
					p.Width, len(p.Title), p.TitleOffset, p.TitleOffset+len(p.Title))
			}
			output += strings.Repeat(box_style.Top, p.TitleOffset) + p.TitleColor + p.Title + p.BorderColor
		}
		output += strings.Repeat(box_style.Top, p.Width-(p.TitleOffset+len(p.Title))) + box_style.Top_Right
		row++
	}

	for _, line := range p.Lines {
		output += Goto(p.X, row)
		line.LineLength(&line.Text)

		var joined bool = false

		if style > 0 {
			top := box_style.Top
			if line.Text[0:len(top)] == top {
				// Yes, this line needs to be joined...
				output += p.BorderColor + box_style.Middle_Left + line.Output() + p.BorderColor + box_style.Middle_Right
				joined = true
			}
		}

		if !joined {
			if style > 0 {
				output += p.BorderColor + box_style.Side
			}
			output += line.Output()
			if style > 0 {
				output += p.BorderColor + box_style.Side
			}
		}

		row++
	}

	if style > 0 {
		// Bottom / border
		output += Goto(p.X, row) + p.BorderColor + box_style.Bottom_Left
		output += strings.Repeat(box_style.Top, p.Width) + box_style.Bottom_Right
	}
	return output
}

// Output anything that has updated
func (p *Panel) Update() string {
	var output string
	var style int = int(p.Style)
	var row, col int

	row = p.Y
	col = p.X
	if style > 0 {
		row++
		col++
	}

	for idx := range p.Lines {
		if p.Lines[idx].Update() {
			// Yes, line was updated
			output += Goto(col, row) + p.Lines[idx].Output()
		}
		row++
	}
	return output
}

// Output the updated line
func (p *Panel) UpdateLine(index int) string {
	var output string
	var style int = int(p.Style)

	p.Lines[index].Update()
	var row, col int

	row = p.Y + index
	col = p.X
	if style > 0 {
		row++
		col++
	}
	var line *Line = &p.Lines[index]
	output += Goto(col, row) + line.Output()
	return output
}

// Position Cursor at the "end" of the panel
func (p *Panel) GotoEnd() string {
	rx, by := p.RightBottomPos()
	return Goto(rx, by)
}

// Is the top line of this style a single line?
func Single(bs BorderStyle) bool {
	switch bs {
	case SINGLE, SINGLE_DOUBLE:
		return true
	default:
		return false
	}
}

// Create a spacer line that will be connected maybe to the sides.
func (p *Panel) Spacer() Line {
	var l Line = Line{}
	var pos int

	if Single(p.Style) {
		pos = 0
	} else {
		pos = 1
	}

	l.Text = strings.Repeat(BOXES[pos].Top, p.Width)
	return l
}

func (p *Panel) Center() {
	p.CenterX()
	p.CenterY()
}

func (p *Panel) CenterX() {
	p.X = (Width - p.Width) / 2
}

func (p *Panel) CenterY() {
	p.Y = (Height - len(p.Lines)) / 2
}