package parser

import (
	"strings"
	"unicode"
)

func ISBN(orig string) string {
	isbn := getISBN(orig)

	if len(isbn) != 13 && len(isbn) != 10 {
		return ""
	}
	if !validChecksum(isbn) {
		return ""
	}

	return toISBN13(isbn)
}

func getISBN(src string) string {
	isbn := strings.ToUpper(src)
	isNotNumber := func(r rune) bool {
		return !unicode.IsNumber(r)
	}
	isNotNumberOrX := func(r rune) bool {
		return !unicode.IsNumber(r) && r != 'X'
	}

	isbn = strings.TrimLeftFunc(isbn, isNotNumber)
	isbn = strings.TrimRightFunc(isbn, isNotNumberOrX)
	isbn = strings.Replace(isbn, "-", "", -1)
	isbn = strings.Replace(isbn, " ", "", -1)

	if len(isbn) > 13 {
		isbn = isbn[:13]
	}
	return isbn
}

func validChecksum(isbn string) bool {
	if len(isbn) == 10 {
		return rune(isbn[9]) == checkDigit10(isbn)
	}
	return rune(isbn[12]) == checkDigit13(isbn)
}

func toISBN13(isbn string) string {
	if len(isbn) == 13 {
		return isbn
	}

	isbn = "978" + isbn
	return isbn[:12] + string(checkDigit13(isbn))
}

func checkDigit10(isbn string) rune {
	acc := 0
	for i, r := range isbn[:9] {
		acc += (10 - i) * int(r-'0')
	}
	check := (11 - (acc % 11)) % 11

	if check == 10 {
		return 'X'
	}
	return rune(check + '0')
}

func checkDigit13(isbn string) rune {
	acc := 0
	for i, r := range isbn[:12] {
		n := int(r - '0')
		if i%2 == 1 {
			n = 3 * n
		}
		acc += n
	}
	check := (10 - (acc % 10)) % 10
	return rune(check + '0')
}