package wpdf

import (
	"bytes"
	"github.com/jung-kurt/gofpdf"
	"gogs.mikescher.com/BlackForestBytes/goext/langext"
)

type WPDFBuilder struct {
	b           *gofpdf.Fpdf
	tr          func(string) string
	cellHeight  float64
	cellSpacing float64
	fontName    PDFFontFamily
	fontStyle   PDFFontStyle
	fontSize    float64
	debug       bool
}

type PDFMargins struct {
	Left  float64
	Top   float64
	Right float64
}

type PDFColor struct {
	R int
	G int
	B int
}

func NewPDFBuilder(orientation PDFOrientation, size PDFSize, unicode bool) *WPDFBuilder {

	fpdfbuilder := gofpdf.New(string(orientation), "mm", string(size), "")

	var tr func(string) string
	if unicode {
		tr = fpdfbuilder.UnicodeTranslatorFromDescriptor("")
	} else {
		tr = func(s string) string { return s }
	}

	b := &WPDFBuilder{
		b:           fpdfbuilder,
		tr:          tr,
		cellHeight:  5,
		cellSpacing: 1,
	}

	b.SetMargins(PDFMargins{Left: 15, Top: 25, Right: 15}) // default values
	b.SetFont(FontHelvetica, Normal, 12)                   // ensures font is set

	return b
}

func (b *WPDFBuilder) FPDF() *gofpdf.Fpdf {
	return b.b
}

func (b *WPDFBuilder) SetMargins(v PDFMargins) {
	b.b.SetMargins(v.Left, v.Top, v.Right)
}

func (b *WPDFBuilder) AddPage() {
	b.b.AddPage()

	if b.debug {

		ml, mt, mr, mb := b.GetMargins()
		pw, ph := b.GetPageSize()

		b.Rect(pw-ml-mr, ph-mt-mb, RectOutline, NewPDFRectOpt().X(ml).Y(mt).LineWidth(0.25).DrawColor(0, 0, 128))

		b.Rect(pw, mt, RectFill, NewPDFRectOpt().X(0).Y(0).FillColor(0, 0, 255).Alpha(0.2, BlendNormal))
		b.Rect(ml, ph-mt-mb, RectFill, NewPDFRectOpt().X(0).Y(mt).FillColor(0, 0, 255).Alpha(0.2, BlendNormal))
		b.Rect(mr, ph-mt-mb, RectFill, NewPDFRectOpt().X(pw-mr).Y(mt).FillColor(0, 0, 255).Alpha(0.2, BlendNormal))
		b.Rect(pw, mb, RectFill, NewPDFRectOpt().X(0).Y(ph-mb).FillColor(0, 0, 255).Alpha(0.2, BlendNormal))
	}
}

func (b *WPDFBuilder) SetTextColor(cr, cg, cb int) {
	b.b.SetTextColor(cr, cg, cb)
}

func (b *WPDFBuilder) GetTextColor() (cr, cg, cb int) {
	return b.b.GetTextColor()
}

func (b *WPDFBuilder) SetDrawColor(cr, cg, cb int) {
	b.b.SetDrawColor(cr, cg, cb)
}

func (b *WPDFBuilder) GetDrawColor() (cr, cg, cb int) {
	return b.b.GetDrawColor()
}

func (b *WPDFBuilder) SetFillColor(cr, cg, cb int) {
	b.b.SetFillColor(cr, cg, cb)
}

func (b *WPDFBuilder) GetFillColor() (cr, cg, cb int) {
	return b.b.GetFillColor()
}

func (b *WPDFBuilder) SetLineWidth(w float64) {
	b.b.SetLineWidth(w)
}

func (b *WPDFBuilder) GetLineWidth() float64 {
	return b.b.GetLineWidth()
}

func (b *WPDFBuilder) SetFont(fontName PDFFontFamily, fontStyle PDFFontStyle, fontSize float64) {
	b.b.SetFont(string(fontName), string(fontStyle), fontSize)

	b.fontName = fontName
	b.fontStyle = fontStyle
	b.fontSize = fontSize

	b.cellHeight = b.b.PointConvert(fontSize)
}

func (b *WPDFBuilder) GetFontSize() float64 {
	return b.fontSize
}

func (b *WPDFBuilder) GetFontFamily() PDFFontStyle {
	return b.fontStyle
}

func (b *WPDFBuilder) GetFontStyle() float64 {
	return b.fontSize
}

func (b *WPDFBuilder) SetCellSpacing(h float64) {
	b.cellSpacing = h
}

