// Copyright 2017-2025 WIT.COM Inc. All rights reserved.
// Use of this source code is governed by the GPL 3.0

package main

import (
	"strings"

	"go.wit.com/log"
	"go.wit.com/widget"
)

/*
	gocui defines the offset like this:

 	   width -> increases to the right
	----------------------------------    hieght
	|            H = 1               |   increases
	|                                |       |
	| W = 1                   W = 18 |       |
	|                                |       v
	|            H = 5               |   downwards
	-------------------------------------
*/

// moves the gocui view to the W and H offset on the screen
func (tk *guiWidget) MoveToOffset(W, H int) {
	tk.gocuiSetWH(W, H)
}

// returns where the corner of widget starts (upper left)
func (tk *guiWidget) Position() (int, int) {
	return tk.gocuiSize.w0, tk.gocuiSize.h0
}

func (w *guiWidget) placeBox(startW int, startH int) {
	if w.WidgetType() != widget.Box {
		return
	}

	w.full.w0 = startW
	w.full.h0 = startH
	newW := startW
	newH := startH

	for _, child := range w.children {
		sizeW, sizeH := child.Size()
		log.Log(INFO, "BOX START size(W,H) =", sizeW, sizeH, "new(W,H) =", newW, newH)
		child.placeWidgets(newW, newH)
		// re-get the Size (they should not have changed, but maybe they can?)
		// TODO: figure this out or report that they did
		sizeW, sizeH = child.Size()
		if w.Direction() == widget.Vertical {
			log.Log(INFO, "BOX IS VERTICAL  ", w.String(), "newWH()", newW, newH, "child()", sizeW, sizeH, child.String())
			// expand based on the child height
			newH += sizeH
		} else {
			log.Log(INFO, "BOX IS HORIZONTAL", w.String(), "newWH()", newW, newH, "child()", sizeW, sizeH, child.String())
			// expand based on the child width
			newW += sizeW
		}
		log.Log(INFO, "BOX END   size(W,H) =", sizeW, sizeH, "new(W,H) =", newW, newH)
	}
}

func (tk *guiWidget) placeWidgets(startW int, startH int) (int, int) {
	if tk == nil {
		return 0, 0
	}
	if me.treeRoot == nil {
		return 0, 0
	}
	if tk.Hidden() {
		return 0, 0
	}

	tk.startW = startW
	tk.startH = startH

	switch tk.WidgetType() {
	case widget.Window:
		tk.full.w0 = startW
		tk.full.h0 = startH
		startW += -2
		startH += 3
		for _, child := range tk.children {
			child.placeWidgets(startW, startH)
			sizeW, _ := child.Size()
			startW += sizeW + 4 // add the width to move the next widget over

		}
		return startW, startH
	case widget.Tab:
	case widget.Grid:
		// tk.dumpWidget(fmt.Sprintf("PlaceGridS(%d,%d)", startW, startH))
		// if you reset the values here, grid horizontal stacking doesn't work anymore
		// tk.widths = make(map[int]int)  // how tall each row in the grid is
		// tk.heights = make(map[int]int) // how wide each column in the grid is
		newW, newH := tk.placeGrid(startW, startH)
		tk.full.w0 = newW
		tk.full.h0 = newH
		tk.full.w1 = newW
		tk.full.h1 = newH
		// tk.dumpWidget(fmt.Sprintf("PlaceGridE(%d,%d)", newW, newH))
		return newW, newH
	case widget.Box:
		tk.placeBox(startW, startH)
		tk.full.w0 = startW
		tk.full.h0 = startH
		tk.full.w1 = startW
		tk.full.h1 = startH
		// tk.dumpWidget(fmt.Sprintf("PlaceBox(%d,%d)", startW, startH))
		return 0, 0
	case widget.Stdout:
		tk.setStdoutWH(startW, startH)
		return tk.gocuiSize.Width(), tk.gocuiSize.Height()
	case widget.Group:
		// move the group to the parent's next location
		tk.gocuiSetWH(startW, startH)
		tk.full.w0 = startW
		tk.full.h0 = startH
		tk.full.w1 = startW
		tk.full.h1 = startH

		newW := startW + me.GroupPadW
		newH := startH + 1 // normal hight of the group label
		var maxW int = 0
		// now move all the children aka: run place() on them
		for _, child := range tk.children {
			sizeW, sizeH := child.placeWidgets(newW, newH)
			// newR := child.realGocuiSize()
			// w := newR.w1 - newR.w0
			// h := newR.h1 - newR.h0

			// increment straight down
			newH += sizeH + 1
			if sizeW > maxW {
				maxW = sizeW
			}
			log.Log(INFO, "REAL HEIGHT sizeW:", sizeW, "sizeH:", sizeH)
		}
		newH = newH - startH
		// tk.dumpWidget(fmt.Sprintf("PlaceGroup(%d,%d)", maxW, newH))
		return maxW, newH
	case widget.Button:
		if tk.isWindowDense() && tk.isInGrid() {
			tk.frame = false
			// tk.color = nil
			// tk.defaultColor = nil
			/*
				if tk.IsEnabled() {
					tk.setColorButtonDense()
				} else {
					tk.setColorDisable()
				}
			*/
			// if tk.full.Height() > 0 {
			tk.full.h1 = tk.full.h0
			// }
		}
		tk.gocuiSetWH(startW, startH)
		return tk.gocuiSize.Width(), tk.gocuiSize.Height()
	default:
		tk.gocuiSetWH(startW, startH)
		return tk.gocuiSize.Width(), tk.gocuiSize.Height()
	}
	return 0, 0
}

