package yimg

import (
	"bytes"
	"strconv"
	"strings"
	"text/template"

	"gopkg.in/yaml.v2"
)

// Images is a mapping of size to Image
// Size is represented by widthxheight or just height, for example 1920x1080 or 480
type Images map[string]Image

// Image contains the fully qualified URL of an image and any known metadata
type Image struct {
	Width  int    `json:"width,omitempty"`
	Height int    `json:"height"`
	URL    string `json:"url"`
}

type yamlData struct {
	Sizes   []string `yaml:":sizes"`
	Heights []string `yaml:":heights"`
	Ratio   float64  `yaml:":ratio"`
	Uid     string   `yaml:":uid"`
	Format  string   `yaml:":format"`
}

type templateData struct {
	Size   string
	Height string
	Ratio  float64
	Uid    string
	Format string
}

func parseSize(size string) (width int, height int, err error) {
	if !strings.Contains(size, "x") {
		height, err = strconv.Atoi(size)
		return 0, height, err
	}

	dims := strings.Split(size, "x")

	width, err = strconv.Atoi(dims[0])
	if err != nil {
		return 0, 0, err
	}

	height, err = strconv.Atoi(dims[1])
	if err != nil {
		return 0, 0, err
	}

	return width, height, nil
}

func parseYaml(in []byte, defaultSize []string) (yamlData, error) {
	var out yamlData
	err := yaml.Unmarshal(in, &out)
	if err != nil {
		return out, err
	}

	if len(out.Sizes) == 0 {
		if len(out.Heights) == 0 {
			out.Heights = defaultSize
		}
		out.Sizes = out.Heights
	}

	return out, nil
}

func parse(format string, in []byte, defaultSize []string) (Images, error) {
	tmpl, err := template.New(format).Parse(format)
	if err != nil {
		return nil, err
	}

	yaml, err := parseYaml(in, defaultSize)
	if err != nil {
		return nil, err
	}

	images := make(Images)
	for _, size := range yaml.Sizes {
		var buf bytes.Buffer

		err = tmpl.Execute(&buf, templateData{
			Size:   size,
			Ratio:  yaml.Ratio,
			Uid:    yaml.Uid,
			Format: yaml.Format,
		})
		if err != nil {
			return nil, err
		}

		pImg := Image{}

		pImg.Width, pImg.Height, err = parseSize(size)
		if err != nil {
			return nil, err
		}

		pImg.URL = buf.String()

		images[size] = pImg
	}
	return images, nil
}
