// Copyright 2014 Alvaro J. Genial. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package form

import (
	"net/url"
	"strconv"
	"strings"
)

type node map[string]interface{}

func (n node) values(d, e rune) url.Values {
	vs := url.Values{}
	n.merge(d, e, "", &vs)
	return vs
}

func (n node) merge(d, e rune, p string, vs *url.Values) {
	for k, x := range n {
		switch y := x.(type) {
		case string:
			vs.Add(p+escape(d, e, k), y)
		case node:
			y.merge(d, e, p+escape(d, e, k)+string(d), vs)
		default:
			panic("value is neither string nor node")
		}
	}
}

// TODO: Add tests for implicit indexing.
func parseValues(d, e rune, vs url.Values, canIndexFirstLevelOrdinally bool) node {
	// NOTE: Because of the flattening of potentially multiple strings to one key, implicit indexing works:
	//    i. At the first level;   e.g. Foo.Bar=A&Foo.Bar=B     becomes 0.Foo.Bar=A&1.Foo.Bar=B
	//   ii. At the last level;    e.g. Foo.Bar._=A&Foo.Bar._=B becomes Foo.Bar.0=A&Foo.Bar.1=B
	// TODO: At in-between levels; e.g. Foo._.Bar=A&Foo._.Bar=B becomes Foo.0.Bar=A&Foo.1.Bar=B
	//       (This last one requires that there only be one placeholder in order for it to be unambiguous.)

	m := map[string]string{}
	for k, ss := range vs {
		indexLastLevelOrdinally := strings.HasSuffix(k, string(d)+implicitKey)

		for i, s := range ss {
			if canIndexFirstLevelOrdinally {
				k = strconv.Itoa(i) + string(d) + k
			} else if indexLastLevelOrdinally {
				k = strings.TrimSuffix(k, implicitKey) + strconv.Itoa(i)
			}

			m[k] = s
		}
	}

	n := node{}
	for k, s := range m {
		n = n.split(d, e, k, s)
	}
	return n
}

func splitPath(d, e rune, path string) (k, rest string) {
	esc := false
	for i, r := range path {
		switch {
		case !esc && r == e:
			esc = true
		case !esc && r == d:
			return unescape(d, e, path[:i]), path[i+1:]
		default:
			esc = false
		}
	}
	return unescape(d, e, path), ""
}

func (n node) split(d, e rune, path, s string) node {
	k, rest := splitPath(d, e, path)
	if rest == "" {
		return add(n, k, s)
	}
	if _, ok := n[k]; !ok {
		n[k] = node{}
	}

	c := getNode(n[k])
	n[k] = c.split(d, e, rest, s)
	return n
}

func add(n node, k, s string) node {
	if n == nil {
		return node{k: s}
	}

	if _, ok := n[k]; ok {
		panic("key " + k + " already set")
	}

	n[k] = s
	return n
}

func isEmpty(x interface{}) bool {
	switch y := x.(type) {
	case string:
		return y == ""
	case node:
		if s, ok := y[""].(string); ok {
			return s == ""
		}
		return false
	}
	panic("value is neither string nor node")
}

func getNode(x interface{}) node {
	switch y := x.(type) {
	case string:
		return node{"": y}
	case node:
		return y
	}
	panic("value is neither string nor node")
}

func getString(x interface{}) string {
	switch y := x.(type) {
	case string:
		return y
	case node:
		if s, ok := y[""].(string); ok {
			return s
		}
		return ""
	}
	panic("value is neither string nor node")
}

func escape(d, e rune, s string) string {
	s = strings.Replace(s, string(e), string(e)+string(e), -1) // Escape the escape    (\ => \\)
	s = strings.Replace(s, string(d), string(e)+string(d), -1) // Escape the delimiter (. => \.)
	return s
}

func unescape(d, e rune, s string) string {
	s = strings.Replace(s, string(e)+string(d), string(d), -1) // Unescape the delimiter (\. => .)
	s = strings.Replace(s, string(e)+string(e), string(e), -1) // Unescape the escape    (\\ => \)
	return s
}