package yimg

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

	"gopkg.in/yaml.v2"
)

type identifier int

const (
	byHeight identifier = iota
	bySize
)

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

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, newFormat string, in []byte, defaultSize []string, identifier identifier) (Images, error) {
	yaml, err := parseYaml(in, defaultSize)
	if err != nil {
		return nil, err
	}

	f := format
	if yaml.NewFormat == true {
		f = newFormat
	}

	tmpl, err := template.New(f).Parse(f)
	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{
			Size:      size,
			Uid:       yaml.Uid,
			Format:    yaml.Format,
			NewFormat: yaml.NewFormat,
		}

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

		pImg.URL = buf.String()

		switch identifier {
		case bySize:
			images[size] = pImg
		case byHeight:
			images[strconv.Itoa(pImg.Height)] = pImg
		default:
			images[size] = pImg
		}

	}
	return images, nil
}
