package utils

import (
	"log"
	"strings"
	"unicode/utf8"

	"golang.org/x/net/html/charset"
	"golang.org/x/text/transform"
)

// NormaliseCharset Overrides/fixes charset names to something we can parse.
// Fixes common mispellings and uses a canonical name for equivalent encodings.
// @see https://encoding.spec.whatwg.org#names-and-labels
func NormaliseCharset(characterSet string) string {
	characterSet = strings.ToUpper(characterSet)
	switch characterSet {
	case "UTF8", "UT-8", "UTR-8", "UFT-8", "UTF8-WITHOUT-BOM", "UTF8_GENERAL_CI":
		return "UTF-8"
	// override Japanese
	// CP943: IBM OS/2 Japanese, superset of Cp932 and Shift-JIS
	case "CP943", "CP943C", "SIFT_JIS", "SHIFT-JIS":
		return "SHIFT_JIS"
	// override Korean
	case "EUC-KR", "MS949", "KSC5601", "WINDOWS-949", "KS_C_5601-1987", "KSC_5601":
		return "UHC"
	// override Thai
	//case "TIS-620", "WINDOWS-874":
	//	return "ISO-8859-11"
	// override latin-2
	case "LATIN2_HUNGARIAN_CI", "LATIN2":
		return "LATIN-2"
	// override cyrillic
	case "WIN1251", "WIN-1251", "WINDOWS-1251":
		return "CP1251"
	// override Hebrew
	case "WINDOWS-1255":
		return "ISO-8859-8"
	// override Turkish
	//case "WINDOWS-1254":
	//	return "ISO-8859-9"
	// override the parsing of ISO-8859-1 to behave as Windows-1252 (CP1252):
	// in ISO-8859-1, everything from 128-255 in the ASCII table are ctrl characters,
	// whilst in CP1252 they're symbols
	// override Baltic
	case "WINDOWS-1257":
		return "ISO-8859-13"
	case "ANSI", "LATIN-1", "ISO", "RFC", "MACINTOSH", "8859-1", "8859-15", "ISO8859-1", "ISO8859-15", "ISO-8559-1", "ISO-8859-1", "ISO-8859-15":
		return "CP1252"
	}
	return characterSet
}

// UTF8encode converts a string from the source character set to UTF-8, skipping invalid byte sequences
// @see http://stackoverflow.com/questions/32512500/ignore-illegal-bytes-when-decoding-text-with-go
func UTF8encode(raw string, sourceCharset string) string {
	enc, name := charset.Lookup(sourceCharset)
	if nil == enc {
		log.Println("Cannot convert from", sourceCharset, ":", name)
		return raw
	}

	dst := make([]byte, len(raw))
	d := enc.NewDecoder()

	var (
		in  int
		out int
	)
	for in < len(raw) {
		// Do the transformation
		ndst, nsrc, err := d.Transform(dst[out:], []byte(raw[in:]), true)
		in += nsrc
		out += ndst
		if err == nil {
			// Completed transformation
			break
		}
		if err == transform.ErrShortDst {
			// Our output buffer is too small, so we need to grow it
			t := make([]byte, (cap(dst)+1)*2)
			copy(t, dst)
			dst = t
			continue
		}
		// We're here because of at least one illegal character. Skip over the current rune
		// and try again.
		_, width := utf8.DecodeRuneInString(raw[in:])
		in += width
	}
	return string(dst)
}
