package pterm

import (
	"strings"

	"github.com/atomicgo/cursor"

	"github.com/pterm/pterm/internal"
)

// DefaultArea is the default area printer.
var DefaultArea = AreaPrinter{}

// AreaPrinter prints an area which can be updated easily.
// use this printer for live output like charts, algorithm visualizations, simulations and even games.
type AreaPrinter struct {
	RemoveWhenDone bool
	Fullscreen     bool
	Center         bool

	content  string
	isActive bool

	area *cursor.Area
}

// GetContent returns the current area content.
func (p *AreaPrinter) GetContent() string {
	return p.content
}

// WithRemoveWhenDone removes the AreaPrinter content after it is stopped.
func (p AreaPrinter) WithRemoveWhenDone(b ...bool) *AreaPrinter {
	p.RemoveWhenDone = internal.WithBoolean(b)
	return &p
}

// WithFullscreen sets the AreaPrinter height the same height as the terminal, making it fullscreen.
func (p AreaPrinter) WithFullscreen(b ...bool) *AreaPrinter {
	p.Fullscreen = internal.WithBoolean(b)
	return &p
}

// WithCenter centers the AreaPrinter content to the terminal.
func (p AreaPrinter) WithCenter(b ...bool) *AreaPrinter {
	p.Center = internal.WithBoolean(b)
	return &p
}

// Update overwrites the content of the AreaPrinter.
// Can be used live.
func (p *AreaPrinter) Update(text ...interface{}) {
	if p.area == nil {
		newArea := cursor.NewArea()
		p.area = &newArea
	}
	str := Sprint(text...)
	p.content = str

	if p.Center {
		str = DefaultCenter.Sprint(str)
	}

	if p.Fullscreen {
		str = strings.TrimRight(str, "\n")
		height := GetTerminalHeight()
		contentHeight := strings.Count(str, "\n")

		topPadding := 0
		bottomPadding := height - contentHeight - 2

		if p.Center {
			topPadding = (bottomPadding / 2) + 1
			bottomPadding /= 2
		}

		if height > contentHeight {
			str = strings.Repeat("\n", topPadding) + str
			str += strings.Repeat("\n", bottomPadding)
		}
	}
	p.area.Update(str)
}

// Start the AreaPrinter.
func (p *AreaPrinter) Start(text ...interface{}) (*AreaPrinter, error) {
	p.isActive = true
	str := Sprint(text...)
	newArea := cursor.NewArea()
	p.area = &newArea

	p.Update(str)

	return p, nil
}

// Stop terminates the AreaPrinter immediately.
// The AreaPrinter will not resolve into anything.
func (p *AreaPrinter) Stop() error {
	p.isActive = false
	if p.RemoveWhenDone {
		p.area.Clear()
	}
	return nil
}

// GenericStart runs Start, but returns a LivePrinter.
// This is used for the interface LivePrinter.
// You most likely want to use Start instead of this in your program.
func (p *AreaPrinter) GenericStart() (*LivePrinter, error) {
	_, _ = p.Start()
	lp := LivePrinter(p)
	return &lp, nil
}

// GenericStop runs Stop, but returns a LivePrinter.
// This is used for the interface LivePrinter.
// You most likely want to use Stop instead of this in your program.
func (p *AreaPrinter) GenericStop() (*LivePrinter, error) {
	_ = p.Stop()
	lp := LivePrinter(p)
	return &lp, nil
}