func (tk *guiWidget) isWindowDense() bool {
	if tk.WidgetType() == widget.Window {
		return tk.window.dense
	}
	if tk.parent == nil {
		return true
	}
	return tk.parent.isWindowDense()
}

func (tk *guiWidget) isInGrid() bool {
	if tk.WidgetType() == widget.Grid {
		return true
	}
	if tk.parent == nil {
		return true
	}
	return tk.parent.isInGrid()
}

func (w *guiWidget) placeGrid(startW int, startH int) (int, int) {
	// w.showWidgetPlacement("grid0:")
	if w.WidgetType() != widget.Grid {
		return 0, 0
	}

	dense := w.isWindowDense()

	w.full.w0 = startW
	w.full.h0 = startH

	// first compute the max sizes of the rows and columns
	for _, child := range w.children {
		childW, childH := child.placeWidgets(child.startW, child.startH)

		// set the child's realWidth, and grid offset
		if w.widths[child.GridW()] < childW {
			w.widths[child.GridW()] = childW
		}
		if w.heights[child.GridH()] < childH {
			w.heights[child.GridH()] = childH
		}
		if dense {
			if w.heights[child.GridH()] > 0 {
				w.heights[child.GridH()] = 1
			} else {
				w.heights[child.GridH()] = 0
			}
		}

		// child.showWidgetPlacement("grid: ")
		log.Log(INFO, "placeGrid:", child.String(), "child()", childW, childH, "At()", child.GridW(), child.GridH())
	}

	var maxW int = 0
	var maxH int = 0

	// find the width and height offset of the grid for AtW,AtH
	for _, child := range w.children {
		// child.showWidgetPlacement("grid1:")

		var totalW, totalH int
		for i, w := range w.widths {
			if i < child.GridW() {
				totalW += w
			}
		}
		for i, h := range w.heights {
			if i < child.GridH() {
				totalH += h
			}
		}

		// the new corner to move the child to
		newW := startW + totalW
		newH := startH + totalH

		if totalW > maxW {
			maxW = totalW
		}
		if totalH > maxH {
			maxH = totalH
		}

		log.Log(INFO, "placeGrid:", child.String(), "new()", newW, newH, "At()", child.GridW(), child.GridH())
		child.placeWidgets(newW, newH)
		// child.showWidgetPlacement("grid2:")
	}
	// w.showWidgetPlacement("grid3:")
	w.full.w1 = startW + maxW
	w.full.h1 = startH + maxH
	return maxW, maxH
}

// computes the real, actual size of all the gocli objects in a widget
func (w *guiWidget) realGocuiSize() *rectType {
	var f func(tk *guiWidget, r *rectType)
	newR := new(rectType)

	outputW, outputH := w.Size()
	// initialize the values to opposite
	newR.w0 = outputW
	newR.h0 = outputH
	if me.baseGui != nil {
		maxW, maxH := me.baseGui.Size()
		newR.w0 = maxW
		newR.h0 = maxH
	}
	newR.w1 = 0
	newR.h1 = 0

	// expand the rectangle to the biggest thing displayed
	f = func(tk *guiWidget, r *rectType) {
		newR := tk.gocuiSize
		if !tk.isFake {
			if r.w0 > newR.w0 {
				r.w0 = newR.w0
			}
			if r.h0 > newR.h0 {
				r.h0 = newR.h0
			}
			if r.w1 < newR.w1 {
				r.w1 = newR.w1
			}
			if r.h1 < newR.h1 {
				r.h1 = newR.h1
			}
		}
		for _, child := range tk.children {
			f(child, r)
		}
	}
	f(w, newR)
	return newR
}

/*
func textSize(n *tree.Node) (int, int) {
	var tk *guiWidget
	tk = n.TK.(*guiWidget)
	var width, height int

	for _, s := range strings.Split(widget.GetString(tk.value), "\n") {
		if width < len(s) {
			width = len(s)
		}
		height += 1
	}
	return width, height
}
*/

func (tk *guiWidget) gocuiSetWH(sizeW, sizeH int) {
	w := len(tk.GetLabel())
	lines := strings.Split(tk.GetLabel(), "\n")
	h := len(lines)

	if tk.Hidden() {
		tk.gocuiSize.w0 = 0
		tk.gocuiSize.h0 = 0
		tk.gocuiSize.w1 = 0
		tk.gocuiSize.h1 = 0
		return
	}

	// tk := n.tk
	if tk.isFake {
		tk.gocuiSize.w0 = sizeW
		tk.gocuiSize.h0 = sizeH
		tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
		tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
		return
	}

	if tk.frame {
		tk.gocuiSize.w0 = sizeW
		tk.gocuiSize.h0 = sizeH
		tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + me.FramePadW
		tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + me.FramePadH
	} else {
		tk.gocuiSize.w0 = sizeW - 1
		tk.gocuiSize.h0 = sizeH - 1
		tk.gocuiSize.w1 = tk.gocuiSize.w0 + w + 1
		tk.gocuiSize.h1 = tk.gocuiSize.h0 + h + 1
	}
}

func (tk *guiWidget) setStdoutWH(sizeW, sizeH int) {
	tk.gocuiSize.w0 = sizeW
	tk.gocuiSize.h0 = sizeH
	tk.gocuiSize.w1 = tk.gocuiSize.w0 + me.stdout.w
	tk.gocuiSize.h1 = tk.gocuiSize.h0 + me.stdout.h
}