package palindrome

import (
	"fmt"
	"strconv"
)

// Product is a palindromic product and all of its
// factorizations
type Product struct {
	Product        int
	Factorizations [][2]int
}

func createProduct(fac1, fac2 int) *Product {
	p := Product{Product: (fac1 * fac2)}
	p.addFactors(fac1, fac2)
	return &p
}

func (p *Product) hasFactors(fac1, fac2 int) bool {
	for i := 0; i < len(p.Factorizations); i++ {
		if p.Factorizations[i][0] == fac1 && p.Factorizations[i][1] == fac2 {
			return true
		}
		if p.Factorizations[i][1] == fac1 && p.Factorizations[i][0] == fac2 {
			return true
		}
	}
	return false
}

func (p *Product) addFactors(fac1, fac2 int) {
	if (fac1 * fac2) == p.Product {
		if !p.hasFactors(fac1, fac2) {
			p.Factorizations = append(p.Factorizations, [2]int{fac1, fac2})
		}
	}
}

// Products takes a min and a max and finds
func Products(fmin, fmax int) (Product, Product, error) {
	if fmin > fmax {
		return Product{}, Product{}, fmt.Errorf("fmin > fmax")
	}
	var pMin, pMax Product
	var allProducts []Product

	for i := fmin; i <= fmax; i++ {
		for j := fmin; j <= fmax; j++ {
			if isPalindrome(strconv.Itoa(i * j)) {
				var found bool
				for k := 0; k < len(allProducts); k++ {
					if allProducts[k].Product == (i * j) {
						allProducts[k].addFactors(i, j)
						found = true
						break
					}
				}
				if !found {
					allProducts = append(allProducts, *createProduct(i, j))
				}
			}
		}
	}
	for i := range allProducts {
		if allProducts[i].Product > pMax.Product {
			// is the old pMax the new pMin?
			if len(pMin.Factorizations) == 0 {
				pMin = pMax
			}
			pMax = allProducts[i]
		} else {
			if allProducts[i].Product < pMin.Product {
				pMin = allProducts[i]
			}
		}
	}
	if len(allProducts) == 0 {
		return pMin, pMax, fmt.Errorf("No palindromes")
	}
	return pMin, pMax, nil
}

func isPalindrome(s string) bool {
	for i := 0; i < (len(s) / 2); i++ {
		if s[i] != s[len(s)-1-i] {
			return false
		}
	}
	return true
}
