// Package main implements a Markdown-to-PDF converter that produces documents
// compliant with IHK Chemnitz project documentation guidelines (Verordnung 2020)
// and DIN 5008 formatting rules.
package main

import (
	"log"
	"strconv"
	"strings"

	"github.com/go-pdf/fpdf"
)

// IHK Chemnitz / DIN 5008 format constants.
// Source: "Hinweise zur Erarbeitung der Dokumentation über die Projektarbeit",
// IHK Chemnitz, Verordnung 2020.
const (
	// Page margins in mm
	dinMarginLeft   = 30.0 // 3.0 cm  left margin
	dinMarginRight  = 40.0 // 4.0 cm  right margin — Korrekturrand (examiner correction space)
	dinMarginTop    = 20.0 // 2.0 cm  top margin
	dinMarginBottom = 25.0 // 2.5 cm  bottom margin

	// Font sizes in pt — Arial/Helvetica, black
	dinFontBody    = 12.0 // body text
	dinFontHeading = 14.0 // headings, bold
	dinFontCaption = 10.0 // captions and footnotes

	// Line heights in mm — 1½ line spacing: pt × 1.5 × (25.4 / 72)
	dinLineHtBody    = 6.35 // 12 pt × 1.5 = 18 pt = 6.35 mm
	dinLineHtHeading = 7.41 // 14 pt × 1.5 = 21 pt = 7.41 mm
	dinLineHtCaption = 5.29 // 10 pt × 1.5 = 15 pt = 5.29 mm

	// Vertical spacing in mm
	// IHK: two blank lines before a heading that is not at the top of the page;
	// one blank line after every heading.
	dinSpaceBeforeHeading  = 2 * dinLineHtBody // ≈ 12.7 mm
	dinSpaceAfterHeading   = dinLineHtBody     // ≈ 6.35 mm
	dinSpaceAfterParagraph = 4.0               // between body paragraphs

	// List items use tighter line spacing than body text (1.2× instead of 1.5×).
	// This keeps lists compact while remaining legible at 12 pt.
	dinLineHtList     = 5.0 // 12 pt × 1.2 ≈ 5.08 mm, rounded to 5.0
	dinSpaceAfterList = 3.0 // gap inserted after the outermost list exits
)

// AppendixKind distinguishes between image and table annexes.
type AppendixKind int

const (
	AppendixKindImage AppendixKind = iota
	AppendixKindTable
)

// Appendix holds the content for one annex entry.
type Appendix struct {
	Kind      AppendixKind
	Landscape bool // true → render on a landscape A4 page (15 mm symmetric margins)
	Title     string
	Path      string     // image path (Kind == AppendixKindImage)
	TableData [][]string // table rows (Kind == AppendixKindTable)
}

// IHKRenderer is the central PDF generator for IHK Chemnitz project documentation.
// All DIN 5008 formatting rules are applied through its methods.
//
// Usage: create with NewIHKRenderer, call Render* methods in document order,
// then call Save. The two-pass rendering in main.go fills the TOC correctly.
type IHKRenderer struct {
	pdf         *fpdf.Fpdf
	config      Config
	numType     NumberingType
	tocItems    []TOCItem
	tableItems  []TableItem
	figureItems []FigureItem
	sources     []string
	appendices  []Appendix
	// pageOffset is the PDF page number of the last Roman-numbered page.
	// Main body page 1 is rendered as PDF page (pageOffset + 1).
	pageOffset  int
	tableCount  int
	figureCount int
	// tr translates UTF-8 strings to the Latin-1 encoding used by fpdf.
	tr func(string) string
}

// NewIHKRenderer constructs a renderer pre-configured with DIN 5008 margins
// and an auto-footer that renders the correct page number style per section.
func NewIHKRenderer(config Config) *IHKRenderer {
	pdf := fpdf.New("P", "mm", "A4", "")
	pdf.SetMargins(dinMarginLeft, dinMarginTop, dinMarginRight)
	pdf.SetAutoPageBreak(true, dinMarginBottom)

	r := &IHKRenderer{
		pdf:         pdf,
		config:      config,
		numType:     NumNone,
		tocItems:    make([]TOCItem, 0),
		tableItems:  make([]TableItem, 0),
		figureItems: make([]FigureItem, 0),
		sources:     make([]string, 0),
		appendices:  make([]Appendix, 0),
		tr:          pdf.UnicodeTranslatorFromDescriptor(""),
	}

	// IHK: page number centered at the bottom of every numbered page.
	pdf.SetFooterFunc(func() {
		if r.numType == NumNone {
			return
		}
		pdf.SetY(-15)
		pdf.SetFont("Helvetica", "", dinFontCaption)
		var pageStr string
		switch r.numType {
		case NumRoman:
			pageStr = toRoman(pdf.PageNo())
		case NumArabic:
			dp := pdf.PageNo() - r.pageOffset
			if dp <= 0 {
				return
			}
			pageStr = strconv.Itoa(dp)
		}
		pdf.CellFormat(0, 10, pageStr, "", 0, "C", false, 0, "")
	})

	return r
}

