package jsontable

import (
	"fmt"
	"io"

	"github.com/olekukonko/tablewriter"
)

// A quick-and-dirty proxy/emulator of tablewriter to enable more easy machine readable output
// - Does not strictly support types, just quoted or unquoted values
// - Does not support nested values.
// If a datalabel is set with SetDataLabel(true, "..."), that will be used as the key for teh data of the table,
// otherwise if the caption is set with SetCaption(true, "..."), the data label will be set to the default of
// "rows", otherwise the table will output as a JSON list.
//
// Proxys all actions through to the tablewriter except addrow and addbatch, which it does at render time
//

type JSONTable struct {
	out             io.Writer
	colsize         int
	rows            [][]string
	keys            []string
	quoted          []bool // hack to do output typing, quoted vs. unquoted
	hasDataLabel    bool
	dataLabel       string
	hasCaption      bool
	caption         string // the actual caption
	hasCaptionLabel bool
	captionLabel    string // the key in the dictionary for the caption
	tbl             *tablewriter.Table
}

func writeChar(w io.Writer, c byte) {
	w.Write([]byte{c})

}

func NewJSONTable(writer io.Writer) *JSONTable {
	t := &JSONTable{
		out:             writer,
		colsize:         0,
		rows:            [][]string{},
		keys:            []string{},
		quoted:          []bool{},
		hasDataLabel:    false,
		dataLabel:       "rows",
		hasCaption:      false,
		caption:         "",
		hasCaptionLabel: false,
		captionLabel:    "caption",
		tbl:             tablewriter.NewWriter(writer),
	}
	return t
}

func (t *JSONTable) NumLines() int {
	// JSON only but reflects a shared state.
	return len(t.rows)
}

func (t *JSONTable) SetHeader(keys []string) {
	// Set the keys value which will assign each column to the keys.
	// Note that we'll ignore values that are beyond the length of the keys list
	t.colsize = len(keys)
	t.keys = []string{}
	for _, k := range keys {
		t.keys = append(t.keys, k)
		t.quoted = append(t.quoted, true)
	}
	t.tbl.SetHeader(keys)
}

func (t *JSONTable) SetColumnQuoting(quoting []bool) {
	// Specify which columns are quoted or unquoted in output
	// JSON only
	for i := 0; i < t.colsize; i++ {
		t.quoted[i] = quoting[i]
	}
}

func (t *JSONTable) Append(row []string) {
	// We'll just append whatever to the rows list. If they fix the keys after appending rows, it'll work as
	// expected.
	// We should detect if the row is narrower than the key list tho.
	// JSON only (but we use the rows later when rendering a regular table)
	t.rows = append(t.rows, row)
}

func (t *JSONTable) Render() {
	// Load the table with rows and render.
	// Proxy only
	for _, row := range t.rows {
		t.tbl.Append(row)
	}

	t.tbl.Render()
}

func (t *JSONTable) _JSONRenderInner() {
	// JSON only
	// Render the list of dictionaries to the writer.
	//// inner render loop
	writeChar(t.out, '[')
	for rowidx, row := range t.rows {
		if rowidx != 0 {
			writeChar(t.out, ',')
		}
		writeChar(t.out, '{')
		for keyidx, key := range t.keys {
			value := "nil"
			if keyidx < len(row) {
				value = row[keyidx]
			}
			if keyidx != 0 {
				writeChar(t.out, ',')
			}
			if t.quoted[keyidx] {
				fmt.Fprintf(t.out, "\"%s\":\"%s\"", key, value)
			} else {
				fmt.Fprintf(t.out, "\"%s\":%s", key, value)
			}
		}
		writeChar(t.out, '}')
	}
	writeChar(t.out, ']')

}

func (t *JSONTable) JSONRender() {
	// write JSON table to output
	// JSON only

	if t.hasDataLabel || t.hasCaption {
		// dict mode
		writeChar(t.out, '{')

		if t.hasCaption {
			fmt.Fprintf(t.out, "\"%s\":\"%s\",", t.captionLabel, t.caption)

		}
		fmt.Fprintf(t.out, "\"%s\":", t.dataLabel)

	}

	// write list
	t._JSONRenderInner()

	if t.hasDataLabel || t.hasCaption {
		// dict mode
		writeChar(t.out, '}')
	}

}

func (t *JSONTable) SetCaption(caption bool, captionText ...string) {
	t.hasCaption = caption
	if len(captionText) == 1 {
		t.caption = captionText[0]
	}
	t.tbl.SetCaption(caption, captionText...)
}

func (t *JSONTable) SetCaptionLabel(captionLabel bool, captionLabelText ...string) {
	// JSON only
	t.hasCaptionLabel = captionLabel
	if len(captionLabelText) == 1 {
		t.captionLabel = captionLabelText[0]
	}
}

func (t *JSONTable) SetDataLabel(dataLabel bool, dataLabelText ...string) {
	// JSON only
	t.hasDataLabel = dataLabel
	if len(dataLabelText) == 1 {
		t.dataLabel = dataLabelText[0]
	}
}

func (t *JSONTable) AppendBulk(rows [][]string) {
	// JSON only but reflects shared state
	for _, row := range rows {
		t.Append(row)
	}
}

// Stuff we should implement but we just proxy for now.
func (t *JSONTable) SetAutoMergeCellsByColumnIndex(cols []int) {
	// FIXME
	t.tbl.SetAutoMergeCellsByColumnIndex(cols)
}

func (t *JSONTable) SetAutoMergeCells(auto bool) {
	// FIXME
	t.tbl.SetAutoMergeCells(auto)
}

// Stub functions
func (t *JSONTable) SetAutoWrapText(auto bool) {
	t.tbl.SetAutoWrapText(auto)
	return
}