// Copyright 2014 Oleku Konko All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// This module is a Table Writer  API for the Go Programming Language.
// The protocols were written in pure Go and works on windows and unix systems

package tablewriter

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"reflect"
	"strings"
	"testing"
)

func checkEqual(t *testing.T, got, want interface{}, msgs ...interface{}) {
	if !reflect.DeepEqual(got, want) {
		buf := bytes.Buffer{}
		buf.WriteString("got:\n[%v]\nwant:\n[%v]\n")
		for _, v := range msgs {
			buf.WriteString(v.(string))
		}
		t.Errorf(buf.String(), got, want)
	}
}

func ExampleShort() {
	data := [][]string{
		{"A", "The Good", "500"},
		{"B", "The Very very Bad Man", "288"},
		{"C", "The Ugly", "120"},
		{"D", "The Gopher", "800"},
	}

	table := NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "Sign", "Rating"})

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	// Output: +------+-----------------------+--------+
	// | NAME |         SIGN          | RATING |
	// +------+-----------------------+--------+
	// | A    | The Good              |    500 |
	// | B    | The Very very Bad Man |    288 |
	// | C    | The Ugly              |    120 |
	// | D    | The Gopher            |    800 |
	// +------+-----------------------+--------+
}

func ExampleLong() {
	data := [][]string{
		{"Learn East has computers with adapted keyboards with enlarged print etc", "  Some Data  ", " Another Data"},
		{"Instead of lining up the letters all ", "the way across, he splits the keyboard in two", "Like most ergonomic keyboards", "See Data"},
	}

	table := NewWriter(os.Stdout)
	table.SetHeader([]string{"Name", "Sign", "Rating"})
	table.SetCenterSeparator("*")
	table.SetRowSeparator("=")

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	// Output: *================================*================================*===============================*==========*
	// |              NAME              |              SIGN              |            RATING             |          |
	// *================================*================================*===============================*==========*
	// | Learn East has computers       |   Some Data                    |  Another Data                 |
	// | with adapted keyboards with    |                                |                               |
	// | enlarged print etc             |                                |                               |
	// | Instead of lining up the       | the way across, he splits the  | Like most ergonomic keyboards | See Data |
	// | letters all                    | keyboard in two                |                               |          |
	// *================================*================================*===============================*==========*
}

func ExampleCSV() {
	table, _ := NewCSV(os.Stdout, "testdata/test.csv", true)
	table.SetCenterSeparator("*")
	table.SetRowSeparator("=")

	table.Render()

	// Output: *============*===========*=========*
	// | FIRST NAME | LAST NAME |   SSN   |
	// *============*===========*=========*
	// | John       | Barry     |  123456 |
	// | Kathy      | Smith     |  687987 |
	// | Bob        | McCornick | 3979870 |
	// *============*===========*=========*
}

// TestNumLines to test the numbers of lines
func TestNumLines(t *testing.T) {
	data := [][]string{
		{"A", "The Good", "500"},
		{"B", "The Very very Bad Man", "288"},
		{"C", "The Ugly", "120"},
		{"D", "The Gopher", "800"},
	}

	buf := &bytes.Buffer{}
	table := NewWriter(buf)
	table.SetHeader([]string{"Name", "Sign", "Rating"})

	for i, v := range data {
		table.Append(v)
		checkEqual(t, table.NumLines(), i+1, "Number of lines failed")
	}

	checkEqual(t, table.NumLines(), len(data), "Number of lines failed")
}

func TestCSVInfo(t *testing.T) {
	buf := &bytes.Buffer{}
	table, err := NewCSV(buf, "testdata/test_info.csv", true)
	if err != nil {
		t.Error(err)
		return
	}
	table.SetAlignment(ALIGN_LEFT)
	table.SetBorder(false)
	table.Render()

	got := buf.String()
	want := `   FIELD   |     TYPE     | NULL | KEY | DEFAULT |     EXTRA       
-----------+--------------+------+-----+---------+-----------------
  user_id  | smallint(5)  | NO   | PRI | NULL    | auto_increment  
  username | varchar(10)  | NO   |     | NULL    |                 
  password | varchar(100) | NO   |     | NULL    |                 
`
	checkEqual(t, got, want, "CSV info failed")
}

