package xvdoc

import (
	"bytes"
	"encoding/json"
	"fmt"
)

func FromJSON(src []byte) (res []*XVDoc, err error) {
	var xv []*XVDoc
	if err = json.Unmarshal(src, &xv); err == nil {
		res = xv
	}
	return
}

type XVDoc struct {
	name         string
	style        string
	rows         []*Row
	styles       map[string]Style
	dropdowns    []*dropDown
	merges       []*rectMerge
	columnsWidth []*rectWidths
	styleRects   []*styleRect
	images       []*cellImage
}

func (s *XVDoc) UnmarshalJSON(src []byte) (err error) {
	dec := json.NewDecoder(bytes.NewReader(src))
	var t json.Token
	for dec.More() && err == nil {
		if t, err = dec.Token(); err == nil {
			if _, check := t.(json.Delim); !check {
				switch t.(string) {
				case "name":
					err = dec.Decode(&s.name)
				case "style":
					err = dec.Decode(&s.style)
				case "rows":
					err = dec.Decode(&s.rows)
				case "styles":
					err = dec.Decode(&s.styles)
				case "merges":
					err = dec.Decode(&s.merges)
				case "style_rects":
					err = dec.Decode(&s.styleRects)
				case "columns_width":
					err = dec.Decode(&s.columnsWidth)
				case "images":
					err = dec.Decode(&s.images)
				case "dropdowns":
					err = dec.Decode(&s.dropdowns)
				default:
					err = dec.Decode(new(interface{}))
				}
			}
		}
	}
	return
}

func (s *XVDoc) String() (res string) {
	res = fmt.Sprintf("xvDoc %v\n*** rows ***\n", s.name)
	res += fmt.Sprintf("styles: %v\n", s.styles)
	res += fmt.Sprintf("name: %v\n", s.name)
	for i, v := range s.rows {
		res = res + fmt.Sprintf("%v: Cells: %v\n", i, v)
	}
	res = res + "*********************\n"
	return res
}

func (s *XVDoc) matrix() Matrix {
	var matrix [][]Cell
	row, rowsHeight := 0, make(map[int]float64)

	// create document matrix
	for _, v := range s.rows {

		// setup row
		if v.offsetY {
			row += v.oy
		} else if v.y >= 0 {
			row = v.y
		}

		// setup custom row height
		if v.IsCustomHeight() {
			rowsHeight[row] = v.Height()
		}

		// fill matrix rows
		if row >= len(matrix) {
			tmp := make([][]Cell, row-len(matrix)+1)
			matrix = append(matrix, tmp...)
		}

		// get row in matrix
		r := matrix[row]

		if v.x+len(v.cells) >= len(r) {
			tmp := make([]Cell, v.x+len(v.cells)-len(r))
			r = append(r, tmp...)
		}

		for i := 0; i < len(v.cells); i++ {
			r[v.x+i] = v.cells[i]
		}

		// setup row in matrix
		matrix[row] = r

		// to next row
		row++
	}

	res := Matrix{
		Name:         s.name,
		Cells:        matrix,
		Styles:       s.styles,
		RowsHeight:   rowsHeight,
		Style:        s.style,
		StyleRects:   s.styleRects,
		Merges:       s.merges,
		Images:       s.images,
		Dropdowns:    s.dropdowns,
		ColumnsWidth: s.columnsWidth,
	}

	return res
}

func Export(format string, docs []*XVDoc) ([]byte, error) {
	if method, check := exportMethods[format]; check {
		m := make([]Matrix, len(docs))
		for i, v := range docs {
			m[i] = v.matrix()
		}
		return method(m)
	} else {
		return nil, fmt.Errorf("xvdoc export error :: unexpected format '%v'", format)
	}
}