package basic

import (
	"bytes"
	"fmt"
	"io"

	"asciigoat.org/ini/parser"
)

// WriteNewLine is the new line representation used by [doc.WriteTo]
const WriteNewLine = "\n"

// AsBuffer returns a INI representation of the document on
// a memory buffer
func (doc *Document) AsBuffer(nl string) *bytes.Buffer {
	var buf bytes.Buffer

	if len(doc.Global) > 0 {
		_, _ = writeFieldsTo(&buf, doc.Global, nl)
	}

	for _, sec := range doc.Sections {
		if buf.Len() > 0 {
			_, _ = buf.WriteString(nl)
		}

		_ = writeSectionToBuffer(&buf, &sec, nl)
	}

	return &buf
}

func writeFieldsTo(w io.Writer, fields []Field, nl string) (int64, error) {
	var written int
	for _, field := range fields {
		n, err := fmt.Fprintf(w, "%s = %q%s", field.Key, field.Value, nl)
		switch {
		case err != nil:
			return int64(written), err
		case n > 0:
			written += n
		}
	}
	return int64(written), nil
}

// String generates a string output for "%s"
func (field Field) String() string {
	var buf bytes.Buffer

	_, _ = writeFieldsTo(&buf, []Field{field}, WriteNewLine)
	return buf.String()
}

func writeSectionToBuffer(w *bytes.Buffer, sec *Section, nl string) int {
	var written, n int

	_, _ = w.WriteRune(parser.RuneSectionStart)
	written++

	n, _ = w.WriteString(sec.Key)
	written += n

	switch {
	case sec.EmptyID:
		n, _ = w.WriteString(" \"\"")
		written += n
	case sec.ID != "":
		_, _ = w.WriteRune(' ')
		n, _ = fmt.Fprintf(w, "%q", sec.ID)
		written += n + 1
	}

	_, _ = w.WriteRune(parser.RuneSectionEnd)
	written++

	n, _ = w.WriteString(nl)
	written += n

	n64, _ := writeFieldsTo(w, sec.Fields, nl)
	return written + int(n64)
}

// String generates a string output for "%s"
func (sec *Section) String() string {
	var buf bytes.Buffer

	_ = writeSectionToBuffer(&buf, sec, WriteNewLine)
	return buf.String()
}

// WriteTo writes a INI representation of the document
// onto the provided writer.
func (doc *Document) WriteTo(w io.Writer) (int64, error) {
	buf := doc.AsBuffer(WriteNewLine)
	return buf.WriteTo(w)
}

// String generates a string output for "%s"
func (doc *Document) String() string {
	buf := doc.AsBuffer(WriteNewLine)
	return buf.String()
}