package toml

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"strconv"
	"strings"
	"testing"
	"time"
)

type basicMarshalTestStruct struct {
	String     string   `toml:"Zstring"`
	StringList []string `toml:"Ystrlist"`
	BasicMarshalTestSubAnonymousStruct
	Sub     basicMarshalTestSubStruct   `toml:"Xsubdoc"`
	SubList []basicMarshalTestSubStruct `toml:"Wsublist"`
}

type basicMarshalTestSubStruct struct {
	String2 string
}

type BasicMarshalTestSubAnonymousStruct struct {
	String3 string
}

var basicTestData = basicMarshalTestStruct{
	String:                             "Hello",
	StringList:                         []string{"Howdy", "Hey There"},
	BasicMarshalTestSubAnonymousStruct: BasicMarshalTestSubAnonymousStruct{"One"},
	Sub:                                basicMarshalTestSubStruct{"Two"},
	SubList:                            []basicMarshalTestSubStruct{{"Three"}, {"Four"}},
}

var basicTestToml = []byte(`String3 = "One"
Ystrlist = ["Howdy", "Hey There"]
Zstring = "Hello"

[[Wsublist]]
  String2 = "Three"

[[Wsublist]]
  String2 = "Four"

[Xsubdoc]
  String2 = "Two"
`)

var basicTestTomlCustomIndentation = []byte(`String3 = "One"
Ystrlist = ["Howdy", "Hey There"]
Zstring = "Hello"

[[Wsublist]]
	String2 = "Three"

[[Wsublist]]
	String2 = "Four"

[Xsubdoc]
	String2 = "Two"
`)

var basicTestTomlOrdered = []byte(`Zstring = "Hello"
Ystrlist = ["Howdy", "Hey There"]
String3 = "One"

[Xsubdoc]
  String2 = "Two"

[[Wsublist]]
  String2 = "Three"

[[Wsublist]]
  String2 = "Four"
`)

var marshalTestToml = []byte(`title = "TOML Marshal Testing"

[basic]
  bool = true
  date = 1979-05-27T07:32:00Z
  float = 123.4
  float64 = 123.456782132399
  int = 5000
  string = "Bite me"
  uint = 5001

[basic_lists]
  bools = [true, false, true]
  dates = [1979-05-27T07:32:00Z, 1980-05-27T07:32:00Z]
  floats = [12.3, 45.6, 78.9]
  ints = [8001, 8001, 8002]
  strings = ["One", "Two", "Three"]
  uints = [5002, 5003]

[basic_map]
  one = "one"
  two = "two"

[subdoc]

  [subdoc.first]
    name = "First"

  [subdoc.second]
    name = "Second"

[[subdoclist]]
  name = "List.First"

[[subdoclist]]
  name = "List.Second"

[[subdocptrs]]
  name = "Second"
`)

var marshalOrderPreserveToml = []byte(`title = "TOML Marshal Testing"

[basic_lists]
  floats = [12.3, 45.6, 78.9]
  bools = [true, false, true]
  dates = [1979-05-27T07:32:00Z, 1980-05-27T07:32:00Z]
  ints = [8001, 8001, 8002]
  uints = [5002, 5003]
  strings = ["One", "Two", "Three"]

[[subdocptrs]]
  name = "Second"

[basic_map]
  one = "one"
  two = "two"

[subdoc]

  [subdoc.second]
    name = "Second"

  [subdoc.first]
    name = "First"

[basic]
  uint = 5001
  bool = true
  float = 123.4
  float64 = 123.456782132399
  int = 5000
  string = "Bite me"
  date = 1979-05-27T07:32:00Z

[[subdoclist]]
  name = "List.First"

[[subdoclist]]
  name = "List.Second"
`)

var mashalOrderPreserveMapToml = []byte(`title = "TOML Marshal Testing"

[basic_map]
  one = "one"
  two = "two"

[long_map]
  a7 = "1"
  b3 = "2"
  c8 = "3"
  d4 = "4"
  e6 = "5"
  f5 = "6"
  g10 = "7"
  h1 = "8"
  i2 = "9"
  j9 = "10"
`)

type Conf struct {
	Name  string
	Age   int
	Inter interface{}
}

type NestedStruct struct {
	FirstName string
	LastName  string
	Age       int
}

var doc = []byte(`Name = "rui"
Age = 18

[Inter]
  FirstName = "wang"
  LastName = "jl"
  Age = 100`)

func TestInterface(t *testing.T) {
	var config Conf
	config.Inter = &NestedStruct{}
	err := Unmarshal(doc, &config)
	expected := Conf{
		Name: "rui",
		Age:  18,
		Inter: &NestedStruct{
			FirstName: "wang",
			LastName:  "jl",
			Age:       100,
		},
	}
	if err != nil || !reflect.DeepEqual(config, expected) {
		t.Errorf("Bad unmarshal: expected %v, got %v", expected, config)
	}
}

func TestBasicMarshal(t *testing.T) {
	result, err := Marshal(basicTestData)
	if err != nil {
		t.Fatal(err)
	}
	expected := basicTestToml
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestBasicMarshalCustomIndentation(t *testing.T) {
	var result bytes.Buffer
	err := NewEncoder(&result).Indentation("\t").Encode(basicTestData)
	if err != nil {
		t.Fatal(err)
	}
	expected := basicTestTomlCustomIndentation
	if !bytes.Equal(result.Bytes(), expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
	}
}

func TestBasicMarshalWrongIndentation(t *testing.T) {
	var result bytes.Buffer
	err := NewEncoder(&result).Indentation("  \n").Encode(basicTestData)
	if err.Error() != "invalid indentation: must only contains space or tab characters" {
		t.Error("expect err:invalid indentation: must only contains space or tab characters but got:", err)
	}
}

func TestBasicMarshalOrdered(t *testing.T) {
	var result bytes.Buffer
	err := NewEncoder(&result).Order(OrderPreserve).Encode(basicTestData)
	if err != nil {
		t.Fatal(err)
	}
	expected := basicTestTomlOrdered
	if !bytes.Equal(result.Bytes(), expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
	}
}

func TestBasicMarshalWithPointer(t *testing.T) {
	result, err := Marshal(&basicTestData)
	if err != nil {
		t.Fatal(err)
	}
	expected := basicTestToml
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestBasicMarshalOrderedWithPointer(t *testing.T) {
	var result bytes.Buffer
	err := NewEncoder(&result).Order(OrderPreserve).Encode(&basicTestData)
	if err != nil {
		t.Fatal(err)
	}
	expected := basicTestTomlOrdered
	if !bytes.Equal(result.Bytes(), expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result.Bytes())
	}
}

func TestBasicUnmarshal(t *testing.T) {
	result := basicMarshalTestStruct{}
	err := Unmarshal(basicTestToml, &result)
	expected := basicTestData
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
	}
}

type quotedKeyMarshalTestStruct struct {
	String  string                      `toml:"Z.string-àéù"`
	Float   float64                     `toml:"Yfloat-𝟘"`
	Sub     basicMarshalTestSubStruct   `toml:"Xsubdoc-àéù"`
	SubList []basicMarshalTestSubStruct `toml:"W.sublist-𝟘"`
}

var quotedKeyMarshalTestData = quotedKeyMarshalTestStruct{
	String:  "Hello",
	Float:   3.5,
	Sub:     basicMarshalTestSubStruct{"One"},
	SubList: []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
}

var quotedKeyMarshalTestToml = []byte(`"Yfloat-𝟘" = 3.5
"Z.string-àéù" = "Hello"

[["W.sublist-𝟘"]]
  String2 = "Two"

[["W.sublist-𝟘"]]
  String2 = "Three"

["Xsubdoc-àéù"]
  String2 = "One"
`)

func TestBasicMarshalQuotedKey(t *testing.T) {
	result, err := Marshal(quotedKeyMarshalTestData)
	if err != nil {
		t.Fatal(err)
	}
	expected := quotedKeyMarshalTestToml
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestBasicUnmarshalQuotedKey(t *testing.T) {
	tree, err := LoadBytes(quotedKeyMarshalTestToml)
	if err != nil {
		t.Fatal(err)
	}

	var q quotedKeyMarshalTestStruct
	tree.Unmarshal(&q)
	fmt.Println(q)

	if !reflect.DeepEqual(quotedKeyMarshalTestData, q) {
		t.Errorf("Bad unmarshal: expected\n-----\n%v\n-----\ngot\n-----\n%v\n-----\n", quotedKeyMarshalTestData, q)
	}
}

type testDoc struct {
	Title       string            `toml:"title"`
	BasicLists  testDocBasicLists `toml:"basic_lists"`
	SubDocPtrs  []*testSubDoc     `toml:"subdocptrs"`
	BasicMap    map[string]string `toml:"basic_map"`
	Subdocs     testDocSubs       `toml:"subdoc"`
	Basics      testDocBasics     `toml:"basic"`
	SubDocList  []testSubDoc      `toml:"subdoclist"`
	err         int               `toml:"shouldntBeHere"`
	unexported  int               `toml:"shouldntBeHere"`
	Unexported2 int               `toml:"-"`
}

type testMapDoc struct {
	Title    string            `toml:"title"`
	BasicMap map[string]string `toml:"basic_map"`
	LongMap  map[string]string `toml:"long_map"`
}

type testDocBasics struct {
	Uint       uint      `toml:"uint"`
	Bool       bool      `toml:"bool"`
	Float32    float32   `toml:"float"`
	Float64    float64   `toml:"float64"`
	Int        int       `toml:"int"`
	String     *string   `toml:"string"`
	Date       time.Time `toml:"date"`
	unexported int       `toml:"shouldntBeHere"`
}

type testDocBasicLists struct {
	Floats  []*float32  `toml:"floats"`
	Bools   []bool      `toml:"bools"`
	Dates   []time.Time `toml:"dates"`
	Ints    []int       `toml:"ints"`
	UInts   []uint      `toml:"uints"`
	Strings []string    `toml:"strings"`
}

type testDocSubs struct {
	Second *testSubDoc `toml:"second"`
	First  testSubDoc  `toml:"first"`
}

type testSubDoc struct {
	Name       string `toml:"name"`
	unexported int    `toml:"shouldntBeHere"`
}

var biteMe = "Bite me"
var float1 float32 = 12.3
var float2 float32 = 45.6
var float3 float32 = 78.9
var subdoc = testSubDoc{"Second", 0}

var docData = testDoc{
	Title:       "TOML Marshal Testing",
	unexported:  0,
	Unexported2: 0,
	Basics: testDocBasics{
		Bool:       true,
		Date:       time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
		Float32:    123.4,
		Float64:    123.456782132399,
		Int:        5000,
		Uint:       5001,
		String:     &biteMe,
		unexported: 0,
	},
	BasicLists: testDocBasicLists{
		Bools: []bool{true, false, true},
		Dates: []time.Time{
			time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
			time.Date(1980, 5, 27, 7, 32, 0, 0, time.UTC),
		},
		Floats:  []*float32{&float1, &float2, &float3},
		Ints:    []int{8001, 8001, 8002},
		Strings: []string{"One", "Two", "Three"},
		UInts:   []uint{5002, 5003},
	},
	BasicMap: map[string]string{
		"one": "one",
		"two": "two",
	},
	Subdocs: testDocSubs{
		First:  testSubDoc{"First", 0},
		Second: &subdoc,
	},
	SubDocList: []testSubDoc{
		{"List.First", 0},
		{"List.Second", 0},
	},
	SubDocPtrs: []*testSubDoc{&subdoc},
}

var mapTestDoc = testMapDoc{
	Title: "TOML Marshal Testing",
	BasicMap: map[string]string{
		"one": "one",
		"two": "two",
	},
	LongMap: map[string]string{
		"h1":  "8",
		"i2":  "9",
		"b3":  "2",
		"d4":  "4",
		"f5":  "6",
		"e6":  "5",
		"a7":  "1",
		"c8":  "3",
		"j9":  "10",
		"g10": "7",
	},
}

func TestDocMarshal(t *testing.T) {
	result, err := Marshal(docData)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(result, marshalTestToml) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalTestToml, result)
	}
}

func TestDocMarshalOrdered(t *testing.T) {
	var result bytes.Buffer
	err := NewEncoder(&result).Order(OrderPreserve).Encode(docData)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(result.Bytes(), marshalOrderPreserveToml) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalOrderPreserveToml, result.Bytes())
	}
}

func TestDocMarshalMaps(t *testing.T) {
	result, err := Marshal(mapTestDoc)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(result, mashalOrderPreserveMapToml) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", mashalOrderPreserveMapToml, result)
	}
}

func TestDocMarshalOrderedMaps(t *testing.T) {
	var result bytes.Buffer
	err := NewEncoder(&result).Order(OrderPreserve).Encode(mapTestDoc)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(result.Bytes(), mashalOrderPreserveMapToml) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", mashalOrderPreserveMapToml, result.Bytes())
	}
}

func TestDocMarshalPointer(t *testing.T) {
	result, err := Marshal(&docData)
	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(result, marshalTestToml) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", marshalTestToml, result)
	}
}

func TestDocUnmarshal(t *testing.T) {
	result := testDoc{}
	err := Unmarshal(marshalTestToml, &result)
	expected := docData
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(result, expected) {
		resStr, _ := json.MarshalIndent(result, "", "  ")
		expStr, _ := json.MarshalIndent(expected, "", "  ")
		t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
	}
}