func (b *WPDFBuilder) Ln(h float64) {
	xBefore, yBefore := b.GetXY()

	b.b.Ln(h)

	yAfter := b.GetY()

	if b.debug {

		_, _, mr, _ := b.GetMargins()
		pw, _ := b.GetPageSize()

		b.Rect(pw-mr-xBefore, yAfter-yBefore, RectOutline, NewPDFRectOpt().X(xBefore).Y(yBefore).LineWidth(0.25).DrawColor(128, 128, 0).Alpha(0.5, BlendNormal))
		b.Rect(pw-mr-xBefore, yAfter-yBefore, RectFill, NewPDFRectOpt().X(xBefore).Y(yBefore).LineWidth(0.25).FillColor(128, 128, 0).Alpha(0.1, BlendNormal))
		b.Line(xBefore, yBefore, pw-mr, yAfter, NewPDFLineOpt().LineWidth(0.25).DrawColor(128, 128, 0))
	}
}

func (b *WPDFBuilder) Build() ([]byte, error) {
	buf := new(bytes.Buffer)
	err := b.b.Output(buf)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (b *WPDFBuilder) SetX(x float64) {
	b.b.SetX(x)
}

func (b *WPDFBuilder) IncX(dx float64) {
	b.b.SetX(b.b.GetX() + dx)
}

func (b *WPDFBuilder) GetX() float64 {
	return b.b.GetX()
}

func (b *WPDFBuilder) SetY(y float64) {
	b.b.SetY(y)
}

func (b *WPDFBuilder) GetY() float64 {
	return b.b.GetY()
}

func (b *WPDFBuilder) SetXY(x float64, y float64) {
	b.b.SetXY(x, y)
}

func (b *WPDFBuilder) GetXY() (x float64, y float64) {
	return b.b.GetXY()
}

func (b *WPDFBuilder) GetMargins() (left, top, right, bottom float64) {
	return b.b.GetMargins()
}

func (b *WPDFBuilder) GetMarginLeft() float64 {
	v, _, _, _ := b.b.GetMargins()
	return v
}

func (b *WPDFBuilder) GetMarginTop() float64 {
	_, v, _, _ := b.b.GetMargins()
	return v
}

func (b *WPDFBuilder) GetMarginRight() float64 {
	_, _, v, _ := b.b.GetMargins()
	return v
}

func (b *WPDFBuilder) GetMarginBottom() float64 {
	_, _, _, v := b.b.GetMargins()
	return v
}

func (b *WPDFBuilder) GetPageSize() (width, height float64) {
	return b.b.GetPageSize()
}

func (b *WPDFBuilder) GetPageWidth() float64 {
	v, _ := b.b.GetPageSize()
	return v
}

func (b *WPDFBuilder) GetPageHeight() float64 {
	_, v := b.b.GetPageSize()
	return v
}

func (b *WPDFBuilder) GetWorkAreaWidth() float64 {
	return b.GetPageWidth() - b.GetMarginLeft() - b.GetMarginRight()
}

func (b *WPDFBuilder) SetAutoPageBreak(auto bool, margin float64) {
	b.b.SetAutoPageBreak(auto, margin)
}

func (b *WPDFBuilder) SetFooterFunc(fnc func()) {
	b.b.SetFooterFunc(fnc)
}

func (b *WPDFBuilder) PageNo() int {
	return b.b.PageNo()
}

func (b *WPDFBuilder) Bookmark(txtStr string, level int, y float64) {
	b.b.Bookmark(b.tr(txtStr), level, y)
}

func (b *WPDFBuilder) GetStringWidth(str string, opts ...PDFCellOpt) float64 {

	var fontNameOverride *PDFFontFamily
	var fontStyleOverride *PDFFontStyle
	var fontSizeOverride *float64

	for _, opt := range opts {
		fontNameOverride = langext.CoalesceOpt(opt.fontNameOverride, fontNameOverride)
		fontStyleOverride = langext.CoalesceOpt(opt.fontStyleOverride, fontStyleOverride)
		fontSizeOverride = langext.CoalesceOpt(opt.fontSizeOverride, fontSizeOverride)
	}

	if fontNameOverride != nil || fontStyleOverride != nil || fontSizeOverride != nil {
		oldFontName := b.fontName
		oldFontStyle := b.fontStyle
		oldFontSize := b.fontSize
		newFontName := langext.Coalesce(fontNameOverride, oldFontName)
		newFontStyle := langext.Coalesce(fontStyleOverride, oldFontStyle)
		newFontSize := langext.Coalesce(fontSizeOverride, oldFontSize)
		b.SetFont(newFontName, newFontStyle, newFontSize)
		defer func() { b.SetFont(oldFontName, oldFontStyle, oldFontSize) }()
	}

	return b.b.GetStringWidth(str)
}

func (b *WPDFBuilder) Debug(v bool) {
	b.debug = v
}