package sortpath

import (
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
)

// Strings follow with numbers like: s1, s1.2, s2-3, ...
var split_path_regex = regexp.MustCompile(`^(.*?)(\d+(?:\.\d+)?)(?:-(\d+(?:\.\d+)?))?$`)

type part struct {
	fullname string
	name     string
	number   float64
}

func (a part) Compare(b part) float64 {
	if a.number == 0 || b.number == 0 {
		return float64(strings.Compare(a.fullname, b.fullname))
	}
	if a.name == b.name {
		return a.number - b.number
	} else {
		return float64(strings.Compare(a.name, b.name))
	}
}

func parsePart(p string) part {
	r := split_path_regex.FindStringSubmatch(p)
	if len(r) == 0 {
		return part{p, p, 0}
	}
	n, err := strconv.ParseFloat(r[2], 64)
	if err != nil {
		return part{p, p, 0}
	}
	return part{p, r[1], n}
}

// mode=0 alpha for path and file
// mode=1 alphanum for path and alpha for file
// mode=2 alphanum for path and file
func parse(filename string, mode int) []part {
	pathname, name := filepath.Split(strings.ToLower(filename))
	pathname = strings.TrimSuffix(pathname, string(filepath.Separator))
	ext := filepath.Ext(name)
	name = name[0 : len(name)-len(ext)]

	f := []part{}
	for _, p := range strings.Split(pathname, string(filepath.Separator)) {
		if mode > 0 { // alphanum for path
			f = append(f, parsePart(p))
		} else {
			f = append(f, part{p, p, 0})
		}
	}
	if mode == 2 { // alphanum for file
		f = append(f, parsePart(name))
	} else {
		f = append(f, part{name, name, 0})
	}
	return f
}

func comparePart(a, b []part) float64 {
	m := len(a)
	if m > len(b) {
		m = len(b)
	}
	for i := 0; i < m; i++ {
		c := a[i].Compare(b[i])
		if c != 0 {
			return c
		}
	}
	return float64(len(a) - len(b))
}

type by struct {
	filenames []string
	paths     [][]part
}

func (b by) Len() int           { return len(b.filenames) }
func (b by) Less(i, j int) bool { return comparePart(b.paths[i], b.paths[j]) < 0 }
func (b by) Swap(i, j int) {
	b.filenames[i], b.filenames[j] = b.filenames[j], b.filenames[i]
	b.paths[i], b.paths[j] = b.paths[j], b.paths[i]
}

func By(filenames []string, mode int) by {
	p := [][]part{}
	for _, filename := range filenames {
		p = append(p, parse(filename, mode))
	}
	return by{filenames, p}
}