func TestCSVSeparator(t *testing.T) {
	buf := &bytes.Buffer{}
	table, err := NewCSV(buf, "testdata/test.csv", true)
	if err != nil {
		t.Error(err)
		return
	}
	table.SetRowLine(true)
	table.SetCenterSeparator("+")
	table.SetColumnSeparator("|")
	table.SetRowSeparator("-")
	table.SetAlignment(ALIGN_LEFT)
	table.Render()

	want := `+------------+-----------+---------+
| FIRST NAME | LAST NAME |   SSN   |
+------------+-----------+---------+
| John       | Barry     | 123456  |
+------------+-----------+---------+
| Kathy      | Smith     | 687987  |
+------------+-----------+---------+
| Bob        | McCornick | 3979870 |
+------------+-----------+---------+
`

	checkEqual(t, buf.String(), want, "CSV info failed")
}

func TestNoBorder(t *testing.T) {
	data := [][]string{
		{"1/1/2014", "Domain name", "2233", "$10.98"},
		{"1/1/2014", "January Hosting", "2233", "$54.95"},
		{"", "    (empty)\n    (empty)", "", ""},
		{"1/4/2014", "February Hosting", "2233", "$51.00"},
		{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
		{"1/4/2014", "    (Discount)", "2233", "-$1.00"},
	}

	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetAutoWrapText(false)
	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
	table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer
	table.SetBorder(false)                                // Set Border to false
	table.AppendBulk(data)                                // Add Bulk Data
	table.Render()

	want := `    DATE   |       DESCRIPTION        |  CV2  | AMOUNT   
-----------+--------------------------+-------+----------
  1/1/2014 | Domain name              |  2233 | $10.98   
  1/1/2014 | January Hosting          |  2233 | $54.95   
           |     (empty)              |       |          
           |     (empty)              |       |          
  1/4/2014 | February Hosting         |  2233 | $51.00   
  1/4/2014 | February Extra Bandwidth |  2233 | $30.00   
  1/4/2014 |     (Discount)           |  2233 | -$1.00   
-----------+--------------------------+-------+----------
                                        TOTAL | $145.93  
                                      --------+----------
`

	checkEqual(t, buf.String(), want, "border table rendering failed")
}

func TestWithBorder(t *testing.T) {
	data := [][]string{
		{"1/1/2014", "Domain name", "2233", "$10.98"},
		{"1/1/2014", "January Hosting", "2233", "$54.95"},
		{"", "    (empty)\n    (empty)", "", ""},
		{"1/4/2014", "February Hosting", "2233", "$51.00"},
		{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
		{"1/4/2014", "    (Discount)", "2233", "-$1.00"},
	}

	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetAutoWrapText(false)
	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
	table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer
	table.AppendBulk(data)                                // Add Bulk Data
	table.Render()

	want := `+----------+--------------------------+-------+---------+
|   DATE   |       DESCRIPTION        |  CV2  | AMOUNT  |
+----------+--------------------------+-------+---------+
| 1/1/2014 | Domain name              |  2233 | $10.98  |
| 1/1/2014 | January Hosting          |  2233 | $54.95  |
|          |     (empty)              |       |         |
|          |     (empty)              |       |         |
| 1/4/2014 | February Hosting         |  2233 | $51.00  |
| 1/4/2014 | February Extra Bandwidth |  2233 | $30.00  |
| 1/4/2014 |     (Discount)           |  2233 | -$1.00  |
+----------+--------------------------+-------+---------+
|                                       TOTAL | $145.93 |
+----------+--------------------------+-------+---------+
`

	checkEqual(t, buf.String(), want, "border table rendering failed")
}

func TestPrintingInMarkdown(t *testing.T) {
	data := [][]string{
		{"1/1/2014", "Domain name", "2233", "$10.98"},
		{"1/1/2014", "January Hosting", "2233", "$54.95"},
		{"1/4/2014", "February Hosting", "2233", "$51.00"},
		{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
	}

	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
	table.AppendBulk(data) // Add Bulk Data
	table.SetBorders(Border{Left: true, Top: false, Right: true, Bottom: false})
	table.SetCenterSeparator("|")
	table.Render()

	want := `|   DATE   |       DESCRIPTION        | CV2  | AMOUNT |
|----------|--------------------------|------|--------|
| 1/1/2014 | Domain name              | 2233 | $10.98 |
| 1/1/2014 | January Hosting          | 2233 | $54.95 |
| 1/4/2014 | February Hosting         | 2233 | $51.00 |
| 1/4/2014 | February Extra Bandwidth | 2233 | $30.00 |
`
	checkEqual(t, buf.String(), want, "border table rendering failed")
}

func TestPrintHeading(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.printHeading()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	checkEqual(t, buf.String(), want, "header rendering failed")
}

func TestPrintHeadingWithoutAutoFormat(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.SetAutoFormatHeaders(false)
	table.printHeading()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	checkEqual(t, buf.String(), want, "header rendering failed")
}

func TestPrintFooter(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.printFooter()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	checkEqual(t, buf.String(), want, "footer rendering failed")
}

func TestPrintFooterWithoutAutoFormat(t *testing.T) {
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetAutoFormatHeaders(false)
	table.SetHeader([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.SetFooter([]string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c"})
	table.printFooter()
	want := `| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | a | b | c |
+---+---+---+---+---+---+---+---+---+---+---+---+
`
	checkEqual(t, buf.String(), want, "footer rendering failed")
}

func TestPrintShortCaption(t *testing.T) {
	var buf bytes.Buffer
	data := [][]string{
		{"A", "The Good", "500"},
		{"B", "The Very very Bad Man", "288"},
		{"C", "The Ugly", "120"},
		{"D", "The Gopher", "800"},
	}

	table := NewWriter(&buf)
	table.SetHeader([]string{"Name", "Sign", "Rating"})
	table.SetCaption(true, "Short caption.")

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	want := `+------+-----------------------+--------+
| NAME |         SIGN          | RATING |
+------+-----------------------+--------+
| A    | The Good              |    500 |
| B    | The Very very Bad Man |    288 |
| C    | The Ugly              |    120 |
| D    | The Gopher            |    800 |
+------+-----------------------+--------+
Short caption.
`
	checkEqual(t, buf.String(), want, "long caption for short example rendering failed")
}

func TestPrintLongCaptionWithShortExample(t *testing.T) {
	var buf bytes.Buffer
	data := [][]string{
		{"A", "The Good", "500"},
		{"B", "The Very very Bad Man", "288"},
		{"C", "The Ugly", "120"},
		{"D", "The Gopher", "800"},
	}

	table := NewWriter(&buf)
	table.SetHeader([]string{"Name", "Sign", "Rating"})
	table.SetCaption(true, "This is a very long caption. The text should wrap. If not, we have a problem that needs to be solved.")

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	want := `+------+-----------------------+--------+
| NAME |         SIGN          | RATING |
+------+-----------------------+--------+
| A    | The Good              |    500 |
| B    | The Very very Bad Man |    288 |
| C    | The Ugly              |    120 |
| D    | The Gopher            |    800 |
+------+-----------------------+--------+
This is a very long caption. The text
should wrap. If not, we have a problem
that needs to be solved.
`
	checkEqual(t, buf.String(), want, "long caption for short example rendering failed")
}

func TestPrintCaptionWithFooter(t *testing.T) {
	data := [][]string{
		{"1/1/2014", "Domain name", "2233", "$10.98"},
		{"1/1/2014", "January Hosting", "2233", "$54.95"},
		{"1/4/2014", "February Hosting", "2233", "$51.00"},
		{"1/4/2014", "February Extra Bandwidth", "2233", "$30.00"},
	}

	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
	table.SetFooter([]string{"", "", "Total", "$146.93"})                                                  // Add Footer
	table.SetCaption(true, "This is a very long caption. The text should wrap to the width of the table.") // Add caption
	table.SetBorder(false)                                                                                 // Set Border to false
	table.AppendBulk(data)                                                                                 // Add Bulk Data
	table.Render()

	want := `    DATE   |       DESCRIPTION        |  CV2  | AMOUNT   
-----------+--------------------------+-------+----------
  1/1/2014 | Domain name              |  2233 | $10.98   
  1/1/2014 | January Hosting          |  2233 | $54.95   
  1/4/2014 | February Hosting         |  2233 | $51.00   
  1/4/2014 | February Extra Bandwidth |  2233 | $30.00   
-----------+--------------------------+-------+----------
                                        TOTAL | $146.93  
                                      --------+----------
This is a very long caption. The text should wrap to the
width of the table.
`
	checkEqual(t, buf.String(), want, "border table rendering failed")
}

func TestPrintLongCaptionWithLongExample(t *testing.T) {
	var buf bytes.Buffer
	data := [][]string{
		{"Learn East has computers with adapted keyboards with enlarged print etc", "Some Data", "Another Data"},
		{"Instead of lining up the letters all", "the way across, he splits the keyboard in two", "Like most ergonomic keyboards"},
	}

	table := NewWriter(&buf)
	table.SetCaption(true, "This is a very long caption. The text should wrap. If not, we have a problem that needs to be solved.")
	table.SetHeader([]string{"Name", "Sign", "Rating"})

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	want := `+--------------------------------+--------------------------------+-------------------------------+
|              NAME              |              SIGN              |            RATING             |
+--------------------------------+--------------------------------+-------------------------------+
| Learn East has computers       | Some Data                      | Another Data                  |
| with adapted keyboards with    |                                |                               |
| enlarged print etc             |                                |                               |
| Instead of lining up the       | the way across, he splits the  | Like most ergonomic keyboards |
| letters all                    | keyboard in two                |                               |
+--------------------------------+--------------------------------+-------------------------------+
This is a very long caption. The text should wrap. If not, we have a problem that needs to be
solved.
`
	checkEqual(t, buf.String(), want, "long caption for long example rendering failed")
}

func Example_autowrap() {
	var multiline = `A multiline
string with some lines being really long.`

	const (
		testRow = iota
		testHeader
		testFooter
		testFooter2
	)
	for mode := testRow; mode <= testFooter2; mode++ {
		for _, autoFmt := range []bool{false, true} {
			if mode == testRow && autoFmt {
				// Nothing special to test, skip
				continue
			}
			for _, autoWrap := range []bool{false, true} {
				for _, reflow := range []bool{false, true} {
					if !autoWrap && reflow {
						// Invalid configuration, skip
						continue
					}
					fmt.Println("mode", mode, "autoFmt", autoFmt, "autoWrap", autoWrap, "reflow", reflow)
					t := NewWriter(os.Stdout)
					t.SetAutoFormatHeaders(autoFmt)
					t.SetAutoWrapText(autoWrap)
					t.SetReflowDuringAutoWrap(reflow)
					if mode == testHeader {
						t.SetHeader([]string{"woo", multiline})
					} else {
						t.SetHeader([]string{"woo", "waa"})
					}
					if mode == testRow {
						t.Append([]string{"woo", multiline})
					} else {
						t.Append([]string{"woo", "waa"})
					}
					if mode == testFooter {
						t.SetFooter([]string{"woo", multiline})
					} else if mode == testFooter2 {
						t.SetFooter([]string{"", multiline})
					} else {
						t.SetFooter([]string{"woo", "waa"})
					}
					t.Render()
				}
			}
		}
		fmt.Println()
	}

	// Output:
	// mode 0 autoFmt false autoWrap false reflow false
	// +-----+-------------------------------------------+
	// | woo |                    waa                    |
	// +-----+-------------------------------------------+
	// | woo | A multiline                               |
	// |     | string with some lines being really long. |
	// +-----+-------------------------------------------+
	// | woo |                    waa                    |
	// +-----+-------------------------------------------+
	// mode 0 autoFmt false autoWrap true reflow false
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	// | woo | A multiline                    |
	// |     |                                |
	// |     | string with some lines being   |
	// |     | really long.                   |
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	// mode 0 autoFmt false autoWrap true reflow true
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	// | woo | A multiline string with some   |
	// |     | lines being really long.       |
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	//
	// mode 1 autoFmt false autoWrap false reflow false
	// +-----+-------------------------------------------+
	// | woo |                A multiline                |
	// |     | string with some lines being really long. |
	// +-----+-------------------------------------------+
	// | woo | waa                                       |
	// +-----+-------------------------------------------+
	// | woo |                    waa                    |
	// +-----+-------------------------------------------+
	// mode 1 autoFmt false autoWrap true reflow false
	// +-----+--------------------------------+
	// | woo |          A multiline           |
	// |     |                                |
	// |     |  string with some lines being  |
	// |     |          really long.          |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	// mode 1 autoFmt false autoWrap true reflow true
	// +-----+--------------------------------+
	// | woo |  A multiline string with some  |
	// |     |    lines being really long.    |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	// mode 1 autoFmt true autoWrap false reflow false
	// +-----+-------------------------------------------+
	// | WOO |                A MULTILINE                |
	// |     | STRING WITH SOME LINES BEING REALLY LONG  |
	// +-----+-------------------------------------------+
	// | woo | waa                                       |
	// +-----+-------------------------------------------+
	// | WOO |                    WAA                    |
	// +-----+-------------------------------------------+
	// mode 1 autoFmt true autoWrap true reflow false
	// +-----+--------------------------------+
	// | WOO |          A MULTILINE           |
	// |     |                                |
	// |     |  STRING WITH SOME LINES BEING  |
	// |     |          REALLY LONG           |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// | WOO |              WAA               |
	// +-----+--------------------------------+
	// mode 1 autoFmt true autoWrap true reflow true
	// +-----+--------------------------------+
	// | WOO |  A MULTILINE STRING WITH SOME  |
	// |     |    LINES BEING REALLY LONG     |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// | WOO |              WAA               |
	// +-----+--------------------------------+
	//
	// mode 2 autoFmt false autoWrap false reflow false
	// +-----+-------------------------------------------+
	// | woo |                    waa                    |
	// +-----+-------------------------------------------+
	// | woo | waa                                       |
	// +-----+-------------------------------------------+
	// | woo |                A multiline                |
	// |     | string with some lines being really long. |
	// +-----+-------------------------------------------+
	// mode 2 autoFmt false autoWrap true reflow false
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// | woo |          A multiline           |
	// |     |                                |
	// |     |  string with some lines being  |
	// |     |          really long.          |
	// +-----+--------------------------------+
	// mode 2 autoFmt false autoWrap true reflow true
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// | woo |  A multiline string with some  |
	// |     |    lines being really long.    |
	// +-----+--------------------------------+
	// mode 2 autoFmt true autoWrap false reflow false
	// +-----+-------------------------------------------+
	// | WOO |                    WAA                    |
	// +-----+-------------------------------------------+
	// | woo | waa                                       |
	// +-----+-------------------------------------------+
	// | WOO |                A MULTILINE                |
	// |     | STRING WITH SOME LINES BEING REALLY LONG  |
	// +-----+-------------------------------------------+
	// mode 2 autoFmt true autoWrap true reflow false
	// +-----+--------------------------------+
	// | WOO |              WAA               |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// | WOO |          A MULTILINE           |
	// |     |                                |
	// |     |  STRING WITH SOME LINES BEING  |
	// |     |          REALLY LONG           |
	// +-----+--------------------------------+
	// mode 2 autoFmt true autoWrap true reflow true
	// +-----+--------------------------------+
	// | WOO |              WAA               |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// | WOO |  A MULTILINE STRING WITH SOME  |
	// |     |    LINES BEING REALLY LONG     |
	// +-----+--------------------------------+
	//
	// mode 3 autoFmt false autoWrap false reflow false
	// +-----+-------------------------------------------+
	// | woo |                    waa                    |
	// +-----+-------------------------------------------+
	// | woo | waa                                       |
	// +-----+-------------------------------------------+
	// |                      A multiline                |
	// |       string with some lines being really long. |
	// +-----+-------------------------------------------+
	// mode 3 autoFmt false autoWrap true reflow false
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// |                A multiline           |
	// |                                      |
	// |        string with some lines being  |
	// |                really long.          |
	// +-----+--------------------------------+
	// mode 3 autoFmt false autoWrap true reflow true
	// +-----+--------------------------------+
	// | woo |              waa               |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// |        A multiline string with some  |
	// |          lines being really long.    |
	// +-----+--------------------------------+
	// mode 3 autoFmt true autoWrap false reflow false
	// +-----+-------------------------------------------+
	// | WOO |                    WAA                    |
	// +-----+-------------------------------------------+
	// | woo | waa                                       |
	// +-----+-------------------------------------------+
	// |                      A MULTILINE                |
	// |       STRING WITH SOME LINES BEING REALLY LONG  |
	// +-----+-------------------------------------------+
	// mode 3 autoFmt true autoWrap true reflow false
	// +-----+--------------------------------+
	// | WOO |              WAA               |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// |                A MULTILINE           |
	// |                                      |
	// |        STRING WITH SOME LINES BEING  |
	// |                REALLY LONG           |
	// +-----+--------------------------------+
	// mode 3 autoFmt true autoWrap true reflow true
	// +-----+--------------------------------+
	// | WOO |              WAA               |
	// +-----+--------------------------------+
	// | woo | waa                            |
	// +-----+--------------------------------+
	// |        A MULTILINE STRING WITH SOME  |
	// |          LINES BEING REALLY LONG     |
	// +-----+--------------------------------+
}

func TestPrintLine(t *testing.T) {
	header := make([]string, 12)
	val := " "
	want := ""
	for i := range header {
		header[i] = val
		want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1))
		val = val + " "
	}
	want = want + "+"
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader(header)
	table.printLine(false)
	checkEqual(t, buf.String(), want, "line rendering failed")
}

func TestAnsiStrip(t *testing.T) {
	header := make([]string, 12)
	val := " "
	want := ""
	for i := range header {
		header[i] = "\033[43;30m" + val + "\033[00m"
		want = fmt.Sprintf("%s+-%s-", want, strings.Replace(val, " ", "-", -1))
		val = val + " "
	}
	want = want + "+"
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader(header)
	table.printLine(false)
	checkEqual(t, buf.String(), want, "line rendering failed")
}

func NewCustomizedTable(out io.Writer) *Table {
	table := NewWriter(out)
	table.SetCenterSeparator("")
	table.SetColumnSeparator("")
	table.SetRowSeparator("")
	table.SetBorder(false)
	table.SetAlignment(ALIGN_LEFT)
	table.SetHeader([]string{})
	return table
}

func TestSubclass(t *testing.T) {
	buf := new(bytes.Buffer)
	table := NewCustomizedTable(buf)

	data := [][]string{
		{"A", "The Good", "500"},
		{"B", "The Very very Bad Man", "288"},
		{"C", "The Ugly", "120"},
		{"D", "The Gopher", "800"},
	}

	for _, v := range data {
		table.Append(v)
	}
	table.Render()

	want := `  A  The Good               500  
  B  The Very very Bad Man  288  
  C  The Ugly               120  
  D  The Gopher             800  
`
	checkEqual(t, buf.String(), want, "test subclass failed")
}

func TestAutoMergeRows(t *testing.T) {
	data := [][]string{
		{"A", "The Good", "500"},
		{"A", "The Very very Bad Man", "288"},
		{"B", "The Very very Bad Man", "120"},
		{"B", "The Very very Bad Man", "200"},
	}
	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"Name", "Sign", "Rating"})

	for _, v := range data {
		table.Append(v)
	}
	table.SetAutoMergeCells(true)
	table.Render()
	want := `+------+-----------------------+--------+
| NAME |         SIGN          | RATING |
+------+-----------------------+--------+
| A    | The Good              |    500 |
|      | The Very very Bad Man |    288 |
| B    |                       |    120 |
|      |                       |    200 |
+------+-----------------------+--------+
`
	got := buf.String()
	if got != want {
		t.Errorf("\ngot:\n%s\nwant:\n%s\n", got, want)
	}

	buf.Reset()
	table = NewWriter(&buf)
	table.SetHeader([]string{"Name", "Sign", "Rating"})

	for _, v := range data {
		table.Append(v)
	}
	table.SetAutoMergeCells(true)
	table.SetRowLine(true)
	table.Render()
	want = `+------+-----------------------+--------+
| NAME |         SIGN          | RATING |
+------+-----------------------+--------+
| A    | The Good              |    500 |
+      +-----------------------+--------+
|      | The Very very Bad Man |    288 |
+------+                       +--------+
| B    |                       |    120 |
+      +                       +--------+
|      |                       |    200 |
+------+-----------------------+--------+
`
	checkEqual(t, buf.String(), want)

	buf.Reset()
	table = NewWriter(&buf)
	table.SetHeader([]string{"Name", "Sign", "Rating"})

	dataWithlongText := [][]string{
		{"A", "The Good", "500"},
		{"A", "The Very very very very very Bad Man", "288"},
		{"B", "The Very very very very very Bad Man", "120"},
		{"C", "The Very very Bad Man", "200"},
	}
	table.AppendBulk(dataWithlongText)
	table.SetAutoMergeCells(true)
	table.SetRowLine(true)
	table.Render()
	want = `+------+--------------------------------+--------+
| NAME |              SIGN              | RATING |
+------+--------------------------------+--------+
| A    | The Good                       |    500 |
+      +--------------------------------+--------+
|      | The Very very very very very   |    288 |
|      | Bad Man                        |        |
+------+                                +--------+
| B    |                                |    120 |
|      |                                |        |
+------+--------------------------------+--------+
| C    | The Very very Bad Man          |    200 |
+------+--------------------------------+--------+
`
	checkEqual(t, buf.String(), want)

	buf.Reset()
	table = NewWriter(&buf)
	table.SetHeader([]string{"Name", "Sign", "Rating"})

	dataWithlongText2 := [][]string{
		{"A", "The Good", "500"},
		{"A", "The Very very very very very Bad Man", "288"},
		{"B", "The Very very Bad Man", "120"},
	}
	table.AppendBulk(dataWithlongText2)
	table.SetAutoMergeCells(true)
	table.SetRowLine(true)
	table.Render()
	want = `+------+--------------------------------+--------+
| NAME |              SIGN              | RATING |
+------+--------------------------------+--------+
| A    | The Good                       |    500 |
+      +--------------------------------+--------+
|      | The Very very very very very   |    288 |
|      | Bad Man                        |        |
+------+--------------------------------+--------+
| B    | The Very very Bad Man          |    120 |
+------+--------------------------------+--------+
`
	checkEqual(t, buf.String(), want)
}

func TestClearRows(t *testing.T) {
	data := [][]string{
		{"1/1/2014", "Domain name", "2233", "$10.98"},
	}

	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetAutoWrapText(false)
	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
	table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer
	table.AppendBulk(data)                                // Add Bulk Data
	table.Render()

	originalWant := `+----------+-------------+-------+---------+
|   DATE   | DESCRIPTION |  CV2  | AMOUNT  |
+----------+-------------+-------+---------+
| 1/1/2014 | Domain name |  2233 | $10.98  |
+----------+-------------+-------+---------+
|                          TOTAL | $145.93 |
+----------+-------------+-------+---------+
`
	want := originalWant

	checkEqual(t, buf.String(), want, "table clear rows failed")

	buf.Reset()
	table.ClearRows()
	table.Render()

	want = `+----------+-------------+-------+---------+
|   DATE   | DESCRIPTION |  CV2  | AMOUNT  |
+----------+-------------+-------+---------+
+----------+-------------+-------+---------+
|                          TOTAL | $145.93 |
+----------+-------------+-------+---------+
`

	checkEqual(t, buf.String(), want, "table clear rows failed")

	buf.Reset()
	table.AppendBulk(data) // Add Bulk Data
	table.Render()

	want = `+----------+-------------+-------+---------+
|   DATE   | DESCRIPTION |  CV2  | AMOUNT  |
+----------+-------------+-------+---------+
| 1/1/2014 | Domain name |  2233 | $10.98  |
+----------+-------------+-------+---------+
|                          TOTAL | $145.93 |
+----------+-------------+-------+---------+
`

	checkEqual(t, buf.String(), want, "table clear rows failed")
}

func TestClearFooters(t *testing.T) {
	data := [][]string{
		{"1/1/2014", "Domain name", "2233", "$10.98"},
	}

	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetAutoWrapText(false)
	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
	table.SetFooter([]string{"", "", "Total", "$145.93"}) // Add Footer
	table.AppendBulk(data)                                // Add Bulk Data
	table.Render()

	buf.Reset()
	table.ClearFooter()
	table.Render()

	want := `+----------+-------------+-------+---------+
|   DATE   | DESCRIPTION |  CV2  | AMOUNT  |
+----------+-------------+-------+---------+
| 1/1/2014 | Domain name |  2233 | $10.98  |
+----------+-------------+-------+---------+
`

	checkEqual(t, buf.String(), want)
}

func TestMoreDataColumnsThanHeaders(t *testing.T) {
	var (
		buf    = &bytes.Buffer{}
		table  = NewWriter(buf)
		header = []string{"A", "B", "C"}
		data   = [][]string{
			{"a", "b", "c", "d"},
			{"1", "2", "3", "4"},
		}
		want = `+---+---+---+---+
| A | B | C |   |
+---+---+---+---+
| a | b | c | d |
| 1 | 2 | 3 | 4 |
+---+---+---+---+
`
	)
	table.SetHeader(header)
	// table.SetFooter(ctx.tableCtx.footer)
	table.AppendBulk(data)
	table.Render()

	checkEqual(t, buf.String(), want)
}

func TestMoreFooterColumnsThanHeaders(t *testing.T) {
	var (
		buf    = &bytes.Buffer{}
		table  = NewWriter(buf)
		header = []string{"A", "B", "C"}
		data   = [][]string{
			{"a", "b", "c", "d"},
			{"1", "2", "3", "4"},
		}
		footer = []string{"a", "b", "c", "d", "e"}
		want   = `+---+---+---+---+---+
| A | B | C |   |   |
+---+---+---+---+---+
| a | b | c | d |
| 1 | 2 | 3 | 4 |
+---+---+---+---+---+
| A | B | C | D | E |
+---+---+---+---+---+
`
	)
	table.SetHeader(header)
	table.SetFooter(footer)
	table.AppendBulk(data)
	table.Render()

	checkEqual(t, buf.String(), want)
}

func TestSetColMinWidth(t *testing.T) {
	var (
		buf    = &bytes.Buffer{}
		table  = NewWriter(buf)
		header = []string{"AAA", "BBB", "CCC"}
		data   = [][]string{
			{"a", "b", "c"},
			{"1", "2", "3"},
		}
		footer = []string{"a", "b", "cccc"}
		want   = `+-----+-----+-------+
| AAA | BBB |  CCC  |
+-----+-----+-------+
| a   | b   | c     |
|   1 |   2 |     3 |
+-----+-----+-------+
|  A  |  B  | CCCC  |
+-----+-----+-------+
`
	)
	table.SetHeader(header)
	table.SetFooter(footer)
	table.AppendBulk(data)
	table.SetColMinWidth(2, 5)
	table.Render()

	checkEqual(t, buf.String(), want)
}

func TestWrapString(t *testing.T) {
	want := []string{"ああああああああああああああああああああああああ", "あああああああ"}
	got, _ := WrapString("ああああああああああああああああああああああああ あああああああ", 55)
	checkEqual(t, got, want)
}

func TestNumberAlign(t *testing.T) {
	var (
		buf   = &bytes.Buffer{}
		table = NewWriter(buf)
		data  = [][]string{
			{"AAAAAAAAAAAAA", "BBBBBBBBBBBBB", "CCCCCCCCCCCCCC"},
			{"A", "B", "C"},
			{"123456789", "2", "3"},
			{"1", "2", "123,456,789"},
			{"1", "123,456.789", "3"},
			{"-123,456", "-2", "-3"},
		}
		want = `+---------------+---------------+----------------+
| AAAAAAAAAAAAA | BBBBBBBBBBBBB | CCCCCCCCCCCCCC |
| A             | B             | C              |
|     123456789 |             2 |              3 |
|             1 |             2 |    123,456,789 |
|             1 |   123,456.789 |              3 |
|      -123,456 |            -2 |             -3 |
+---------------+---------------+----------------+
`
	)
	table.AppendBulk(data)
	table.Render()

	checkEqual(t, buf.String(), want)
}

func TestCustomAlign(t *testing.T) {
	var (
		buf    = &bytes.Buffer{}
		table  = NewWriter(buf)
		header = []string{"AAA", "BBB", "CCC"}
		data   = [][]string{
			{"a", "b", "c"},
			{"1", "2", "3"},
		}
		footer = []string{"a", "b", "cccc"}
		want   = `+-----+-----+-------+
| AAA | BBB |  CCC  |
+-----+-----+-------+
| a   |  b  |     c |
| 1   |  2  |     3 |
+-----+-----+-------+
|  A  |  B  | CCCC  |
+-----+-----+-------+
`
	)
	table.SetHeader(header)
	table.SetFooter(footer)
	table.AppendBulk(data)
	table.SetColMinWidth(2, 5)
	table.SetColumnAlignment([]int{ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT})
	table.Render()

	checkEqual(t, buf.String(), want)
}

func TestTitle(t *testing.T) {
	ts := []struct {
		text string
		want string
	}{
		{"", ""},
		{"foo", "FOO"},
		{"Foo", "FOO"},
		{"foO", "FOO"},
		{".foo", "FOO"},
		{"foo.", "FOO"},
		{".foo.", "FOO"},
		{".foo.bar.", "FOO BAR"},
		{"_foo", "FOO"},
		{"foo_", "FOO"},
		{"_foo_", "FOO"},
		{"_foo_bar_", "FOO BAR"},
		{" foo", "FOO"},
		{"foo ", "FOO"},
		{" foo ", "FOO"},
		{" foo bar ", "FOO BAR"},
		{"0.1", "0.1"},
		{"FOO 0.1", "FOO 0.1"},
		{".1 0.1", ".1 0.1"},
		{"1. 0.1", "1. 0.1"},
		{"1. 0.", "1. 0."},
		{".1. 0.", ".1. 0."},
		{".$ . $.", "$ . $"},
		{".$. $.", "$  $"},
	}
	for _, tt := range ts {
		got := Title(tt.text)
		if got != tt.want {
			t.Errorf("want %q, bot got %q", tt.want, got)
		}
	}
}

func TestKubeFormat(t *testing.T) {
	data := [][]string{
		{"1/1/2014", "jan_hosting", "2233", "$10.98"},
		{"1/1/2014", "feb_hosting", "2233", "$54.95"},
		{"1/4/2014", "feb_extra_bandwidth", "2233", "$51.00"},
		{"1/4/2014", "mar_hosting", "2233", "$30.00"},
	}

	var buf bytes.Buffer
	table := NewWriter(&buf)
	table.SetHeader([]string{"Date", "Description", "CV2", "Amount"})
	table.SetAutoWrapText(false)
	table.SetAutoFormatHeaders(true)
	table.SetHeaderAlignment(ALIGN_LEFT)
	table.SetAlignment(ALIGN_LEFT)
	table.SetCenterSeparator("")
	table.SetColumnSeparator("")
	table.SetRowSeparator("")
	table.SetHeaderLine(false)
	table.SetBorder(false)
	table.SetTablePadding("\t") // pad with tabs
	table.SetNoWhiteSpace(true)
	table.AppendBulk(data) // Add Bulk Data
	table.Render()

	want := `DATE    	DESCRIPTION        	CV2 	AMOUNT 
1/1/2014	jan_hosting        	2233	$10.98	
1/1/2014	feb_hosting        	2233	$54.95	
1/4/2014	feb_extra_bandwidth	2233	$51.00	
1/4/2014	mar_hosting        	2233	$30.00	
`

	checkEqual(t, buf.String(), want, "kube format rendering failed")
}