func TestDocPartialUnmarshal(t *testing.T) {
	file, err := ioutil.TempFile("", "test-*.toml")
	if err != nil {
		t.Fatal(err)
	}
	defer os.Remove(file.Name())

	err = ioutil.WriteFile(file.Name(), marshalTestToml, 0)
	if err != nil {
		t.Fatal(err)
	}

	tree, _ := LoadFile(file.Name())
	subTree := tree.Get("subdoc").(*Tree)

	result := testDocSubs{}
	err = subTree.Unmarshal(&result)
	expected := docData.Subdocs
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(result, expected) {
		resStr, _ := json.MarshalIndent(result, "", "  ")
		expStr, _ := json.MarshalIndent(expected, "", "  ")
		t.Errorf("Bad partial unmartial: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)
	}
}

type tomlTypeCheckTest struct {
	name string
	item interface{}
	typ  int //0=primitive, 1=otherslice, 2=treeslice, 3=tree
}

func TestTypeChecks(t *testing.T) {
	tests := []tomlTypeCheckTest{
		{"bool", true, 0},
		{"bool", false, 0},
		{"int", int(2), 0},
		{"int8", int8(2), 0},
		{"int16", int16(2), 0},
		{"int32", int32(2), 0},
		{"int64", int64(2), 0},
		{"uint", uint(2), 0},
		{"uint8", uint8(2), 0},
		{"uint16", uint16(2), 0},
		{"uint32", uint32(2), 0},
		{"uint64", uint64(2), 0},
		{"float32", float32(3.14), 0},
		{"float64", float64(3.14), 0},
		{"string", "lorem ipsum", 0},
		{"time", time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC), 0},
		{"stringlist", []string{"hello", "hi"}, 1},
		{"stringlistptr", &[]string{"hello", "hi"}, 1},
		{"stringarray", [2]string{"hello", "hi"}, 1},
		{"stringarrayptr", &[2]string{"hello", "hi"}, 1},
		{"timelist", []time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
		{"timelistptr", &[]time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
		{"timearray", [1]time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
		{"timearrayptr", &[1]time.Time{time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, 1},
		{"objectlist", []tomlTypeCheckTest{}, 2},
		{"objectlistptr", &[]tomlTypeCheckTest{}, 2},
		{"objectarray", [2]tomlTypeCheckTest{{}, {}}, 2},
		{"objectlistptr", &[2]tomlTypeCheckTest{{}, {}}, 2},
		{"object", tomlTypeCheckTest{}, 3},
		{"objectptr", &tomlTypeCheckTest{}, 3},
	}

	for _, test := range tests {
		expected := []bool{false, false, false, false}
		expected[test.typ] = true
		result := []bool{
			isPrimitive(reflect.TypeOf(test.item)),
			isOtherSequence(reflect.TypeOf(test.item)),
			isTreeSequence(reflect.TypeOf(test.item)),
			isTree(reflect.TypeOf(test.item)),
		}
		if !reflect.DeepEqual(expected, result) {
			t.Errorf("Bad type check on %q: expected %v, got %v", test.name, expected, result)
		}
	}
}

type unexportedMarshalTestStruct struct {
	String      string                      `toml:"string"`
	StringList  []string                    `toml:"strlist"`
	Sub         basicMarshalTestSubStruct   `toml:"subdoc"`
	SubList     []basicMarshalTestSubStruct `toml:"sublist"`
	unexported  int                         `toml:"shouldntBeHere"`
	Unexported2 int                         `toml:"-"`
}

var unexportedTestData = unexportedMarshalTestStruct{
	String:      "Hello",
	StringList:  []string{"Howdy", "Hey There"},
	Sub:         basicMarshalTestSubStruct{"One"},
	SubList:     []basicMarshalTestSubStruct{{"Two"}, {"Three"}},
	unexported:  0,
	Unexported2: 0,
}

var unexportedTestToml = []byte(`string = "Hello"
strlist = ["Howdy","Hey There"]
unexported = 1
shouldntBeHere = 2

[subdoc]
  String2 = "One"

[[sublist]]
  String2 = "Two"

[[sublist]]
  String2 = "Three"
`)

func TestUnexportedUnmarshal(t *testing.T) {
	result := unexportedMarshalTestStruct{}
	err := Unmarshal(unexportedTestToml, &result)
	expected := unexportedTestData
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("Bad unexported unmarshal: expected %v, got %v", expected, result)
	}
}

type errStruct struct {
	Bool   bool      `toml:"bool"`
	Date   time.Time `toml:"date"`
	Float  float64   `toml:"float"`
	Int    int16     `toml:"int"`
	String *string   `toml:"string"`
}

var errTomls = []string{
	"bool = truly\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
	"bool = true\ndate = 1979-05-27T07:3200Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123a4\nint = 5000\nstring = \"Bite me\"",
	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = j000\nstring = \"Bite me\"",
	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = Bite me",
	"bool = 1\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
	"bool = true\ndate = 1\nfloat = 123.4\nint = 5000\nstring = \"Bite me\"",
	"bool = true\ndate = 1979-05-27T07:32:00Z\n\"sorry\"\nint = 5000\nstring = \"Bite me\"",
	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = \"sorry\"\nstring = \"Bite me\"",
	"bool = true\ndate = 1979-05-27T07:32:00Z\nfloat = 123.4\nint = 5000\nstring = 1",
}

type mapErr struct {
	Vals map[string]float64
}

type intErr struct {
	Int1  int
	Int2  int8
	Int3  int16
	Int4  int32
	Int5  int64
	UInt1 uint
	UInt2 uint8
	UInt3 uint16
	UInt4 uint32
	UInt5 uint64
	Flt1  float32
	Flt2  float64
}

var intErrTomls = []string{
	"Int1 = []\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = []\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = []\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = []\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = []\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = []\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = []\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = []\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = []\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = []\nFlt1 = 1.0\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = []\nFlt2 = 2.0",
	"Int1 = 1\nInt2 = 2\nInt3 = 3\nInt4 = 4\nInt5 = 5\nUInt1 = 1\nUInt2 = 2\nUInt3 = 3\nUInt4 = 4\nUInt5 = 5\nFlt1 = 1.0\nFlt2 = []",
}

func TestErrUnmarshal(t *testing.T) {
	for ind, toml := range errTomls {
		result := errStruct{}
		err := Unmarshal([]byte(toml), &result)
		if err == nil {
			t.Errorf("Expected err from case %d\n", ind)
		}
	}
	result2 := mapErr{}
	err := Unmarshal([]byte("[Vals]\nfred=\"1.2\""), &result2)
	if err == nil {
		t.Errorf("Expected err from map")
	}
	for ind, toml := range intErrTomls {
		result3 := intErr{}
		err := Unmarshal([]byte(toml), &result3)
		if err == nil {
			t.Errorf("Expected int err from case %d\n", ind)
		}
	}
}

type emptyMarshalTestStruct struct {
	Title      string                  `toml:"title"`
	Bool       bool                    `toml:"bool"`
	Int        int                     `toml:"int"`
	String     string                  `toml:"string"`
	StringList []string                `toml:"stringlist"`
	Ptr        *basicMarshalTestStruct `toml:"ptr"`
	Map        map[string]string       `toml:"map"`
}

var emptyTestData = emptyMarshalTestStruct{
	Title:      "Placeholder",
	Bool:       false,
	Int:        0,
	String:     "",
	StringList: []string{},
	Ptr:        nil,
	Map:        map[string]string{},
}

var emptyTestToml = []byte(`bool = false
int = 0
string = ""
stringlist = []
title = "Placeholder"

[map]
`)

type emptyMarshalTestStruct2 struct {
	Title      string                  `toml:"title"`
	Bool       bool                    `toml:"bool,omitempty"`
	Int        int                     `toml:"int, omitempty"`
	String     string                  `toml:"string,omitempty "`
	StringList []string                `toml:"stringlist,omitempty"`
	Ptr        *basicMarshalTestStruct `toml:"ptr,omitempty"`
	Map        map[string]string       `toml:"map,omitempty"`
}

var emptyTestData2 = emptyMarshalTestStruct2{
	Title:      "Placeholder",
	Bool:       false,
	Int:        0,
	String:     "",
	StringList: []string{},
	Ptr:        nil,
	Map:        map[string]string{},
}

var emptyTestToml2 = []byte(`title = "Placeholder"
`)

func TestEmptyMarshal(t *testing.T) {
	result, err := Marshal(emptyTestData)
	if err != nil {
		t.Fatal(err)
	}
	expected := emptyTestToml
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad empty marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestEmptyMarshalOmit(t *testing.T) {
	result, err := Marshal(emptyTestData2)
	if err != nil {
		t.Fatal(err)
	}
	expected := emptyTestToml2
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad empty omit marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestEmptyUnmarshal(t *testing.T) {
	result := emptyMarshalTestStruct{}
	err := Unmarshal(emptyTestToml, &result)
	expected := emptyTestData
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("Bad empty unmarshal: expected %v, got %v", expected, result)
	}
}

func TestEmptyUnmarshalOmit(t *testing.T) {
	result := emptyMarshalTestStruct2{}
	err := Unmarshal(emptyTestToml, &result)
	expected := emptyTestData2
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("Bad empty omit unmarshal: expected %v, got %v", expected, result)
	}
}

type pointerMarshalTestStruct struct {
	Str       *string
	List      *[]string
	ListPtr   *[]*string
	Map       *map[string]string
	MapPtr    *map[string]*string
	EmptyStr  *string
	EmptyList *[]string
	EmptyMap  *map[string]string
	DblPtr    *[]*[]*string
}

var pointerStr = "Hello"
var pointerList = []string{"Hello back"}
var pointerListPtr = []*string{&pointerStr}
var pointerMap = map[string]string{"response": "Goodbye"}
var pointerMapPtr = map[string]*string{"alternate": &pointerStr}
var pointerTestData = pointerMarshalTestStruct{
	Str:       &pointerStr,
	List:      &pointerList,
	ListPtr:   &pointerListPtr,
	Map:       &pointerMap,
	MapPtr:    &pointerMapPtr,
	EmptyStr:  nil,
	EmptyList: nil,
	EmptyMap:  nil,
}

var pointerTestToml = []byte(`List = ["Hello back"]
ListPtr = ["Hello"]
Str = "Hello"

[Map]
  response = "Goodbye"

[MapPtr]
  alternate = "Hello"
`)

func TestPointerMarshal(t *testing.T) {
	result, err := Marshal(pointerTestData)
	if err != nil {
		t.Fatal(err)
	}
	expected := pointerTestToml
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad pointer marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestPointerUnmarshal(t *testing.T) {
	result := pointerMarshalTestStruct{}
	err := Unmarshal(pointerTestToml, &result)
	expected := pointerTestData
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("Bad pointer unmarshal: expected %v, got %v", expected, result)
	}
}

func TestUnmarshalTypeMismatch(t *testing.T) {
	result := pointerMarshalTestStruct{}
	err := Unmarshal([]byte("List = 123"), &result)
	if !strings.HasPrefix(err.Error(), "(1, 1): Can't convert 123(int64) to []string(slice)") {
		t.Errorf("Type mismatch must be reported: got %v", err.Error())
	}
}

type nestedMarshalTestStruct struct {
	String [][]string
	//Struct [][]basicMarshalTestSubStruct
	StringPtr *[]*[]*string
	// StructPtr *[]*[]*basicMarshalTestSubStruct
}

var str1 = "Three"
var str2 = "Four"
var strPtr = []*string{&str1, &str2}
var strPtr2 = []*[]*string{&strPtr}

var nestedTestData = nestedMarshalTestStruct{
	String:    [][]string{{"Five", "Six"}, {"One", "Two"}},
	StringPtr: &strPtr2,
}

var nestedTestToml = []byte(`String = [["Five", "Six"], ["One", "Two"]]
StringPtr = [["Three", "Four"]]
`)

func TestNestedMarshal(t *testing.T) {
	result, err := Marshal(nestedTestData)
	if err != nil {
		t.Fatal(err)
	}
	expected := nestedTestToml
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad nested marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestNestedUnmarshal(t *testing.T) {
	result := nestedMarshalTestStruct{}
	err := Unmarshal(nestedTestToml, &result)
	expected := nestedTestData
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("Bad nested unmarshal: expected %v, got %v", expected, result)
	}
}

type customMarshalerParent struct {
	Self    customMarshaler   `toml:"me"`
	Friends []customMarshaler `toml:"friends"`
}

type customMarshaler struct {
	FirstName string
	LastName  string
}

func (c customMarshaler) MarshalTOML() ([]byte, error) {
	fullName := fmt.Sprintf("%s %s", c.FirstName, c.LastName)
	return []byte(fullName), nil
}

var customMarshalerData = customMarshaler{FirstName: "Sally", LastName: "Fields"}
var customMarshalerToml = []byte(`Sally Fields`)
var nestedCustomMarshalerData = customMarshalerParent{
	Self:    customMarshaler{FirstName: "Maiku", LastName: "Suteda"},
	Friends: []customMarshaler{customMarshalerData},
}
var nestedCustomMarshalerToml = []byte(`friends = ["Sally Fields"]
me = "Maiku Suteda"
`)
var nestedCustomMarshalerTomlForUnmarshal = []byte(`[friends]
FirstName = "Sally"
LastName = "Fields"`)

func TestCustomMarshaler(t *testing.T) {
	result, err := Marshal(customMarshalerData)
	if err != nil {
		t.Fatal(err)
	}
	expected := customMarshalerToml
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad custom marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

type IntOrString string

func (x *IntOrString) MarshalTOML() ([]byte, error) {
	s := *(*string)(x)
	_, err := strconv.Atoi(s)
	if err != nil {
		return []byte(fmt.Sprintf(`"%s"`, s)), nil
	}
	return []byte(s), nil
}

func TestNestedCustomMarshaler(t *testing.T) {
	num := IntOrString("100")
	str := IntOrString("hello")
	var parent = struct {
		IntField    *IntOrString `toml:"int"`
		StringField *IntOrString `toml:"string"`
	}{
		&num,
		&str,
	}

	result, err := Marshal(parent)
	if err != nil {
		t.Fatal(err)
	}
	expected := `int = 100
string = "hello"
`
	if !bytes.Equal(result, []byte(expected)) {
		t.Errorf("Bad nested text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

type textMarshaler struct {
	FirstName string
	LastName  string
}

func (m textMarshaler) MarshalText() ([]byte, error) {
	fullName := fmt.Sprintf("%s %s", m.FirstName, m.LastName)
	return []byte(fullName), nil
}

func TestTextMarshaler(t *testing.T) {
	m := textMarshaler{FirstName: "Sally", LastName: "Fields"}

	result, err := Marshal(m)
	if err != nil {
		t.Fatal(err)
	}
	expected := `Sally Fields`
	if !bytes.Equal(result, []byte(expected)) {
		t.Errorf("Bad text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestUnmarshalTextMarshaler(t *testing.T) {
	var nested = struct {
		Friends textMarshaler `toml:"friends"`
	}{}

	var expected = struct {
		Friends textMarshaler `toml:"friends"`
	}{
		Friends: textMarshaler{FirstName: "Sally", LastName: "Fields"},
	}

	err := Unmarshal(nestedCustomMarshalerTomlForUnmarshal, &nested)
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(nested, expected) {
		t.Errorf("Bad unmarshal: expected %v, got %v", expected, nested)
	}
}

func TestNestedTextMarshaler(t *testing.T) {
	var parent = struct {
		Self     textMarshaler   `toml:"me"`
		Friends  []textMarshaler `toml:"friends"`
		Stranger *textMarshaler  `toml:"stranger"`
	}{
		Self:     textMarshaler{FirstName: "Maiku", LastName: "Suteda"},
		Friends:  []textMarshaler{textMarshaler{FirstName: "Sally", LastName: "Fields"}},
		Stranger: &textMarshaler{FirstName: "Earl", LastName: "Henson"},
	}

	result, err := Marshal(parent)
	if err != nil {
		t.Fatal(err)
	}
	expected := `friends = ["Sally Fields"]
me = "Maiku Suteda"
stranger = "Earl Henson"
`
	if !bytes.Equal(result, []byte(expected)) {
		t.Errorf("Bad nested text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

type precedentMarshaler struct {
	FirstName string
	LastName  string
}

func (m precedentMarshaler) MarshalText() ([]byte, error) {
	return []byte("shadowed"), nil
}

func (m precedentMarshaler) MarshalTOML() ([]byte, error) {
	fullName := fmt.Sprintf("%s %s", m.FirstName, m.LastName)
	return []byte(fullName), nil
}

func TestPrecedentMarshaler(t *testing.T) {
	m := textMarshaler{FirstName: "Sally", LastName: "Fields"}

	result, err := Marshal(m)
	if err != nil {
		t.Fatal(err)
	}
	expected := `Sally Fields`
	if !bytes.Equal(result, []byte(expected)) {
		t.Errorf("Bad text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

type customPointerMarshaler struct {
	FirstName string
	LastName  string
}

func (m *customPointerMarshaler) MarshalTOML() ([]byte, error) {
	return []byte(`"hidden"`), nil
}

type textPointerMarshaler struct {
	FirstName string
	LastName  string
}

func (m *textPointerMarshaler) MarshalText() ([]byte, error) {
	return []byte("hidden"), nil
}

func TestPointerMarshaler(t *testing.T) {
	var parent = struct {
		Self     customPointerMarshaler  `toml:"me"`
		Stranger *customPointerMarshaler `toml:"stranger"`
		Friend   textPointerMarshaler    `toml:"friend"`
		Fiend    *textPointerMarshaler   `toml:"fiend"`
	}{
		Self:     customPointerMarshaler{FirstName: "Maiku", LastName: "Suteda"},
		Stranger: &customPointerMarshaler{FirstName: "Earl", LastName: "Henson"},
		Friend:   textPointerMarshaler{FirstName: "Sally", LastName: "Fields"},
		Fiend:    &textPointerMarshaler{FirstName: "Casper", LastName: "Snider"},
	}

	result, err := Marshal(parent)
	if err != nil {
		t.Fatal(err)
	}
	expected := `fiend = "hidden"
stranger = "hidden"

[friend]
  FirstName = "Sally"
  LastName = "Fields"

[me]
  FirstName = "Maiku"
  LastName = "Suteda"
`
	if !bytes.Equal(result, []byte(expected)) {
		t.Errorf("Bad nested text marshaler: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestPointerCustomMarshalerSequence(t *testing.T) {
	var customPointerMarshalerSlice *[]*customPointerMarshaler
	var customPointerMarshalerArray *[2]*customPointerMarshaler

	if !isCustomMarshalerSequence(reflect.TypeOf(customPointerMarshalerSlice)) {
		t.Errorf("error: should be a sequence of custom marshaler interfaces")
	}
	if !isCustomMarshalerSequence(reflect.TypeOf(customPointerMarshalerArray)) {
		t.Errorf("error: should be a sequence of custom marshaler interfaces")
	}
}

func TestPointerTextMarshalerSequence(t *testing.T) {
	var textPointerMarshalerSlice *[]*textPointerMarshaler
	var textPointerMarshalerArray *[2]*textPointerMarshaler

	if !isTextMarshalerSequence(reflect.TypeOf(textPointerMarshalerSlice)) {
		t.Errorf("error: should be a sequence of text marshaler interfaces")
	}
	if !isTextMarshalerSequence(reflect.TypeOf(textPointerMarshalerArray)) {
		t.Errorf("error: should be a sequence of text marshaler interfaces")
	}
}

var commentTestToml = []byte(`
# it's a comment on type
[postgres]
  # isCommented = "dvalue"
  noComment = "cvalue"

  # A comment on AttrB with a
  # break line
  password = "bvalue"

  # A comment on AttrA
  user = "avalue"

  [[postgres.My]]

    # a comment on my on typeC
    My = "Foo"

  [[postgres.My]]

    # a comment on my on typeC
    My = "Baar"
`)

func TestMarshalComment(t *testing.T) {
	type TypeC struct {
		My string `comment:"a comment on my on typeC"`
	}
	type TypeB struct {
		AttrA string `toml:"user" comment:"A comment on AttrA"`
		AttrB string `toml:"password" comment:"A comment on AttrB with a\n break line"`
		AttrC string `toml:"noComment"`
		AttrD string `toml:"isCommented" commented:"true"`
		My    []TypeC
	}
	type TypeA struct {
		TypeB TypeB `toml:"postgres" comment:"it's a comment on type"`
	}

	ta := []TypeC{{My: "Foo"}, {My: "Baar"}}
	config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", AttrC: "cvalue", AttrD: "dvalue", My: ta}}
	result, err := Marshal(config)
	if err != nil {
		t.Fatal(err)
	}
	expected := commentTestToml
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestMarshalMultilineCommented(t *testing.T) {
	expectedToml := []byte(`# MultilineArray = [
  # 100,
  # 200,
  # 300,
# ]
# MultilineNestedArray = [
  # [
  # "a",
  # "b",
  # "c",
# ],
  # [
  # "d",
  # "e",
  # "f",
# ],
# ]
# MultilineString = """
# I
# am
# Allen"""
NonCommented = "Not commented line"
`)
	type StructWithMultiline struct {
		NonCommented         string
		MultilineString      string     `commented:"true" multiline:"true"`
		MultilineArray       []int      `commented:"true"`
		MultilineNestedArray [][]string `commented:"true"`
	}

	var buf bytes.Buffer
	enc := NewEncoder(&buf)
	if err := enc.ArraysWithOneElementPerLine(true).Encode(StructWithMultiline{
		NonCommented:    "Not commented line",
		MultilineString: "I\nam\nAllen",
		MultilineArray:  []int{100, 200, 300},
		MultilineNestedArray: [][]string{
			{"a", "b", "c"},
			{"d", "e", "f"},
		},
	}); err == nil {
		result := buf.Bytes()
		if !bytes.Equal(result, expectedToml) {
			t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expectedToml, result)
		}
	} else {
		t.Fatal(err)
	}
}

func TestMarshalNonPrimitiveTypeCommented(t *testing.T) {
	expectedToml := []byte(`
# [CommentedMapField]

  # [CommentedMapField.CommentedMapField1]
    # SingleLineString = "This line should be commented out"

  # [CommentedMapField.CommentedMapField2]
    # SingleLineString = "This line should be commented out"

# [CommentedStructField]

  # [CommentedStructField.CommentedStructField]
    # MultilineArray = [
      # 1,
      # 2,
    # ]
    # MultilineNestedArray = [
      # [
      # 10,
      # 20,
    # ],
      # [
      # 100,
      # 200,
    # ],
    # ]
    # MultilineString = """
# This line
# should be
# commented out"""

  # [CommentedStructField.NotCommentedStructField]
    # MultilineArray = [
      # 1,
      # 2,
    # ]
    # MultilineNestedArray = [
      # [
      # 10,
      # 20,
    # ],
      # [
      # 100,
      # 200,
    # ],
    # ]
    # MultilineString = """
# This line
# should be
# commented out"""

[NotCommentedStructField]

  # [NotCommentedStructField.CommentedStructField]
    # MultilineArray = [
      # 1,
      # 2,
    # ]
    # MultilineNestedArray = [
      # [
      # 10,
      # 20,
    # ],
      # [
      # 100,
      # 200,
    # ],
    # ]
    # MultilineString = """
# This line
# should be
# commented out"""

  [NotCommentedStructField.NotCommentedStructField]
    MultilineArray = [
      3,
      4,
    ]
    MultilineNestedArray = [
      [
      30,
      40,
    ],
      [
      300,
      400,
    ],
    ]
    MultilineString = """
This line
should NOT be
commented out"""
`)
	type InnerStruct struct {
		MultilineString      string `multiline:"true"`
		MultilineArray       []int
		MultilineNestedArray [][]int
	}
	type MiddleStruct struct {
		NotCommentedStructField InnerStruct
		CommentedStructField    InnerStruct `commented:"true"`
	}
	type OuterStruct struct {
		CommentedStructField    MiddleStruct `commented:"true"`
		NotCommentedStructField MiddleStruct
		CommentedMapField       map[string]struct{ SingleLineString string } `commented:"true"`
	}

	commentedTestStruct := OuterStruct{
		CommentedStructField: MiddleStruct{
			NotCommentedStructField: InnerStruct{
				MultilineString:      "This line\nshould be\ncommented out",
				MultilineArray:       []int{1, 2},
				MultilineNestedArray: [][]int{{10, 20}, {100, 200}},
			},
			CommentedStructField: InnerStruct{
				MultilineString:      "This line\nshould be\ncommented out",
				MultilineArray:       []int{1, 2},
				MultilineNestedArray: [][]int{{10, 20}, {100, 200}},
			},
		},
		NotCommentedStructField: MiddleStruct{
			NotCommentedStructField: InnerStruct{
				MultilineString:      "This line\nshould NOT be\ncommented out",
				MultilineArray:       []int{3, 4},
				MultilineNestedArray: [][]int{{30, 40}, {300, 400}},
			},
			CommentedStructField: InnerStruct{
				MultilineString:      "This line\nshould be\ncommented out",
				MultilineArray:       []int{1, 2},
				MultilineNestedArray: [][]int{{10, 20}, {100, 200}},
			},
		},
		CommentedMapField: map[string]struct{ SingleLineString string }{
			"CommentedMapField1": {
				SingleLineString: "This line should be commented out",
			},
			"CommentedMapField2": {
				SingleLineString: "This line should be commented out",
			},
		},
	}

	var buf bytes.Buffer
	enc := NewEncoder(&buf)
	if err := enc.ArraysWithOneElementPerLine(true).Encode(commentedTestStruct); err == nil {
		result := buf.Bytes()
		if !bytes.Equal(result, expectedToml) {
			t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expectedToml, result)
		}
	} else {
		t.Fatal(err)
	}
}

type mapsTestStruct struct {
	Simple map[string]string
	Paths  map[string]string
	Other  map[string]float64
	X      struct {
		Y struct {
			Z map[string]bool
		}
	}
}

var mapsTestData = mapsTestStruct{
	Simple: map[string]string{
		"one plus one": "two",
		"next":         "three",
	},
	Paths: map[string]string{
		"/this/is/a/path": "/this/is/also/a/path",
		"/heloo.txt":      "/tmp/lololo.txt",
	},
	Other: map[string]float64{
		"testing": 3.9999,
	},
	X: struct{ Y struct{ Z map[string]bool } }{
		Y: struct{ Z map[string]bool }{
			Z: map[string]bool{
				"is.Nested": true,
			},
		},
	},
}
var mapsTestToml = []byte(`
[Other]
  "testing" = 3.9999

[Paths]
  "/heloo.txt" = "/tmp/lololo.txt"
  "/this/is/a/path" = "/this/is/also/a/path"

[Simple]
  "next" = "three"
  "one plus one" = "two"

[X]

  [X.Y]

    [X.Y.Z]
      "is.Nested" = true
`)

func TestEncodeQuotedMapKeys(t *testing.T) {
	var buf bytes.Buffer
	if err := NewEncoder(&buf).QuoteMapKeys(true).Encode(mapsTestData); err != nil {
		t.Fatal(err)
	}
	result := buf.Bytes()
	expected := mapsTestToml
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad maps marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestDecodeQuotedMapKeys(t *testing.T) {
	result := mapsTestStruct{}
	err := NewDecoder(bytes.NewBuffer(mapsTestToml)).Decode(&result)
	expected := mapsTestData
	if err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(result, expected) {
		t.Errorf("Bad maps unmarshal: expected %v, got %v", expected, result)
	}
}

type structArrayNoTag struct {
	A struct {
		B []int64
		C []int64
	}
}

func TestMarshalArray(t *testing.T) {
	expected := []byte(`
[A]
  B = [1, 2, 3]
  C = [1]
`)

	m := structArrayNoTag{
		A: struct {
			B []int64
			C []int64
		}{
			B: []int64{1, 2, 3},
			C: []int64{1},
		},
	}

	b, err := Marshal(m)

	if err != nil {
		t.Fatal(err)
	}

	if !bytes.Equal(b, expected) {
		t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
	}
}

func TestMarshalArrayOnePerLine(t *testing.T) {
	expected := []byte(`
[A]
  B = [
    1,
    2,
    3,
  ]
  C = [1]
`)

	m := structArrayNoTag{
		A: struct {
			B []int64
			C []int64
		}{
			B: []int64{1, 2, 3},
			C: []int64{1},
		},
	}

	var buf bytes.Buffer
	encoder := NewEncoder(&buf).ArraysWithOneElementPerLine(true)
	err := encoder.Encode(m)

	if err != nil {
		t.Fatal(err)
	}

	b := buf.Bytes()

	if !bytes.Equal(b, expected) {
		t.Errorf("Bad arrays marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, b)
	}
}

var customTagTestToml = []byte(`
[postgres]
  password = "bvalue"
  user = "avalue"

  [[postgres.My]]
    My = "Foo"

  [[postgres.My]]
    My = "Baar"
`)

func TestMarshalCustomTag(t *testing.T) {
	type TypeC struct {
		My string
	}
	type TypeB struct {
		AttrA string `file:"user"`
		AttrB string `file:"password"`
		My    []TypeC
	}
	type TypeA struct {
		TypeB TypeB `file:"postgres"`
	}

	ta := []TypeC{{My: "Foo"}, {My: "Baar"}}
	config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue", My: ta}}
	var buf bytes.Buffer
	err := NewEncoder(&buf).SetTagName("file").Encode(config)
	if err != nil {
		t.Fatal(err)
	}
	expected := customTagTestToml
	result := buf.Bytes()
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

var customCommentTagTestToml = []byte(`
# db connection
[postgres]

  # db pass
  password = "bvalue"

  # db user
  user = "avalue"
`)

func TestMarshalCustomComment(t *testing.T) {
	type TypeB struct {
		AttrA string `toml:"user" descr:"db user"`
		AttrB string `toml:"password" descr:"db pass"`
	}
	type TypeA struct {
		TypeB TypeB `toml:"postgres" descr:"db connection"`
	}

	config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue"}}
	var buf bytes.Buffer
	err := NewEncoder(&buf).SetTagComment("descr").Encode(config)
	if err != nil {
		t.Fatal(err)
	}
	expected := customCommentTagTestToml
	result := buf.Bytes()
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

var customCommentedTagTestToml = []byte(`
[postgres]
  # password = "bvalue"
  # user = "avalue"
`)

func TestMarshalCustomCommented(t *testing.T) {
	type TypeB struct {
		AttrA string `toml:"user" disable:"true"`
		AttrB string `toml:"password" disable:"true"`
	}
	type TypeA struct {
		TypeB TypeB `toml:"postgres"`
	}

	config := TypeA{TypeB{AttrA: "avalue", AttrB: "bvalue"}}
	var buf bytes.Buffer
	err := NewEncoder(&buf).SetTagCommented("disable").Encode(config)
	if err != nil {
		t.Fatal(err)
	}
	expected := customCommentedTagTestToml
	result := buf.Bytes()
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestMarshalDirectMultilineString(t *testing.T) {
	tree := newTree()
	tree.SetWithOptions("mykey", SetOptions{
		Multiline: true,
	}, "my\x11multiline\nstring\ba\tb\fc\rd\"e\\!")
	result, err := tree.Marshal()
	if err != nil {
		t.Fatal("marshal should not error:", err)
	}
	expected := []byte("mykey = \"\"\"\nmy\\u0011multiline\nstring\\ba\tb\\fc\rd\"e\\!\"\"\"\n")
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestUnmarshalTabInStringAndQuotedKey(t *testing.T) {
	type Test struct {
		Field1 string `toml:"Fie	ld1"`
		Field2 string
	}

	type TestCase struct {
		desc     string
		input    []byte
		expected Test
	}

	testCases := []TestCase{
		{
			desc:  "multiline string with tab",
			input: []byte("Field2 = \"\"\"\nhello\tworld\"\"\""),
			expected: Test{
				Field2: "hello\tworld",
			},
		},
		{
			desc:  "quoted key with tab",
			input: []byte("\"Fie\tld1\" = \"key with tab\""),
			expected: Test{
				Field1: "key with tab",
			},
		},
		{
			desc:  "basic string tab",
			input: []byte("Field2 = \"hello\tworld\""),
			expected: Test{
				Field2: "hello\tworld",
			},
		},
	}

	for i := range testCases {
		result := Test{}
		err := Unmarshal(testCases[i].input, &result)
		if err != nil {
			t.Errorf("%s test error:%v", testCases[i].desc, err)
			continue
		}

		if !reflect.DeepEqual(result, testCases[i].expected) {
			t.Errorf("%s test error: expected\n-----\n%+v\n-----\ngot\n-----\n%+v\n-----\n",
				testCases[i].desc, testCases[i].expected, result)
		}
	}
}

var customMultilineTagTestToml = []byte(`int_slice = [
  1,
  2,
  3,
]
`)

func TestMarshalCustomMultiline(t *testing.T) {
	type TypeA struct {
		AttrA []int `toml:"int_slice" mltln:"true"`
	}

	config := TypeA{AttrA: []int{1, 2, 3}}
	var buf bytes.Buffer
	err := NewEncoder(&buf).ArraysWithOneElementPerLine(true).SetTagMultiline("mltln").Encode(config)
	if err != nil {
		t.Fatal(err)
	}
	expected := customMultilineTagTestToml
	result := buf.Bytes()
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

func TestMultilineWithAdjacentQuotationMarks(t *testing.T) {
	type testStruct struct {
		Str string `multiline:"true"`
	}
	type testCase struct {
		expected []byte
		data     testStruct
	}

	testCases := []testCase{
		{
			expected: []byte(`Str = """
hello\""""
`),
			data: testStruct{
				Str: "hello\"",
			},
		},
		{
			expected: []byte(`Str = """
""\"""\"""\""""
`),
			data: testStruct{
				Str: "\"\"\"\"\"\"\"\"\"",
			},
		},
	}
	for i := range testCases {
		result, err := Marshal(testCases[i].data)
		if err != nil {
			t.Fatal(err)
		}

		if !bytes.Equal(result, testCases[i].expected) {
			t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n",
				testCases[i].expected, result)
		} else {
			var data testStruct
			if err = Unmarshal(result, &data); err != nil {
				t.Fatal(err)
			}
			if data.Str != testCases[i].data.Str {
				t.Errorf("Round trip test fail: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n",
					testCases[i].data.Str, data.Str)
			}
		}
	}
}

func TestMarshalEmbedTree(t *testing.T) {
	expected := []byte(`OuterField1 = "Out"
OuterField2 = 1024

[TreeField]
  InnerField1 = "In"
  InnerField2 = 2048

  [TreeField.EmbedStruct]
    EmbedField = "Embed"
`)
	type InnerStruct struct {
		InnerField1 string
		InnerField2 int
		EmbedStruct struct {
			EmbedField string
		}
	}

	type OuterStruct struct {
		OuterField1 string
		OuterField2 int
		TreeField   *Tree
	}

	tree, err := Load(`
InnerField1 = "In"
InnerField2 = 2048

[EmbedStruct]
	EmbedField = "Embed"
`)
	if err != nil {
		t.Fatal(err)
	}

	out := OuterStruct{
		"Out",
		1024,
		tree,
	}
	actual, _ := Marshal(out)

	if !bytes.Equal(actual, expected) {
		t.Errorf("Bad marshal: expected %s, got %s", expected, actual)
	}
}

var testDocBasicToml = []byte(`
[document]
  bool_val = true
  date_val = 1979-05-27T07:32:00Z
  float_val = 123.4
  int_val = 5000
  string_val = "Bite me"
  uint_val = 5001
`)

type testDocCustomTag struct {
	Doc testDocBasicsCustomTag `file:"document"`
}
type testDocBasicsCustomTag struct {
	Bool       bool      `file:"bool_val"`
	Date       time.Time `file:"date_val"`
	Float      float32   `file:"float_val"`
	Int        int       `file:"int_val"`
	Uint       uint      `file:"uint_val"`
	String     *string   `file:"string_val"`
	unexported int       `file:"shouldntBeHere"`
}

var testDocCustomTagData = testDocCustomTag{
	Doc: testDocBasicsCustomTag{
		Bool:       true,
		Date:       time.Date(1979, 5, 27, 7, 32, 0, 0, time.UTC),
		Float:      123.4,
		Int:        5000,
		Uint:       5001,
		String:     &biteMe,
		unexported: 0,
	},
}

func TestUnmarshalCustomTag(t *testing.T) {
	buf := bytes.NewBuffer(testDocBasicToml)

	result := testDocCustomTag{}
	err := NewDecoder(buf).SetTagName("file").Decode(&result)
	if err != nil {
		t.Fatal(err)
	}
	expected := testDocCustomTagData
	if !reflect.DeepEqual(result, expected) {
		resStr, _ := json.MarshalIndent(result, "", "  ")
		expStr, _ := json.MarshalIndent(expected, "", "  ")
		t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)

	}
}

func TestUnmarshalMap(t *testing.T) {
	testToml := []byte(`
		a = 1
		b = 2
		c = 3
		`)
	var result map[string]int
	err := Unmarshal(testToml, &result)
	if err != nil {
		t.Errorf("Received unexpected error: %s", err)
		return
	}

	expected := map[string]int{
		"a": 1,
		"b": 2,
		"c": 3,
	}

	if !reflect.DeepEqual(result, expected) {
		t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
	}
}

func TestUnmarshalMapWithTypedKey(t *testing.T) {
	testToml := []byte(`
		a = 1
		b = 2
		c = 3
		`)

	type letter string
	var result map[letter]int
	err := Unmarshal(testToml, &result)
	if err != nil {
		t.Errorf("Received unexpected error: %s", err)
		return
	}

	expected := map[letter]int{
		"a": 1,
		"b": 2,
		"c": 3,
	}

	if !reflect.DeepEqual(result, expected) {
		t.Errorf("Bad unmarshal: expected %v, got %v", expected, result)
	}
}

func TestUnmarshalNonPointer(t *testing.T) {
	a := 1
	err := Unmarshal([]byte{}, a)
	if err == nil {
		t.Fatal("unmarshal should err when given a non pointer")
	}
}

func TestUnmarshalInvalidPointerKind(t *testing.T) {
	a := 1
	err := Unmarshal([]byte{}, &a)
	if err == nil {
		t.Fatal("unmarshal should err when given an invalid pointer type")
	}
}

func TestMarshalSlice(t *testing.T) {
	m := make([]int, 1)
	m[0] = 1

	var buf bytes.Buffer
	err := NewEncoder(&buf).Encode(&m)
	if err == nil {
		t.Error("expected error, got nil")
		return
	}
	if err.Error() != "Only pointer to struct can be marshaled to TOML" {
		t.Fail()
	}
}

func TestMarshalSlicePointer(t *testing.T) {
	m := make([]int, 1)
	m[0] = 1

	var buf bytes.Buffer
	err := NewEncoder(&buf).Encode(m)
	if err == nil {
		t.Error("expected error, got nil")
		return
	}
	if err.Error() != "Only a struct or map can be marshaled to TOML" {
		t.Fail()
	}
}

func TestMarshalNestedArrayInlineTables(t *testing.T) {
	type table struct {
		Value1 int `toml:"ZValue1"`
		Value2 int `toml:"YValue2"`
		Value3 int `toml:"XValue3"`
	}

	type nestedTable struct {
		Table table
	}

	nestedArray := struct {
		Simple        [][]table
		SimplePointer *[]*[]table
		Nested        [][]nestedTable
		NestedPointer *[]*[]nestedTable
	}{
		Simple:        [][]table{{{Value1: 1}, {Value1: 10}}},
		SimplePointer: &[]*[]table{{{Value2: 2}}},
		Nested:        [][]nestedTable{{{Table: table{Value3: 3}}}},
		NestedPointer: &[]*[]nestedTable{{{Table: table{Value3: -3}}}},
	}

	expectedPreserve := `Simple = [[{ ZValue1 = 1, YValue2 = 0, XValue3 = 0 }, { ZValue1 = 10, YValue2 = 0, XValue3 = 0 }]]
SimplePointer = [[{ ZValue1 = 0, YValue2 = 2, XValue3 = 0 }]]
Nested = [[{ Table = { ZValue1 = 0, YValue2 = 0, XValue3 = 3 } }]]
NestedPointer = [[{ Table = { ZValue1 = 0, YValue2 = 0, XValue3 = -3 } }]]
`

	expectedAlphabetical := `Nested = [[{ Table = { XValue3 = 3, YValue2 = 0, ZValue1 = 0 } }]]
NestedPointer = [[{ Table = { XValue3 = -3, YValue2 = 0, ZValue1 = 0 } }]]
Simple = [[{ XValue3 = 0, YValue2 = 0, ZValue1 = 1 }, { XValue3 = 0, YValue2 = 0, ZValue1 = 10 }]]
SimplePointer = [[{ XValue3 = 0, YValue2 = 2, ZValue1 = 0 }]]
`

	var bufPreserve bytes.Buffer
	if err := NewEncoder(&bufPreserve).Order(OrderPreserve).Encode(nestedArray); err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}
	if !bytes.Equal(bufPreserve.Bytes(), []byte(expectedPreserve)) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expectedPreserve, bufPreserve.String())
	}

	var bufAlphabetical bytes.Buffer
	if err := NewEncoder(&bufAlphabetical).Order(OrderAlphabetical).Encode(nestedArray); err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}
	if !bytes.Equal(bufAlphabetical.Bytes(), []byte(expectedAlphabetical)) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expectedAlphabetical, bufAlphabetical.String())
	}
}

type testDuration struct {
	Nanosec   time.Duration  `toml:"nanosec"`
	Microsec1 time.Duration  `toml:"microsec1"`
	Microsec2 *time.Duration `toml:"microsec2"`
	Millisec  time.Duration  `toml:"millisec"`
	Sec       time.Duration  `toml:"sec"`
	Min       time.Duration  `toml:"min"`
	Hour      time.Duration  `toml:"hour"`
	Mixed     time.Duration  `toml:"mixed"`
	AString   string         `toml:"a_string"`
}

var testDurationToml = []byte(`
nanosec = "1ns"
microsec1 = "1us"
microsec2 = "1µs"
millisec = "1ms"
sec = "1s"
min = "1m"
hour = "1h"
mixed = "1h1m1s1ms1µs1ns"
a_string = "15s"
`)

func TestUnmarshalDuration(t *testing.T) {
	buf := bytes.NewBuffer(testDurationToml)

	result := testDuration{}
	err := NewDecoder(buf).Decode(&result)
	if err != nil {
		t.Fatal(err)
	}
	ms := time.Duration(1) * time.Microsecond
	expected := testDuration{
		Nanosec:   1,
		Microsec1: time.Microsecond,
		Microsec2: &ms,
		Millisec:  time.Millisecond,
		Sec:       time.Second,
		Min:       time.Minute,
		Hour:      time.Hour,
		Mixed: time.Hour +
			time.Minute +
			time.Second +
			time.Millisecond +
			time.Microsecond +
			time.Nanosecond,
		AString: "15s",
	}
	if !reflect.DeepEqual(result, expected) {
		resStr, _ := json.MarshalIndent(result, "", "  ")
		expStr, _ := json.MarshalIndent(expected, "", "  ")
		t.Errorf("Bad unmarshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expStr, resStr)

	}
}

var testDurationToml2 = []byte(`a_string = "15s"
hour = "1h0m0s"
microsec1 = "1µs"
microsec2 = "1µs"
millisec = "1ms"
min = "1m0s"
mixed = "1h1m1.001001001s"
nanosec = "1ns"
sec = "1s"
`)

func TestMarshalDuration(t *testing.T) {
	ms := time.Duration(1) * time.Microsecond
	data := testDuration{
		Nanosec:   1,
		Microsec1: time.Microsecond,
		Microsec2: &ms,
		Millisec:  time.Millisecond,
		Sec:       time.Second,
		Min:       time.Minute,
		Hour:      time.Hour,
		Mixed: time.Hour +
			time.Minute +
			time.Second +
			time.Millisecond +
			time.Microsecond +
			time.Nanosecond,
		AString: "15s",
	}

	var buf bytes.Buffer
	err := NewEncoder(&buf).Encode(data)
	if err != nil {
		t.Fatal(err)
	}
	expected := testDurationToml2
	result := buf.Bytes()
	if !bytes.Equal(result, expected) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
	}
}

type testBadDuration struct {
	Val time.Duration `toml:"val"`
}

var testBadDurationToml = []byte(`val = "1z"`)

func TestUnmarshalBadDuration(t *testing.T) {
	buf := bytes.NewBuffer(testBadDurationToml)

	result := testBadDuration{}
	err := NewDecoder(buf).Decode(&result)
	if err == nil {
		t.Fatal("expected bad duration error")
	}
}

var testCamelCaseKeyToml = []byte(`fooBar = 10`)

func TestUnmarshalCamelCaseKey(t *testing.T) {
	var x struct {
		FooBar int
		B      int
	}

	if err := Unmarshal(testCamelCaseKeyToml, &x); err != nil {
		t.Fatal(err)
	}

	if x.FooBar != 10 {
		t.Fatal("Did not set camelCase'd key")
	}
}

func TestUnmarshalNegativeUint(t *testing.T) {
	type check struct{ U uint }

	tree, _ := Load("u = -1")
	err := tree.Unmarshal(&check{})
	if err.Error() != "(1, 1): -1(int64) is negative so does not fit in uint" {
		t.Error("expect err:(1, 1): -1(int64) is negative so does not fit in uint but got:", err)
	}
}

func TestUnmarshalCheckConversionFloatInt(t *testing.T) {
	type conversionCheck struct {
		U uint
		I int
		F float64
	}

	treeU, _ := Load("u = 1e300")
	treeI, _ := Load("i = 1e300")
	treeF, _ := Load("f = 9223372036854775806")

	errU := treeU.Unmarshal(&conversionCheck{})
	errI := treeI.Unmarshal(&conversionCheck{})
	errF := treeF.Unmarshal(&conversionCheck{})

	if errU.Error() != "(1, 1): Can't convert 1e+300(float64) to uint" {
		t.Error("expect err:(1, 1): Can't convert 1e+300(float64) to uint but got:", errU)
	}
	if errI.Error() != "(1, 1): Can't convert 1e+300(float64) to int" {
		t.Error("expect err:(1, 1): Can't convert 1e+300(float64) to int but got:", errI)
	}
	if errF.Error() != "(1, 1): Can't convert 9223372036854775806(int64) to float64" {
		t.Error("expect err:(1, 1): Can't convert 9223372036854775806(int64) to float64 but got:", errF)
	}
}

func TestUnmarshalOverflow(t *testing.T) {
	type overflow struct {
		U8  uint8
		I8  int8
		F32 float32
	}

	treeU8, _ := Load("u8 = 300")
	treeI8, _ := Load("i8 = 300")
	treeF32, _ := Load("f32 = 1e300")

	errU8 := treeU8.Unmarshal(&overflow{})
	errI8 := treeI8.Unmarshal(&overflow{})
	errF32 := treeF32.Unmarshal(&overflow{})

	if errU8.Error() != "(1, 1): 300(int64) would overflow uint8" {
		t.Error("expect err:(1, 1): 300(int64) would overflow uint8 but got:", errU8)
	}
	if errI8.Error() != "(1, 1): 300(int64) would overflow int8" {
		t.Error("expect err:(1, 1): 300(int64) would overflow int8 but got:", errI8)
	}
	if errF32.Error() != "(1, 1): 1e+300(float64) would overflow float32" {
		t.Error("expect err:(1, 1): 1e+300(float64) would overflow float32 but got:", errF32)
	}
}

func TestUnmarshalDefault(t *testing.T) {
	type EmbeddedStruct struct {
		StringField string `default:"c"`
	}

	type aliasUint uint

	var doc struct {
		StringField       string  `default:"a"`
		BoolField         bool    `default:"true"`
		UintField         uint    `default:"1"`
		Uint8Field        uint8   `default:"8"`
		Uint16Field       uint16  `default:"16"`
		Uint32Field       uint32  `default:"32"`
		Uint64Field       uint64  `default:"64"`
		IntField          int     `default:"-1"`
		Int8Field         int8    `default:"-8"`
		Int16Field        int16   `default:"-16"`
		Int32Field        int32   `default:"-32"`
		Int64Field        int64   `default:"-64"`
		Float32Field      float32 `default:"32.1"`
		Float64Field      float64 `default:"64.1"`
		NonEmbeddedStruct struct {
			StringField string `default:"b"`
		}
		EmbeddedStruct
		AliasUintField aliasUint `default:"1000"`
	}

	err := Unmarshal([]byte(``), &doc)
	if err != nil {
		t.Fatal(err)
	}
	if doc.BoolField != true {
		t.Errorf("BoolField should be true, not %t", doc.BoolField)
	}
	if doc.StringField != "a" {
		t.Errorf("StringField should be \"a\", not %s", doc.StringField)
	}
	if doc.UintField != 1 {
		t.Errorf("UintField should be 1, not %d", doc.UintField)
	}
	if doc.Uint8Field != 8 {
		t.Errorf("Uint8Field should be 8, not %d", doc.Uint8Field)
	}
	if doc.Uint16Field != 16 {
		t.Errorf("Uint16Field should be 16, not %d", doc.Uint16Field)
	}
	if doc.Uint32Field != 32 {
		t.Errorf("Uint32Field should be 32, not %d", doc.Uint32Field)
	}
	if doc.Uint64Field != 64 {
		t.Errorf("Uint64Field should be 64, not %d", doc.Uint64Field)
	}
	if doc.IntField != -1 {
		t.Errorf("IntField should be -1, not %d", doc.IntField)
	}
	if doc.Int8Field != -8 {
		t.Errorf("Int8Field should be -8, not %d", doc.Int8Field)
	}
	if doc.Int16Field != -16 {
		t.Errorf("Int16Field should be -16, not %d", doc.Int16Field)
	}
	if doc.Int32Field != -32 {
		t.Errorf("Int32Field should be -32, not %d", doc.Int32Field)
	}
	if doc.Int64Field != -64 {
		t.Errorf("Int64Field should be -64, not %d", doc.Int64Field)
	}
	if doc.Float32Field != 32.1 {
		t.Errorf("Float32Field should be 32.1, not %f", doc.Float32Field)
	}
	if doc.Float64Field != 64.1 {
		t.Errorf("Float64Field should be 64.1, not %f", doc.Float64Field)
	}
	if doc.NonEmbeddedStruct.StringField != "b" {
		t.Errorf("StringField should be \"b\", not %s", doc.NonEmbeddedStruct.StringField)
	}
	if doc.EmbeddedStruct.StringField != "c" {
		t.Errorf("StringField should be \"c\", not %s", doc.EmbeddedStruct.StringField)
	}
	if doc.AliasUintField != 1000 {
		t.Errorf("AliasUintField should be 1000, not %d", doc.AliasUintField)
	}
}

func TestUnmarshalDefaultFailureBool(t *testing.T) {
	var doc struct {
		Field bool `default:"blah"`
	}

	err := Unmarshal([]byte(``), &doc)
	if err == nil {
		t.Fatal("should error")
	}
}

func TestUnmarshalDefaultFailureInt(t *testing.T) {
	var doc struct {
		Field int `default:"blah"`
	}

	err := Unmarshal([]byte(``), &doc)
	if err == nil {
		t.Fatal("should error")
	}
}

func TestUnmarshalDefaultFailureInt64(t *testing.T) {
	var doc struct {
		Field int64 `default:"blah"`
	}

	err := Unmarshal([]byte(``), &doc)
	if err == nil {
		t.Fatal("should error")
	}
}

func TestUnmarshalDefaultFailureFloat64(t *testing.T) {
	var doc struct {
		Field float64 `default:"blah"`
	}

	err := Unmarshal([]byte(``), &doc)
	if err == nil {
		t.Fatal("should error")
	}
}

func TestUnmarshalDefaultFailureUnsupported(t *testing.T) {
	var doc struct {
		Field struct{} `default:"blah"`
	}

	err := Unmarshal([]byte(``), &doc)
	if err == nil {
		t.Fatal("should error")
	}
}

func TestMarshalNestedAnonymousStructs(t *testing.T) {
	type Embedded struct {
		Value string `toml:"value"`
		Top   struct {
			Value string `toml:"value"`
		} `toml:"top"`
	}

	type Named struct {
		Value string `toml:"value"`
	}

	var doc struct {
		Embedded
		Named     `toml:"named"`
		Anonymous struct {
			Value string `toml:"value"`
		} `toml:"anonymous"`
	}

	expected := `value = ""

[anonymous]
  value = ""

[named]
  value = ""

[top]
  value = ""
`

	result, err := Marshal(doc)
	if err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}
	if !bytes.Equal(result, []byte(expected)) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, string(result))
	}
}

func TestEncoderPromoteNestedAnonymousStructs(t *testing.T) {
	type Embedded struct {
		Value string `toml:"value"`
	}

	var doc struct {
		Embedded
	}

	expected := `
[Embedded]
  value = ""
`
	var buf bytes.Buffer
	if err := NewEncoder(&buf).PromoteAnonymous(true).Encode(doc); err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}
	if !bytes.Equal(buf.Bytes(), []byte(expected)) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, buf.String())
	}
}

func TestMarshalNestedAnonymousStructs_DuplicateField(t *testing.T) {
	type Embedded struct {
		Value string `toml:"value"`
		Top   struct {
			Value string `toml:"value"`
		} `toml:"top"`
	}

	var doc struct {
		Value string `toml:"value"`
		Embedded
	}
	doc.Embedded.Value = "shadowed"
	doc.Value = "shadows"

	expected := `value = "shadows"

[top]
  value = ""
`

	result, err := Marshal(doc)
	if err != nil {
		t.Fatalf("unexpected error: %s", err.Error())
	}
	if !bytes.Equal(result, []byte(expected)) {
		t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, string(result))
	}
}

func TestUnmarshalNestedAnonymousStructs(t *testing.T) {
	type Nested struct {
		Value string `toml:"nested_field"`
	}
	type Deep struct {
		Nested
	}
	type Document struct {
		Deep
		Value string `toml:"own_field"`
	}

	var doc Document

	err := Unmarshal([]byte(`nested_field = "nested value"`+"\n"+`own_field = "own value"`), &doc)
	if err != nil {
		t.Fatal("should not error")
	}
	if doc.Value != "own value" || doc.Nested.Value != "nested value" {
		t.Fatal("unexpected values")
	}
}

func TestUnmarshalNestedAnonymousStructs_Controversial(t *testing.T) {
	type Nested struct {
		Value string `toml:"nested"`
	}
	type Deep struct {
		Nested
	}
	type Document struct {
		Deep
		Value string `toml:"own"`
	}

	var doc Document

	err := Unmarshal([]byte(`nested = "nested value"`+"\n"+`own = "own value"`), &doc)
	if err == nil {
		t.Fatal("should error")
	}
}

type unexportedFieldPreservationTest struct {
	Exported   string `toml:"exported"`
	unexported string
	Nested1    unexportedFieldPreservationTestNested    `toml:"nested1"`
	Nested2    *unexportedFieldPreservationTestNested   `toml:"nested2"`
	Nested3    *unexportedFieldPreservationTestNested   `toml:"nested3"`
	Slice1     []unexportedFieldPreservationTestNested  `toml:"slice1"`
	Slice2     []*unexportedFieldPreservationTestNested `toml:"slice2"`
}

type unexportedFieldPreservationTestNested struct {
	Exported1   string `toml:"exported1"`
	unexported1 string
}

func TestUnmarshalPreservesUnexportedFields(t *testing.T) {
	toml := `
	exported = "visible"
	unexported = "ignored"

	[nested1]
	exported1 = "visible1"
	unexported1 = "ignored1"

	[nested2]
	exported1 = "visible2"
	unexported1 = "ignored2"

	[nested3]
	exported1 = "visible3"
	unexported1 = "ignored3"

	[[slice1]]
	exported1 = "visible3"

	[[slice1]]
	exported1 = "visible4"

	[[slice2]]
	exported1 = "visible5"
	`

	t.Run("unexported field should not be set from toml", func(t *testing.T) {
		var actual unexportedFieldPreservationTest
		err := Unmarshal([]byte(toml), &actual)

		if err != nil {
			t.Fatal("did not expect an error")
		}

		expect := unexportedFieldPreservationTest{
			Exported:   "visible",
			unexported: "",
			Nested1:    unexportedFieldPreservationTestNested{"visible1", ""},
			Nested2:    &unexportedFieldPreservationTestNested{"visible2", ""},
			Nested3:    &unexportedFieldPreservationTestNested{"visible3", ""},
			Slice1: []unexportedFieldPreservationTestNested{
				{Exported1: "visible3"},
				{Exported1: "visible4"},
			},
			Slice2: []*unexportedFieldPreservationTestNested{
				{Exported1: "visible5"},
			},
		}

		if !reflect.DeepEqual(actual, expect) {
			t.Fatalf("%+v did not equal %+v", actual, expect)
		}
	})

	t.Run("unexported field should be preserved", func(t *testing.T) {
		actual := unexportedFieldPreservationTest{
			Exported:   "foo",
			unexported: "bar",
			Nested1:    unexportedFieldPreservationTestNested{"baz", "bax"},
			Nested2:    nil,
			Nested3:    &unexportedFieldPreservationTestNested{"baz", "bax"},
		}
		err := Unmarshal([]byte(toml), &actual)

		if err != nil {
			t.Fatal("did not expect an error")
		}

		expect := unexportedFieldPreservationTest{
			Exported:   "visible",
			unexported: "bar",
			Nested1:    unexportedFieldPreservationTestNested{"visible1", "bax"},
			Nested2:    &unexportedFieldPreservationTestNested{"visible2", ""},
			Nested3:    &unexportedFieldPreservationTestNested{"visible3", "bax"},
			Slice1: []unexportedFieldPreservationTestNested{
				{Exported1: "visible3"},
				{Exported1: "visible4"},
			},
			Slice2: []*unexportedFieldPreservationTestNested{
				{Exported1: "visible5"},
			},
		}

		if !reflect.DeepEqual(actual, expect) {
			t.Fatalf("%+v did not equal %+v", actual, expect)
		}
	})
}

func TestTreeMarshal(t *testing.T) {
	cases := [][]byte{
		basicTestToml,
		marshalTestToml,
		emptyTestToml,
		pointerTestToml,
	}
	for _, expected := range cases {
		t.Run("", func(t *testing.T) {
			tree, err := LoadBytes(expected)
			if err != nil {
				t.Fatal(err)
			}
			result, err := tree.Marshal()
			if err != nil {
				t.Fatal(err)
			}
			if !bytes.Equal(result, expected) {
				t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", expected, result)
			}
		})
	}
}

func TestMarshalArrays(t *testing.T) {
	cases := []struct {
		Data     interface{}
		Expected string
	}{
		{
			Data: struct {
				XY [2]int
			}{
				XY: [2]int{1, 2},
			},
			Expected: `XY = [1, 2]
`,
		},
		{
			Data: struct {
				XY [1][2]int
			}{
				XY: [1][2]int{{1, 2}},
			},
			Expected: `XY = [[1, 2]]
`,
		},
		{
			Data: struct {
				XY [1][]int
			}{
				XY: [1][]int{{1, 2}},
			},
			Expected: `XY = [[1, 2]]
`,
		},
		{
			Data: struct {
				XY [][2]int
			}{
				XY: [][2]int{{1, 2}},
			},
			Expected: `XY = [[1, 2]]
`,
		},
	}
	for _, tc := range cases {
		t.Run("", func(t *testing.T) {
			result, err := Marshal(tc.Data)
			if err != nil {
				t.Fatal(err)
			}
			if !bytes.Equal(result, []byte(tc.Expected)) {
				t.Errorf("Bad marshal: expected\n-----\n%s\n-----\ngot\n-----\n%s\n-----\n", []byte(tc.Expected), result)
			}
		})
	}
}

func TestUnmarshalLocalDate(t *testing.T) {
	t.Run("ToLocalDate", func(t *testing.T) {
		type dateStruct struct {
			Date LocalDate
		}

		toml := `date = 1979-05-27`

		var obj dateStruct

		err := Unmarshal([]byte(toml), &obj)

		if err != nil {
			t.Fatal(err)
		}

		if obj.Date.Year != 1979 {
			t.Errorf("expected year 1979, got %d", obj.Date.Year)
		}
		if obj.Date.Month != 5 {
			t.Errorf("expected month 5, got %d", obj.Date.Month)
		}
		if obj.Date.Day != 27 {
			t.Errorf("expected day 27, got %d", obj.Date.Day)
		}
	})

	t.Run("ToLocalDate", func(t *testing.T) {
		type dateStruct struct {
			Date time.Time
		}

		toml := `date = 1979-05-27`

		var obj dateStruct

		err := Unmarshal([]byte(toml), &obj)

		if err != nil {
			t.Fatal(err)
		}

		if obj.Date.Year() != 1979 {
			t.Errorf("expected year 1979, got %d", obj.Date.Year())
		}
		if obj.Date.Month() != 5 {
			t.Errorf("expected month 5, got %d", obj.Date.Month())
		}
		if obj.Date.Day() != 27 {
			t.Errorf("expected day 27, got %d", obj.Date.Day())
		}
	})
}

func TestMarshalLocalDate(t *testing.T) {
	type dateStruct struct {
		Date LocalDate
	}

	obj := dateStruct{Date: LocalDate{
		Year:  1979,
		Month: 5,
		Day:   27,
	}}

	b, err := Marshal(obj)

	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	got := string(b)
	expected := `Date = 1979-05-27
`

	if got != expected {
		t.Errorf("expected '%s', got '%s'", expected, got)
	}
}

func TestUnmarshalLocalDateTime(t *testing.T) {
	examples := []struct {
		name string
		in   string
		out  LocalDateTime
	}{
		{
			name: "normal",
			in:   "1979-05-27T07:32:00",
			out: LocalDateTime{
				Date: LocalDate{
					Year:  1979,
					Month: 5,
					Day:   27,
				},
				Time: LocalTime{
					Hour:       7,
					Minute:     32,
					Second:     0,
					Nanosecond: 0,
				},
			}},
		{
			name: "with nanoseconds",
			in:   "1979-05-27T00:32:00.999999",
			out: LocalDateTime{
				Date: LocalDate{
					Year:  1979,
					Month: 5,
					Day:   27,
				},
				Time: LocalTime{
					Hour:       0,
					Minute:     32,
					Second:     0,
					Nanosecond: 999999000,
				},
			},
		},
	}

	for i, example := range examples {
		toml := fmt.Sprintf(`date = %s`, example.in)

		t.Run(fmt.Sprintf("ToLocalDateTime_%d_%s", i, example.name), func(t *testing.T) {
			type dateStruct struct {
				Date LocalDateTime
			}

			var obj dateStruct

			err := Unmarshal([]byte(toml), &obj)

			if err != nil {
				t.Fatal(err)
			}

			if obj.Date != example.out {
				t.Errorf("expected '%s', got '%s'", example.out, obj.Date)
			}
		})

		t.Run(fmt.Sprintf("ToTime_%d_%s", i, example.name), func(t *testing.T) {
			type dateStruct struct {
				Date time.Time
			}

			var obj dateStruct

			err := Unmarshal([]byte(toml), &obj)

			if err != nil {
				t.Fatal(err)
			}

			if obj.Date.Year() != example.out.Date.Year {
				t.Errorf("expected year %d, got %d", example.out.Date.Year, obj.Date.Year())
			}
			if obj.Date.Month() != example.out.Date.Month {
				t.Errorf("expected month %d, got %d", example.out.Date.Month, obj.Date.Month())
			}
			if obj.Date.Day() != example.out.Date.Day {
				t.Errorf("expected day %d, got %d", example.out.Date.Day, obj.Date.Day())
			}
			if obj.Date.Hour() != example.out.Time.Hour {
				t.Errorf("expected hour %d, got %d", example.out.Time.Hour, obj.Date.Hour())
			}
			if obj.Date.Minute() != example.out.Time.Minute {
				t.Errorf("expected minute %d, got %d", example.out.Time.Minute, obj.Date.Minute())
			}
			if obj.Date.Second() != example.out.Time.Second {
				t.Errorf("expected second %d, got %d", example.out.Time.Second, obj.Date.Second())
			}
			if obj.Date.Nanosecond() != example.out.Time.Nanosecond {
				t.Errorf("expected nanoseconds %d, got %d", example.out.Time.Nanosecond, obj.Date.Nanosecond())
			}
		})
	}
}

func TestMarshalLocalDateTime(t *testing.T) {
	type dateStruct struct {
		DateTime LocalDateTime
	}

	examples := []struct {
		name string
		in   LocalDateTime
		out  string
	}{
		{
			name: "normal",
			out:  "DateTime = 1979-05-27T07:32:00\n",
			in: LocalDateTime{
				Date: LocalDate{
					Year:  1979,
					Month: 5,
					Day:   27,
				},
				Time: LocalTime{
					Hour:       7,
					Minute:     32,
					Second:     0,
					Nanosecond: 0,
				},
			}},
		{
			name: "with nanoseconds",
			out:  "DateTime = 1979-05-27T00:32:00.999999000\n",
			in: LocalDateTime{
				Date: LocalDate{
					Year:  1979,
					Month: 5,
					Day:   27,
				},
				Time: LocalTime{
					Hour:       0,
					Minute:     32,
					Second:     0,
					Nanosecond: 999999000,
				},
			},
		},
	}

	for i, example := range examples {
		t.Run(fmt.Sprintf("%d_%s", i, example.name), func(t *testing.T) {
			obj := dateStruct{
				DateTime: example.in,
			}
			b, err := Marshal(obj)

			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			got := string(b)

			if got != example.out {
				t.Errorf("expected '%s', got '%s'", example.out, got)
			}
		})
	}
}

func TestUnmarshalLocalTime(t *testing.T) {
	examples := []struct {
		name string
		in   string
		out  LocalTime
	}{
		{
			name: "normal",
			in:   "07:32:00",
			out: LocalTime{
				Hour:       7,
				Minute:     32,
				Second:     0,
				Nanosecond: 0,
			},
		},
		{
			name: "with nanoseconds",
			in:   "00:32:00.999999",
			out: LocalTime{
				Hour:       0,
				Minute:     32,
				Second:     0,
				Nanosecond: 999999000,
			},
		},
	}

	for i, example := range examples {
		toml := fmt.Sprintf(`Time = %s`, example.in)

		t.Run(fmt.Sprintf("ToLocalTime_%d_%s", i, example.name), func(t *testing.T) {
			type dateStruct struct {
				Time LocalTime
			}

			var obj dateStruct

			err := Unmarshal([]byte(toml), &obj)

			if err != nil {
				t.Fatal(err)
			}

			if obj.Time != example.out {
				t.Errorf("expected '%s', got '%s'", example.out, obj.Time)
			}
		})
	}
}

func TestMarshalLocalTime(t *testing.T) {
	type timeStruct struct {
		Time LocalTime
	}

	examples := []struct {
		name string
		in   LocalTime
		out  string
	}{
		{
			name: "normal",
			out:  "Time = 07:32:00\n",
			in: LocalTime{
				Hour:       7,
				Minute:     32,
				Second:     0,
				Nanosecond: 0,
			}},
		{
			name: "with nanoseconds",
			out:  "Time = 00:32:00.999999000\n",
			in: LocalTime{
				Hour:       0,
				Minute:     32,
				Second:     0,
				Nanosecond: 999999000,
			},
		},
	}

	for i, example := range examples {
		t.Run(fmt.Sprintf("%d_%s", i, example.name), func(t *testing.T) {
			obj := timeStruct{
				Time: example.in,
			}
			b, err := Marshal(obj)

			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			got := string(b)

			if got != example.out {
				t.Errorf("expected '%s', got '%s'", example.out, got)
			}
		})
	}
}

// test case for issue #339
func TestUnmarshalSameInnerField(t *testing.T) {
	type InterStruct2 struct {
		Test string
		Name string
		Age  int
	}
	type Inter2 struct {
		Name         string
		Age          int
		InterStruct2 InterStruct2
	}
	type Server struct {
		Name   string `toml:"name"`
		Inter2 Inter2 `toml:"inter2"`
	}

	var server Server

	if err := Unmarshal([]byte(`name = "123"
[inter2]
name = "inter2"
age = 222`), &server); err == nil {
		expected := Server{
			Name: "123",
			Inter2: Inter2{
				Name: "inter2",
				Age:  222,
			},
		}
		if !reflect.DeepEqual(server, expected) {
			t.Errorf("Bad unmarshal: expected %v, got %v", expected, server)
		}
	} else {
		t.Fatalf("unexpected error: %v", err)
	}
}

func TestMarshalInterface(t *testing.T) {
	type InnerStruct struct {
		InnerField string
	}

	type OuterStruct struct {
		PrimitiveField        interface{}
		ArrayField            interface{}
		StructArrayField      interface{}
		MapField              map[string]interface{}
		StructField           interface{}
		PointerField          interface{}
		NilField              interface{}
		InterfacePointerField *interface{}
	}

	expected := []byte(`ArrayField = [1, 2, 3]
InterfacePointerField = "hello world"
PrimitiveField = "string"

[MapField]
  key1 = "value1"
  key2 = false

  [MapField.key3]
    InnerField = "value3"

[PointerField]
  InnerField = "yyy"

[[StructArrayField]]
  InnerField = "s1"

[[StructArrayField]]
  InnerField = "s2"

[StructField]
  InnerField = "xxx"
`)

	var h interface{} = "hello world"
	if result, err := Marshal(OuterStruct{
		"string",
		[]int{1, 2, 3},
		[]InnerStruct{{"s1"}, {"s2"}},
		map[string]interface{}{
			"key1":      "value1",
			"key2":      false,
			"key3":      InnerStruct{"value3"},
			"nil value": nil,
		},
		InnerStruct{
			"xxx",
		},
		&InnerStruct{
			"yyy",
		},
		nil,
		&h,
	}); err == nil {
		if !bytes.Equal(result, expected) {
			t.Errorf("Bad marshal: expected\n----\n%s\n----\ngot\n----\n%s\n----\n", expected, result)
		}
	} else {
		t.Fatal(err)
	}
}

func TestUnmarshalToNilInterface(t *testing.T) {
	toml := []byte(`
PrimitiveField = "Hello"
ArrayField = [1,2,3]
InterfacePointerField = "World"

[StructField]
Field1 = 123
Field2 = "Field2"

[MapField]
MapField1 = [4,5,6]
MapField2 = {A = "A"}
MapField3 = false

[[StructArrayField]]
Name = "Allen"
Age = 20

[[StructArrayField]]
Name = "Jack"
Age = 23
`)

	type OuterStruct struct {
		PrimitiveField        interface{}
		ArrayField            interface{}
		StructArrayField      interface{}
		MapField              map[string]interface{}
		StructField           interface{}
		NilField              interface{}
		InterfacePointerField *interface{}
	}

	var s interface{} = "World"
	expected := OuterStruct{
		PrimitiveField: "Hello",
		ArrayField:     []interface{}{int64(1), int64(2), int64(3)},
		StructField: map[string]interface{}{
			"Field1": int64(123),
			"Field2": "Field2",
		},
		MapField: map[string]interface{}{
			"MapField1": []interface{}{int64(4), int64(5), int64(6)},
			"MapField2": map[string]interface{}{
				"A": "A",
			},
			"MapField3": false,
		},
		NilField:              nil,
		InterfacePointerField: &s,
		StructArrayField: []map[string]interface{}{
			{
				"Name": "Allen",
				"Age":  int64(20),
			},
			{
				"Name": "Jack",
				"Age":  int64(23),
			},
		},
	}
	actual := OuterStruct{}
	if err := Unmarshal(toml, &actual); err == nil {
		if !reflect.DeepEqual(actual, expected) {
			t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
		}
	} else {
		t.Fatal(err)
	}
}

func TestUnmarshalToNonNilInterface(t *testing.T) {
	toml := []byte(`
PrimitiveField = "Allen"
ArrayField = [1,2,3]

[StructField]
InnerField = "After1"

[PointerField]
InnerField = "After2"

[InterfacePointerField]
InnerField = "After"

[MapField]
MapField1 = [4,5,6]
MapField2 = {A = "A"}
MapField3 = false

[[StructArrayField]]
InnerField = "After3"

[[StructArrayField]]
InnerField = "After4"
`)
	type InnerStruct struct {
		InnerField interface{}
	}

	type OuterStruct struct {
		PrimitiveField        interface{}
		ArrayField            interface{}
		StructArrayField      interface{}
		MapField              map[string]interface{}
		StructField           interface{}
		PointerField          interface{}
		NilField              interface{}
		InterfacePointerField *interface{}
	}

	var s interface{} = InnerStruct{"After"}
	expected := OuterStruct{
		PrimitiveField: "Allen",
		ArrayField:     []int{1, 2, 3},
		StructField:    InnerStruct{InnerField: "After1"},
		MapField: map[string]interface{}{
			"MapField1": []interface{}{int64(4), int64(5), int64(6)},
			"MapField2": map[string]interface{}{
				"A": "A",
			},
			"MapField3": false,
		},
		PointerField:          &InnerStruct{InnerField: "After2"},
		NilField:              nil,
		InterfacePointerField: &s,
		StructArrayField: []InnerStruct{
			{InnerField: "After3"},
			{InnerField: "After4"},
		},
	}
	actual := OuterStruct{
		PrimitiveField: "aaa",
		ArrayField:     []int{100, 200, 300, 400},
		StructField:    InnerStruct{InnerField: "Before1"},
		MapField: map[string]interface{}{
			"MapField1": []int{4, 5, 6},
			"MapField2": map[string]string{
				"B": "BBB",
			},
			"MapField3": true,
		},
		PointerField:          &InnerStruct{InnerField: "Before2"},
		NilField:              nil,
		InterfacePointerField: &s,
		StructArrayField: []InnerStruct{
			{InnerField: "Before3"},
			{InnerField: "Before4"},
		},
	}
	if err := Unmarshal(toml, &actual); err == nil {
		if !reflect.DeepEqual(actual, expected) {
			t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
		}
	} else {
		t.Fatal(err)
	}
}

func TestUnmarshalEmbedTree(t *testing.T) {
	toml := []byte(`
OuterField1 = "Out"
OuterField2 = 1024

[TreeField]
InnerField1 = "In"
InnerField2 = 2048

	[TreeField.EmbedStruct]
		EmbedField = "Embed"

`)
	type InnerStruct struct {
		InnerField1 string
		InnerField2 int
		EmbedStruct struct {
			EmbedField string
		}
	}

	type OuterStruct struct {
		OuterField1 string
		OuterField2 int
		TreeField   *Tree
	}

	out := OuterStruct{}
	actual := InnerStruct{}
	expected := InnerStruct{
		"In",
		2048,
		struct {
			EmbedField string
		}{
			EmbedField: "Embed",
		},
	}
	if err := Unmarshal(toml, &out); err != nil {
		t.Fatal(err)
	}
	if err := out.TreeField.Unmarshal(&actual); err != nil {
		t.Fatal(err)
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
	}
}

func TestMarshalNil(t *testing.T) {
	if _, err := Marshal(nil); err == nil {
		t.Errorf("Expected err from nil marshal")
	}
	if _, err := Marshal((*struct{})(nil)); err == nil {
		t.Errorf("Expected err from nil marshal")
	}
}

func TestUnmarshalNil(t *testing.T) {
	if err := Unmarshal([]byte(`whatever = "whatever"`), nil); err == nil {
		t.Errorf("Expected err from nil marshal")
	}
	if err := Unmarshal([]byte(`whatever = "whatever"`), (*struct{})(nil)); err == nil {
		t.Errorf("Expected err from nil marshal")
	}
}

var sliceTomlDemo = []byte(`str_slice = ["Howdy","Hey There"]
str_slice_ptr= ["Howdy","Hey There"]
int_slice=[1,2]
int_slice_ptr=[1,2]
[[struct_slice]]
String2="1"
[[struct_slice]]
String2="2"
[[struct_slice_ptr]]
String2="1"
[[struct_slice_ptr]]
String2="2"
`)

type sliceStruct struct {
	Slice          []string                     `  toml:"str_slice"  `
	SlicePtr       *[]string                    `  toml:"str_slice_ptr"  `
	IntSlice       []int                        `  toml:"int_slice"  `
	IntSlicePtr    *[]int                       `  toml:"int_slice_ptr"  `
	StructSlice    []basicMarshalTestSubStruct  `  toml:"struct_slice"  `
	StructSlicePtr *[]basicMarshalTestSubStruct `  toml:"struct_slice_ptr"  `
}

type arrayStruct struct {
	Slice          [4]string                     `  toml:"str_slice"  `
	SlicePtr       *[4]string                    `  toml:"str_slice_ptr"  `
	IntSlice       [4]int                        `  toml:"int_slice"  `
	IntSlicePtr    *[4]int                       `  toml:"int_slice_ptr"  `
	StructSlice    [4]basicMarshalTestSubStruct  `  toml:"struct_slice"  `
	StructSlicePtr *[4]basicMarshalTestSubStruct `  toml:"struct_slice_ptr"  `
}

type arrayTooSmallStruct struct {
	Slice       [1]string                    `  toml:"str_slice"  `
	StructSlice [1]basicMarshalTestSubStruct `  toml:"struct_slice"  `
}

func TestUnmarshalSlice(t *testing.T) {
	tree, _ := LoadBytes(sliceTomlDemo)
	tree, _ = TreeFromMap(tree.ToMap())

	var actual sliceStruct
	err := tree.Unmarshal(&actual)
	if err != nil {
		t.Error("shound not err", err)
	}
	expected := sliceStruct{
		Slice:          []string{"Howdy", "Hey There"},
		SlicePtr:       &[]string{"Howdy", "Hey There"},
		IntSlice:       []int{1, 2},
		IntSlicePtr:    &[]int{1, 2},
		StructSlice:    []basicMarshalTestSubStruct{{"1"}, {"2"}},
		StructSlicePtr: &[]basicMarshalTestSubStruct{{"1"}, {"2"}},
	}
	if !reflect.DeepEqual(actual, expected) {
		t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual)
	}

}

func TestUnmarshalSliceFail(t *testing.T) {
	tree, _ := TreeFromMap(map[string]interface{}{
		"str_slice": []int{1, 2},
	})

	var actual sliceStruct
	err := tree.Unmarshal(&actual)
	if err.Error() != "(0, 0): Can't convert 1(int64) to string" {
		t.Error("expect err:(0, 0): Can't convert 1(int64) to string but got ", err)
	}
}

func TestUnmarshalSliceFail2(t *testing.T) {
	tree, _ := Load(`str_slice=[1,2]`)

	var actual sliceStruct
	err := tree.Unmarshal(&actual)
	if err.Error() != "(1, 1): Can't convert 1(int64) to string" {
		t.Error("expect err:(1, 1): Can't convert 1(int64) to string but got ", err)
	}

}

func TestMarshalMixedTypeArray(t *testing.T) {
	type InnerStruct struct {
		IntField int
		StrField string
	}

	type TestStruct struct {
		ArrayField []interface{}
	}

	expected := []byte(`ArrayField = [3.14, 100, true, "hello world", { IntField = 100, StrField = "inner1" }, [{ IntField = 200, StrField = "inner2" }, { IntField = 300, StrField = "inner3" }]]
`)

	if result, err := Marshal(TestStruct{
		ArrayField: []interface{}{
			3.14,
			100,
			true,
			"hello world",
			InnerStruct{
				IntField: 100,
				StrField: "inner1",
			},
			[]InnerStruct{
				{IntField: 200, StrField: "inner2"},
				{IntField: 300, StrField: "inner3"},
			},
		},
	}); err == nil {
		if !bytes.Equal(result, expected) {
			t.Errorf("Bad marshal: expected\n----\n%s\n----\ngot\n----\n%s\n----\n", expected, result)
		}
	} else {
		t.Fatal(err)
	}
}

func TestUnmarshalMixedTypeArray(t *testing.T) {
	type TestStruct struct {
		ArrayField []interface{}
	}

	toml := []byte(`ArrayField = [3.14,100,true,"hello world",{Field = "inner1"},[{Field = "inner2"},{Field = "inner3"}]]
`)

	actual := TestStruct{}
	expected := TestStruct{
		ArrayField: []interface{}{
			3.14,
			int64(100),
			true,
			"hello world",
			map[string]interface{}{
				"Field": "inner1",
			},
			[]map[string]interface{}{
				{"Field": "inner2"},
				{"Field": "inner3"},
			},
		},
	}

	if err := Unmarshal(toml, &actual); err == nil {
		if !reflect.DeepEqual(actual, expected) {
			t.Errorf("Bad unmarshal: expected %#v, got %#v", expected, actual)
		}
	} else {
		t.Fatal(err)
	}
}

func TestUnmarshalArray(t *testing.T) {
	var tree *Tree
	var err error

	tree, _ = LoadBytes(sliceTomlDemo)
	var actual1 arrayStruct
	err = tree.Unmarshal(&actual1)
	if err != nil {
		t.Error("shound not err", err)
	}

	tree, _ = TreeFromMap(tree.ToMap())
	var actual2 arrayStruct
	err = tree.Unmarshal(&actual2)
	if err != nil {
		t.Error("shound not err", err)
	}

	expected := arrayStruct{
		Slice:          [4]string{"Howdy", "Hey There"},
		SlicePtr:       &[4]string{"Howdy", "Hey There"},
		IntSlice:       [4]int{1, 2},
		IntSlicePtr:    &[4]int{1, 2},
		StructSlice:    [4]basicMarshalTestSubStruct{{"1"}, {"2"}},
		StructSlicePtr: &[4]basicMarshalTestSubStruct{{"1"}, {"2"}},
	}
	if !reflect.DeepEqual(actual1, expected) {
		t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual1)
	}
	if !reflect.DeepEqual(actual2, expected) {
		t.Errorf("Bad unmarshal: expected %v, got %v", expected, actual2)
	}
}

func TestUnmarshalArrayFail(t *testing.T) {
	tree, _ := TreeFromMap(map[string]interface{}{
		"str_slice": []string{"Howdy", "Hey There"},
	})

	var actual arrayTooSmallStruct
	err := tree.Unmarshal(&actual)
	if err.Error() != "(0, 0): unmarshal: TOML array length (2) exceeds destination array length (1)" {
		t.Error("expect err:(0, 0): unmarshal: TOML array length (2) exceeds destination array length (1) but got ", err)
	}
}

func TestUnmarshalArrayFail2(t *testing.T) {
	tree, _ := Load(`str_slice=["Howdy","Hey There"]`)

	var actual arrayTooSmallStruct
	err := tree.Unmarshal(&actual)
	if err.Error() != "(1, 1): unmarshal: TOML array length (2) exceeds destination array length (1)" {
		t.Error("expect err:(1, 1): unmarshal: TOML array length (2) exceeds destination array length (1) but got ", err)
	}
}

func TestUnmarshalArrayFail3(t *testing.T) {
	tree, _ := Load(`[[struct_slice]]
String2="1"
[[struct_slice]]
String2="2"`)

	var actual arrayTooSmallStruct
	err := tree.Unmarshal(&actual)
	if err.Error() != "(3, 1): unmarshal: TOML array length (2) exceeds destination array length (1)" {
		t.Error("expect err:(3, 1): unmarshal: TOML array length (2) exceeds destination array length (1) but got ", err)
	}
}

func TestDecoderStrict(t *testing.T) {
	input := `
[decoded]
  key = ""

[undecoded]
  key = ""

  [undecoded.inner]
	key = ""

  [[undecoded.array]]
	key = ""

  [[undecoded.array]]
	key = ""

`
	var doc struct {
		Decoded struct {
			Key string
		}
	}

	expected := `undecoded keys: ["undecoded.array.0.key" "undecoded.array.1.key" "undecoded.inner.key" "undecoded.key"]`

	err := NewDecoder(bytes.NewReader([]byte(input))).Strict(true).Decode(&doc)
	if err == nil {
		t.Error("expected error, got none")
	} else if err.Error() != expected {
		t.Errorf("expect err: %s, got: %s", expected, err.Error())
	}

	if err := NewDecoder(bytes.NewReader([]byte(input))).Decode(&doc); err != nil {
		t.Errorf("unexpected err: %s", err)
	}

	var m map[string]interface{}
	if err := NewDecoder(bytes.NewReader([]byte(input))).Decode(&m); err != nil {
		t.Errorf("unexpected err: %s", err)
	}
}

func TestDecoderStrictValid(t *testing.T) {
	input := `
[decoded]
  key = ""
`
	var doc struct {
		Decoded struct {
			Key string
		}
	}

	err := NewDecoder(bytes.NewReader([]byte(input))).Strict(true).Decode(&doc)
	if err != nil {
		t.Fatal("unexpected error:", err)
	}
}

type docUnmarshalTOML struct {
	Decoded struct {
		Key string
	}
}

func (d *docUnmarshalTOML) UnmarshalTOML(i interface{}) error {
	if iMap, ok := i.(map[string]interface{}); !ok {
		return fmt.Errorf("type assertion error: wants %T, have %T", map[string]interface{}{}, i)
	} else if key, ok := iMap["key"]; !ok {
		return fmt.Errorf("key '%s' not in map", "key")
	} else if keyString, ok := key.(string); !ok {
		return fmt.Errorf("type assertion error: wants %T, have %T", "", key)
	} else {
		d.Decoded.Key = keyString
	}
	return nil
}

func TestDecoderStrictCustomUnmarshal(t *testing.T) {
	input := `key = "ok"`
	var doc docUnmarshalTOML
	err := NewDecoder(bytes.NewReader([]byte(input))).Strict(true).Decode(&doc)
	if err != nil {
		t.Fatal("unexpected error:", err)
	}
	if doc.Decoded.Key != "ok" {
		t.Errorf("Bad unmarshal: expected ok, got %v", doc.Decoded.Key)
	}
}

type parent struct {
	Doc        docUnmarshalTOML
	DocPointer *docUnmarshalTOML
}

func TestCustomUnmarshal(t *testing.T) {
	input := `
[Doc]
    key = "ok1"
[DocPointer]
    key = "ok2"
`

	var d parent
	if err := Unmarshal([]byte(input), &d); err != nil {
		t.Fatalf("unexpected err: %s", err.Error())
	}
	if d.Doc.Decoded.Key != "ok1" {
		t.Errorf("Bad unmarshal: expected ok, got %v", d.Doc.Decoded.Key)
	}
	if d.DocPointer.Decoded.Key != "ok2" {
		t.Errorf("Bad unmarshal: expected ok, got %v", d.DocPointer.Decoded.Key)
	}
}

func TestCustomUnmarshalError(t *testing.T) {
	input := `
[Doc]
    key = 1
[DocPointer]
    key = "ok2"
`

	expected := "(2, 1): unmarshal toml: type assertion error: wants string, have int64"

	var d parent
	err := Unmarshal([]byte(input), &d)
	if err == nil {
		t.Error("expected error, got none")
	} else if err.Error() != expected {
		t.Errorf("expect err: %s, got: %s", expected, err.Error())
	}
}

type intWrapper struct {
	Value int
}

func (w *intWrapper) UnmarshalText(text []byte) error {
	var err error
	if w.Value, err = strconv.Atoi(string(text)); err == nil {
		return nil
	}
	if b, err := strconv.ParseBool(string(text)); err == nil {
		if b {
			w.Value = 1
		}
		return nil
	}
	if f, err := strconv.ParseFloat(string(text), 32); err == nil {
		w.Value = int(f)
		return nil
	}
	return fmt.Errorf("unsupported: %s", text)
}

func TestTextUnmarshal(t *testing.T) {
	var doc struct {
		UnixTime intWrapper
		Version  *intWrapper

		Bool  intWrapper
		Int   intWrapper
		Float intWrapper
	}

	input := `
UnixTime = "12"
Version = "42"
Bool = true
Int = 21
Float = 2.0
`

	if err := Unmarshal([]byte(input), &doc); err != nil {
		t.Fatalf("unexpected err: %s", err.Error())
	}
	if doc.UnixTime.Value != 12 {
		t.Fatalf("expected UnixTime: 12 got: %d", doc.UnixTime.Value)
	}
	if doc.Version.Value != 42 {
		t.Fatalf("expected Version: 42 got: %d", doc.Version.Value)
	}
	if doc.Bool.Value != 1 {
		t.Fatalf("expected Bool: 1 got: %d", doc.Bool.Value)
	}
	if doc.Int.Value != 21 {
		t.Fatalf("expected Int: 21 got: %d", doc.Int.Value)
	}
	if doc.Float.Value != 2 {
		t.Fatalf("expected Float: 2 got: %d", doc.Float.Value)
	}
}

func TestTextUnmarshalError(t *testing.T) {
	var doc struct {
		Failer intWrapper
	}

	input := `Failer = "hello"`
	if err := Unmarshal([]byte(input), &doc); err == nil {
		t.Fatalf("expected err, got none")
	}
}

// issue406
func TestPreserveNotEmptyField(t *testing.T) {
	toml := []byte(`Field1 = "ccc"`)
	type Inner struct {
		InnerField1 string
		InnerField2 int
	}
	type TestStruct struct {
		Field1 string
		Field2 int
		Field3 Inner
	}

	actual := TestStruct{
		"aaa",
		100,
		Inner{
			"bbb",
			200,
		},
	}

	expected := TestStruct{
		"ccc",
		100,
		Inner{
			"bbb",
			200,
		},
	}

	err := Unmarshal(toml, &actual)
	if err != nil {
		t.Fatal(err)
	}

	if !reflect.DeepEqual(actual, expected) {
		t.Errorf("Bad unmarshal: expected %+v, got %+v", expected, actual)
	}
}

// github issue 432
func TestUnmarshalEmptyInterface(t *testing.T) {
	doc := []byte(`User = "pelletier"`)

	var v interface{}

	err := Unmarshal(doc, &v)
	if err != nil {
		t.Fatal(err)
	}

	x, ok := v.(map[string]interface{})
	if !ok {
		t.Fatal(err)
	}

	if x["User"] != "pelletier" {
		t.Fatalf("expected User=pelletier, but got %v", x)
	}
}

func TestUnmarshalEmptyInterfaceDeep(t *testing.T) {
	doc := []byte(`
User = "pelletier"
Age = 99

[foo]
bar = 42
`)

	var v interface{}

	err := Unmarshal(doc, &v)
	if err != nil {
		t.Fatal(err)
	}

	x, ok := v.(map[string]interface{})
	if !ok {
		t.Fatal(err)
	}

	expected := map[string]interface{}{
		"User": "pelletier",
		"Age":  99,
		"foo": map[string]interface{}{
			"bar": 42,
		},
	}

	reflect.DeepEqual(x, expected)
}

type Config struct {
	Key string `toml:"key"`
	Obj Custom `toml:"obj"`
}

type Custom struct {
	v string
}

func (c *Custom) UnmarshalTOML(v interface{}) error {
	c.v = "called"
	return nil
}

func TestGithubIssue431(t *testing.T) {
	doc := `key = "value"`
	tree, err := LoadBytes([]byte(doc))
	if err != nil {
		t.Fatalf("unexpected error: %s", err)
	}

	var c Config
	if err := tree.Unmarshal(&c); err != nil {
		t.Fatalf("unexpected error: %s", err)
	}

	if c.Key != "value" {
		t.Errorf("expected c.Key='value', not '%s'", c.Key)
	}

	if c.Obj.v == "called" {
		t.Errorf("UnmarshalTOML should not have been called")
	}
}
