package document_test

import (
	"bufio"
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"testing"

	"go.devnw.com/ooxml/document"
)

func TestFootnotesLoad(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	saveTestData(t, doc)
}

func TestHasFootnotes(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	if !doc.HasFootnotes() {
		t.Error("document should contain footnotes")
	}
	doc = document.New()
	if doc.HasFootnotes() {
		t.Error("document should not contain footnotes")
	}
}

func TestFootnotesListFootnotes(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	if len(doc.Footnotes()) == 0 {
		t.Error("document should contain footnotes")
	}
	if len(doc.Footnotes()) <= 2 {
		t.Error("document should contain footnotes array that includes 2 added by the system")
	}
	saveTestData(t, doc)
}

func TestFootnotesPullByID(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	fn := doc.Footnote(2)
	if fn.X().IdAttr != 2 {
		t.Error("test did not retrieve the proper footnote")
	}
	saveTestData(t, doc)
}

func TestFootnotesPullParagraphs(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	fn := doc.Footnote(2)
	if len(fn.Paragraphs()) == 0 {
		t.Error("test did not retrieve the paragraphs properly")
	}
	saveTestData(t, doc)
}

func TestFootnotesLinkedToRuns(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	var linkedRuns int = 0
	for _, p := range doc.Paragraphs() {
		for _, r := range p.Runs() {
			if ok, _ := r.IsFootnote(); ok {
				linkedRuns++
			}
		}
	}
	if linkedRuns == 0 {
		t.Error("document not properly linked to footnotes")
	}
	saveTestData(t, doc)
}

func TestFootnotesLinkedToRunsProperly(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	linkedFns := make([]document.Footnote, 0)
	for _, p := range doc.Paragraphs() {
		for _, r := range p.Runs() {
			if ok, fnID := r.IsFootnote(); ok {
				linkedFns = append(linkedFns, doc.Footnote(fnID))
			}
		}
	}
	if len(linkedFns) == 0 {
		t.Error("document not properly linked to footnotes")
	}
	saveTestData(t, doc)
}

func TestFootnotesHaveText(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	fn := doc.Footnote(2)
	var buff bytes.Buffer
	for _, p1 := range fn.Paragraphs() {
		for _, r2 := range p1.Runs() {
			buff.WriteString(r2.Text())
		}
	}
	if buff.String() == "" {
		t.Error("content not accessible from footnote")
	}
	saveTestData(t, doc)
}

func TestFootnotesClearContent(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	fn := doc.Footnote(2)
	var buff bytes.Buffer
	for _, p1 := range fn.Paragraphs() {
		for _, r2 := range p1.Runs() {
			r2.Clear()
			buff.WriteString(r2.Text())
		}
	}
	output := buff.String()
	if output != "" {
		t.Errorf("content not cleared from footnote: %s", output)
	}
	saveTestData(t, doc)
}

func TestFootnotesSimplifyContent(t *testing.T) {
	newTxt := "This will be shortened."
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}

	fn := doc.Footnote(2)
	for i, p := range fn.Paragraphs() {
		if i > 0 {
			fn.RemoveParagraph(p)
		}
		for j, r := range p.Runs() {
			if j > 0 {
				p.RemoveRun(r)
			}
			r.Clear()
			r.AddText(newTxt)
		}
	}

	fn2 := doc.Footnote(2)
	if len(fn2.Paragraphs()) != 1 {
		t.Error("wrong number of paragraphs")
	}
	if len(fn2.Paragraphs()[0].Runs()) != 1 {
		t.Errorf("wrong number of runs: expect %d, got %d", 1, len(fn2.Paragraphs()[0].Runs()))
	}
	if fn2.Paragraphs()[0].Runs()[0].Text() != newTxt {
		t.Error("incorrect modification to footnote text")
	}
	saveTestData(t, doc)
}

func TestFootnotesExpandContent(t *testing.T) {
	newTxt := "This will be expanded."
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	fn := doc.Footnote(3)

	para := fn.AddParagraph()
	run := para.AddRun()
	run.AddText(newTxt)

	fn2 := doc.Footnote(3)
	if len(fn2.Paragraphs()) != 2 {
		t.Error("wrong number of paragraphs")
	}
	if len(fn2.Paragraphs()[1].Runs()) != 1 {
		t.Errorf("wrong number of runs: expect %d, got %d", 1, len(fn2.Paragraphs()[0].Runs()))
	}
	if fn2.Paragraphs()[1].Runs()[0].Text() != newTxt {
		t.Error("incorrect modification to footnote text")
	}
	saveTestData(t, doc)
}

func TestFootnotesAddFootnote(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	fnB4 := len(doc.Footnotes())
	var fnB4InP int
	var p document.Paragraph
	for _, p1 := range doc.Paragraphs() {
		for _, r := range p1.Runs() {
			if ok, fnID := r.IsFootnote(); ok {
				if fnID == 2 {
					p = p1
					fnB4InP++
				}
			}
		}
	}
	fn := p.AddFootnote("testing")

	if len(doc.Footnotes()) == fnB4 {
		t.Fatal("did not properly add footnote")
	}

	var fnInP int
	for _, r := range p.Runs() {
		if ok, _ := r.IsFootnote(); ok {
			fnInP++
		}
	}

	if fnInP <= fnB4InP {
		t.Error("footnotes not properly added to run")
	}

	pinner := fn.AddParagraph()
	rinner := pinner.AddRun()
	rinner.AddText("more test goes in this paragraph")

	if len(fn.Paragraphs()) != 2 {
		t.Error("wrong number of paragraphs in footnote")
	}
	for _, p := range fn.Paragraphs() {
		if p.Properties().Style() != "Footnote" {
			t.Error("wrong style set for the paragraph")
		}
	}

	saveTestData(t, doc)
}

func TestFootnotesRemoveFootnote(t *testing.T) {
	doc := loadTestData(t)
	if doc == nil {
		t.Fatal("could not read test document")
	}
	fnB4 := len(doc.Footnotes())
	var runsInP int
	var p document.Paragraph
	for _, p1 := range doc.Paragraphs() {
		for _, r := range p1.Runs() {
			if ok, fnID := r.IsFootnote(); ok {
				if fnID == 2 {
					p = p1
					runsInP = len(p.Runs())
				}
			}
		}
	}
	p.RemoveFootnote(2)

	if len(doc.Footnotes()) == fnB4 {
		fmt.Println(doc.Footnotes())
		t.Fatal("did not properly remove footnote")
	}

	if len(p.Runs()) >= runsInP {
		t.Fatal("did not properly remove footnote")
	}
}

func loadTestData(t *testing.T) *document.Document {
	docBytes, err := ioutil.ReadFile(filepath.Join("testdata", "footnotes_endnotes.docx"))
	if err != nil {
		t.Error("cannot read text fixture from disk")
		return nil
	}
	doc, err := document.Read(bytes.NewReader(docBytes), int64(len(docBytes)))
	if err != nil {
		t.Error("cannot unmarshal test fixture into document struct")
		return nil
	}
	return doc
}

func saveTestData(t *testing.T, doc *document.Document) {
	// Ensure the document can render
	var b bytes.Buffer
	buff := bufio.NewWriter(&b)
	err := doc.Save(buff)
	if err != nil {
		t.Error("failure rendering document to docx:", err)
	}
}