// StartFrontMatter activates Roman-numeral page numbering.
// Must be called before adding the first front-matter page (i.e., before RenderTOC).
// The title page (page 1) is unnumbered; the TOC appears as page II.
func (r *IHKRenderer) StartFrontMatter() {
	r.numType = NumRoman
}

// StartMainBody switches to Arabic page numbering and captures the current PDF
// page as the offset so that the first main-body page displays as "1".
func (r *IHKRenderer) StartMainBody() {
	r.numType = NumArabic
	r.pageOffset = r.pdf.PageNo()
}

// AddSource registers a bibliography entry. Whitespace is trimmed;
// duplicate entries are silently ignored.
func (r *IHKRenderer) AddSource(source string) {
	source = strings.TrimSpace(source)
	if source == "" {
		return
	}
	for _, s := range r.sources {
		if s == source {
			return
		}
	}
	r.sources = append(r.sources, source)
}

// AddAppendix registers an image annex in "Title | /path/to/image" format.
func (r *IHKRenderer) AddAppendix(titlePath string) {
	parts := strings.SplitN(titlePath, "|", 2)
	if len(parts) < 2 {
		log.Printf("warning: @Anhang directive missing '|' separator: %q", titlePath)
		return
	}
	r.appendices = append(r.appendices, Appendix{
		Kind:  AppendixKindImage,
		Title: strings.TrimSpace(parts[0]),
		Path:  strings.TrimSpace(parts[1]),
	})
}

// AddTableAppendix registers a table annex entry.
func (r *IHKRenderer) AddTableAppendix(title string, data [][]string) {
	if len(data) == 0 {
		log.Printf("warning: @TabelleAnhang %q has no table data — skipped", title)
		return
	}
	r.appendices = append(r.appendices, Appendix{
		Kind:      AppendixKindTable,
		Title:     title,
		TableData: data,
	})
}

// AddLandscapeAppendix registers an image annex in "Title | /path/to/image" format
// that will be rendered on a landscape A4 page with 15 mm symmetric margins.
func (r *IHKRenderer) AddLandscapeAppendix(titlePath string) {
	parts := strings.SplitN(titlePath, "|", 2)
	if len(parts) < 2 {
		log.Printf("warning: @AnhangBildQuer directive missing '|' separator: %q", titlePath)
		return
	}
	r.appendices = append(r.appendices, Appendix{
		Kind:      AppendixKindImage,
		Landscape: true,
		Title:     strings.TrimSpace(parts[0]),
		Path:      strings.TrimSpace(parts[1]),
	})
}

// AddTableAppendixLandscape registers a table annex entry rendered on a landscape page.
func (r *IHKRenderer) AddTableAppendixLandscape(title string, data [][]string) {
	if len(data) == 0 {
		log.Printf("warning: @TabelleAnhangQuer %q has no table data — skipped", title)
		return
	}
	r.appendices = append(r.appendices, Appendix{
		Kind:      AppendixKindTable,
		Landscape: true,
		Title:     title,
		TableData: data,
	})
}

// Save writes the completed PDF to the given file path.
func (r *IHKRenderer) Save(filename string) error {
	return r.pdf.OutputFileAndClose(filename)
}

// usableWidth returns the printable line width on the current page in mm.
func (r *IHKRenderer) usableWidth() float64 {
	w, _ := r.pdf.GetPageSize()
	lm, _, rm, _ := r.pdf.GetMargins()
	return w - lm - rm
}

// isAtPageTop returns true when the Y cursor sits at or within 1 mm of the
// top margin, meaning no body content has been placed on this page yet.
func (r *IHKRenderer) isAtPageTop() bool {
	_, tm, _, _ := r.pdf.GetMargins()
	return r.pdf.GetY() <= tm+1.0
}
