package goqr

import (
	"math"
)

const maxPoly = 64

type galoisField struct {
	p   int
	log []uint8
	exp []uint8
}

var gf16Exp = []uint8{
	0x01, 0x02, 0x04, 0x08, 0x03, 0x06, 0x0c, 0x0b,
	0x05, 0x0a, 0x07, 0x0e, 0x0f, 0x0d, 0x09, 0x01,
}

var gf16Log = []uint8{
	0x00, 0x0f, 0x01, 0x04, 0x02, 0x08, 0x05, 0x0a,
	0x03, 0x0e, 0x09, 0x07, 0x06, 0x0d, 0x0b, 0x0c,
}

var gf16 = galoisField{
	p:   15,
	log: gf16Log,
	exp: gf16Exp,
}

var gf256Exp = [256]uint8{
	0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80,
	0x1d, 0x3a, 0x74, 0xe8, 0xcd, 0x87, 0x13, 0x26,
	0x4c, 0x98, 0x2d, 0x5a, 0xb4, 0x75, 0xea, 0xc9,
	0x8f, 0x03, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0,
	0x9d, 0x27, 0x4e, 0x9c, 0x25, 0x4a, 0x94, 0x35,
	0x6a, 0xd4, 0xb5, 0x77, 0xee, 0xc1, 0x9f, 0x23,
	0x46, 0x8c, 0x05, 0x0a, 0x14, 0x28, 0x50, 0xa0,
	0x5d, 0xba, 0x69, 0xd2, 0xb9, 0x6f, 0xde, 0xa1,
	0x5f, 0xbe, 0x61, 0xc2, 0x99, 0x2f, 0x5e, 0xbc,
	0x65, 0xca, 0x89, 0x0f, 0x1e, 0x3c, 0x78, 0xf0,
	0xfd, 0xe7, 0xd3, 0xbb, 0x6b, 0xd6, 0xb1, 0x7f,
	0xfe, 0xe1, 0xdf, 0xa3, 0x5b, 0xb6, 0x71, 0xe2,
	0xd9, 0xaf, 0x43, 0x86, 0x11, 0x22, 0x44, 0x88,
	0x0d, 0x1a, 0x34, 0x68, 0xd0, 0xbd, 0x67, 0xce,
	0x81, 0x1f, 0x3e, 0x7c, 0xf8, 0xed, 0xc7, 0x93,
	0x3b, 0x76, 0xec, 0xc5, 0x97, 0x33, 0x66, 0xcc,
	0x85, 0x17, 0x2e, 0x5c, 0xb8, 0x6d, 0xda, 0xa9,
	0x4f, 0x9e, 0x21, 0x42, 0x84, 0x15, 0x2a, 0x54,
	0xa8, 0x4d, 0x9a, 0x29, 0x52, 0xa4, 0x55, 0xaa,
	0x49, 0x92, 0x39, 0x72, 0xe4, 0xd5, 0xb7, 0x73,
	0xe6, 0xd1, 0xbf, 0x63, 0xc6, 0x91, 0x3f, 0x7e,
	0xfc, 0xe5, 0xd7, 0xb3, 0x7b, 0xf6, 0xf1, 0xff,
	0xe3, 0xdb, 0xab, 0x4b, 0x96, 0x31, 0x62, 0xc4,
	0x95, 0x37, 0x6e, 0xdc, 0xa5, 0x57, 0xae, 0x41,
	0x82, 0x19, 0x32, 0x64, 0xc8, 0x8d, 0x07, 0x0e,
	0x1c, 0x38, 0x70, 0xe0, 0xdd, 0xa7, 0x53, 0xa6,
	0x51, 0xa2, 0x59, 0xb2, 0x79, 0xf2, 0xf9, 0xef,
	0xc3, 0x9b, 0x2b, 0x56, 0xac, 0x45, 0x8a, 0x09,
	0x12, 0x24, 0x48, 0x90, 0x3d, 0x7a, 0xf4, 0xf5,
	0xf7, 0xf3, 0xfb, 0xeb, 0xcb, 0x8b, 0x0b, 0x16,
	0x2c, 0x58, 0xb0, 0x7d, 0xfa, 0xe9, 0xcf, 0x83,
	0x1b, 0x36, 0x6c, 0xd8, 0xad, 0x47, 0x8e, 0x01,
}

var gf256Log = [256]uint8{
	0x00, 0xff, 0x01, 0x19, 0x02, 0x32, 0x1a, 0xc6,
	0x03, 0xdf, 0x33, 0xee, 0x1b, 0x68, 0xc7, 0x4b,
	0x04, 0x64, 0xe0, 0x0e, 0x34, 0x8d, 0xef, 0x81,
	0x1c, 0xc1, 0x69, 0xf8, 0xc8, 0x08, 0x4c, 0x71,
	0x05, 0x8a, 0x65, 0x2f, 0xe1, 0x24, 0x0f, 0x21,
	0x35, 0x93, 0x8e, 0xda, 0xf0, 0x12, 0x82, 0x45,
	0x1d, 0xb5, 0xc2, 0x7d, 0x6a, 0x27, 0xf9, 0xb9,
	0xc9, 0x9a, 0x09, 0x78, 0x4d, 0xe4, 0x72, 0xa6,
	0x06, 0xbf, 0x8b, 0x62, 0x66, 0xdd, 0x30, 0xfd,
	0xe2, 0x98, 0x25, 0xb3, 0x10, 0x91, 0x22, 0x88,
	0x36, 0xd0, 0x94, 0xce, 0x8f, 0x96, 0xdb, 0xbd,
	0xf1, 0xd2, 0x13, 0x5c, 0x83, 0x38, 0x46, 0x40,
	0x1e, 0x42, 0xb6, 0xa3, 0xc3, 0x48, 0x7e, 0x6e,
	0x6b, 0x3a, 0x28, 0x54, 0xfa, 0x85, 0xba, 0x3d,
	0xca, 0x5e, 0x9b, 0x9f, 0x0a, 0x15, 0x79, 0x2b,
	0x4e, 0xd4, 0xe5, 0xac, 0x73, 0xf3, 0xa7, 0x57,
	0x07, 0x70, 0xc0, 0xf7, 0x8c, 0x80, 0x63, 0x0d,
	0x67, 0x4a, 0xde, 0xed, 0x31, 0xc5, 0xfe, 0x18,
	0xe3, 0xa5, 0x99, 0x77, 0x26, 0xb8, 0xb4, 0x7c,
	0x11, 0x44, 0x92, 0xd9, 0x23, 0x20, 0x89, 0x2e,
	0x37, 0x3f, 0xd1, 0x5b, 0x95, 0xbc, 0xcf, 0xcd,
	0x90, 0x87, 0x97, 0xb2, 0xdc, 0xfc, 0xbe, 0x61,
	0xf2, 0x56, 0xd3, 0xab, 0x14, 0x2a, 0x5d, 0x9e,
	0x84, 0x3c, 0x39, 0x53, 0x47, 0x6d, 0x41, 0xa2,
	0x1f, 0x2d, 0x43, 0xd8, 0xb7, 0x7b, 0xa4, 0x76,
	0xc4, 0x17, 0x49, 0xec, 0x7f, 0x0c, 0x6f, 0xf6,
	0x6c, 0xa1, 0x3b, 0x52, 0x29, 0x9d, 0x55, 0xaa,
	0xfb, 0x60, 0x86, 0xb1, 0xbb, 0xcc, 0x3e, 0x5a,
	0xcb, 0x59, 0x5f, 0xb0, 0x9c, 0xa9, 0xa0, 0x51,
	0x0b, 0xf5, 0x16, 0xeb, 0x7a, 0x75, 0x2c, 0xd7,
	0x4f, 0xae, 0xd5, 0xe9, 0xe6, 0xe7, 0xad, 0xe8,
	0x74, 0xd6, 0xf4, 0xea, 0xa8, 0x50, 0x58, 0xaf,
}

var gf256 = galoisField{
	255,
	gf256Log[:],
	gf256Exp[:],
}

/************************************************************************
 * Polynomial operations
 */

func polyAdd(dst, src []uint8, c uint8, shift int, gf *galoisField) {

	logC := gf.log[c]
	if c == 0 {
		return
	}

	for i := 0; i < maxPoly; i++ {
		p := i + shift
		v := src[i]
		if p < 0 || p >= maxPoly {
			continue
		}
		if v == 0 {
			continue
		}
		pos := (int(gf.log[v]) + int(logC)) % gf.p

		dst[p] ^= gf.exp[pos]
	}
}

func polyEval(s []uint8, x uint8, gf *galoisField) uint8 {
	sum := uint8(0)
	logX := gf.log[x]

	if x == 0 {
		return s[0]
	}

	for i := 0; i < maxPoly; i++ {
		c := s[i]
		if c == 0 {
			continue
		}
		sum ^= gf.exp[(int(gf.log[c])+int(logX)*i)%gf.p]
	}
	return sum
}

/************************************************************************
 * Berlekamp-Massey algorithm for finding error locator polynomials.
 */

func berlekampMassey(s []uint8, num int, gf *galoisField, sigma []uint8) {

	C := make([]uint8, maxPoly)
	B := make([]uint8, maxPoly)

	L := 0
	m := 1
	b := uint8(1)

	B[0] = 1
	C[0] = 1

	for n := 0; n < num; n++ {
		d := s[n]

		for i := 1; i <= L; i++ {
			if !(C[i] > 0 && s[n-i] > 0) {
				continue
			}
			d ^= gf.exp[(int(gf.log[C[i]])+int(gf.log[s[n-i]]))%gf.p]
		}

		mult := gf.exp[(gf.p-int(gf.log[b])+int(gf.log[d]))%gf.p]

		if d == 0 {
			m++
		} else if L*2 <= n {
			T := make([]uint8, 0)
			T = append(T, C...)

			polyAdd(C, B, mult, m, gf)

			B = B[:0]
			B = append(B, T...)

			L = n + 1 - L
			b = d
			m = 1

		} else {
			polyAdd(C, B, mult, m, gf)
			m++
		}
	}
	sigma = sigma[:0]
	sigma = append(sigma, C...)
}

/************************************************************************
 * Code stream error correction
 *
 * Generator polynomial for GF(2^8) is x^8 + x^4 + x^3 + x^2 + 1
 */

func blockSyndromes(data []uint8, bs, npar int, s []uint8) int {
	nonzero := 0
	for i := 0; i < maxPoly; i++ {
		s[i] = 0
	}
	for i := 0; i < npar; i++ {
		for j := 0; j < bs; j++ {
			c := data[bs-j-1]
			if c == 0 {
				continue
			}
			s[i] ^= gf256Exp[((int)(gf256Log[c])+i*j)%255]
		}
		if s[i] != 0 {
			nonzero = 1
		}
	}
	return nonzero
}

func elocPoly(omega []uint8, s []uint8, sigma []uint8, npar int) {

	for i := 0; i < maxPoly; i++ {
		omega[i] = 0
	}

	for i := 0; i < npar; i++ {
		a := sigma[i]
		logA := gf256Log[a]

		if a == 0 {
			continue
		}

		for j := 0; j+1 < maxPoly; j++ {
			b := s[j+1]
			if i+j >= npar {
				break
			}

			if b == 0 {
				continue
			}
			omega[i+j] ^= gf256Exp[(int(logA)+int(gf256Log[b]))%255]
		}
	}
}

func correctBlock(data []uint8, ecc *qrRsParams) error {

	npar := ecc.bs - ecc.dw
	s := make([]uint8, maxPoly)

	// Compute syndrome vector
	if 0 == blockSyndromes(data, ecc.bs, npar, s) {
		return nil
	}

	sigma := make([]uint8, maxPoly)
	berlekampMassey(s, npar, &gf256, sigma)

	// Compute derivative of sigma
	sigmaDeriv := make([]uint8, maxPoly)
	for i := 0; i+1 < maxPoly; i += 2 {
		sigmaDeriv[i] = sigma[i+1]
	}

	// Compute error evaluator polynomial
	omega := make([]uint8, maxPoly)
	elocPoly(omega, s, sigma, npar-1)

	// Find error locations and magnitudes
	for i := 0; i < ecc.bs; i++ {
		xinv := gf256Exp[255-i]
		if 0 == polyEval(sigma, xinv, &gf256) {
			sdX := polyEval(sigmaDeriv, xinv, &gf256)
			omegaX := polyEval(omega, xinv, &gf256)
			error := gf256Exp[(255-int(gf256Log[sdX])+int(gf256Log[omegaX]))%255]
			data[ecc.bs-i-1] ^= error
		}
	}

	if blockSyndromes(data, ecc.bs, npar, s) != 0 {
		return ErrDataEcc
	}
	return nil
}

/************************************************************************
 * Format value error correction
 *
 * Generator polynomial for GF(2^4) is x^4 + x + 1
 */

const formatMaxError = 3
const formatSyndrome = formatMaxError * 2
const formatBits = 15

func formatSyndromes(u uint16, s []uint8) int {
	nonzero := 0
	for i := 0; i < maxPoly; i++ {
		s[i] = 0
	}
	for i := 0; i < formatSyndrome; i++ {
		s[i] = 0
		for j := 0; j < formatBits; j++ {
			if u&(uint16(1 << uint(j))) != 0 {
				s[i] ^= gf16Exp[((i+1)*j)%15]
			}
		}
		if s[i] != 0 {
			nonzero = 1
		}
	}
	return nonzero
}

func correctFormat(fRet *uint16) error {
	u := *fRet

	// Evaluate U (received codeword) at each of alpha_1 .. alpha_6
	// to get S_1 .. S_6 (but we index them from 0).

	s := make([]uint8, maxPoly)
	if 0 == formatSyndromes(u, s) {
		return nil
	}

	sigma := make([]uint8, maxPoly)
	berlekampMassey(s, formatSyndrome, &gf16, sigma)

	// Now, find the roots of the polynomial
	for i := 0; i < 15; i++ {
		if 0 == polyEval(sigma, gf16Exp[15-i], &gf16) {
			u ^= 1 << uint16(i)
		}
	}
	if 0 != formatSyndromes(u, s) {
		return ErrFormatEcc
	}

	*fRet = u
	return nil
}

/************************************************************************
 * Decoder algorithm
 */

type datastream struct {
	raw      [qrMaxPayload]uint8
	dataBits int
	ptr      int
	data     [qrMaxPayload]uint8
}

func maskBit(mask, i, j int) int {
	k := 0
	switch mask {
	case 0:
		k = (i + j) % 2
	case 1:
		k = i % 2
	case 2:
		k = j % 3
	case 3:
		k = (i + j) % 3
	case 4:
		k = ((i / 2) + (j / 3)) % 2
	case 5:
		k = (i*j)%2 + (i*j)%3
	case 6:
		k = ((i*j)%2 + (i*j)%3) % 2
	case 7:
		k = ((i*j)%3 + (i+j)%2) % 2
	default:
		return 0
	}
	if k != 0 {
		return 0
	}
	return 1
}

func reservedCell(version, i, j int) int {
	ver := &qrVersionDb[version]
	size := version*4 + 17
	ai := -1
	aj := -1
	a := 0

	// Finder + format: top left
	if i < 9 && j < 9 {
		return 1
	}

	// Finder + format: bottom left
	if i+8 >= size && j < 9 {
		return 1
	}

	// Finder + format: top right
	if i < 9 && j+8 >= size {
		return 1
	}
	// Exclude timing patterns
	if i == 6 || j == 6 {
		return 1
	}

	// Exclude Version info, if it exists. Version info sits adjacent to
	// the top-right and bottom-left finders in three rows, bounded by
	// the timing pattern.

	if version >= 7 {
		if i < 6 && j+11 >= size {
			return 1
		}

		if i+11 >= size && j < 6 {
			return 1
		}
	}
	// Exclude alignment patterns
	for a = 0; a < qrMaxAliment && ver.apat[a] != 0; a++ {
		p := ver.apat[a]

		if int(math.Abs(float64(p-i))) < 3 {
			ai = a
		}
		if int(math.Abs(float64(p-j))) < 3 {
			aj = a
		}
	}

	if ai >= 0 && aj >= 0 {
		a--
		if ai > 0 && ai < a {
			return 1
		}
		if aj > 0 && aj < a {
			return 1
		}
		if aj == a && ai == a {
			return 1
		}
	}
	return 0
}

func codestreamEcc(data *QRData, ds *datastream) error {
	ver := &qrVersionDb[data.Version]
	sbEcc := &ver.ecc[data.EccLevel]
	var lbEcc qrRsParams
	lbCount := (ver.dataBytes - sbEcc.bs*sbEcc.ns) / (sbEcc.bs + 1)

	bc := lbCount + sbEcc.ns
	eccOffset := sbEcc.dw*bc + lbCount

	dstOffset := 0
	lbEcc = *sbEcc

	lbEcc.dw++
	lbEcc.bs++

	for i := 0; i < bc; i++ {
		dst := ds.data[dstOffset:]
		ecc := sbEcc
		if i < sbEcc.ns {
			ecc = sbEcc
		} else {
			ecc = &lbEcc
		}
		numEc := ecc.bs - ecc.dw

		for j := 0; j < ecc.dw; j++ {
			dst[j] = ds.raw[j*bc+i]
		}

		for j := 0; j < numEc; j++ {
			dst[ecc.dw+j] = ds.raw[eccOffset+j*bc+i]
		}

		err := correctBlock(dst, ecc)
		if err != nil {
			return err
		}

		dstOffset += ecc.dw
	}

	ds.dataBits = dstOffset * 8
	return nil
}

func bitsRemaining(ds *datastream) int {
	return ds.dataBits - ds.ptr
}

func takeBits(ds *datastream, len int) int {
	ret := 0
	for len > 0 && (ds.ptr < ds.dataBits) {
		b := ds.data[ds.ptr>>3]
		bitpos := ds.ptr & 7
		ret <<= 1
		if ((b << uint(bitpos)) & 0x80) != 0 {
			ret |= 1
		}
		ds.ptr++
		len--
	}
	return ret
}

func numericTuple(data *QRData, ds *datastream, bits, digits int) int {
	if bitsRemaining(ds) < bits {
		return -1
	}
	tuple := takeBits(ds, bits)

	for i := digits - 1; i >= 0; i-- {
		data.Payload = append(data.Payload, uint8(tuple%10)+uint8('0'))

		tuple /= 10
	}
	return 0
}

func decodeNumeric(data *QRData, ds *datastream) error {
	bits := 14
	if data.Version < 10 {
		bits = 10
	} else if data.Version < 27 {
		bits = 12
	}

	count := takeBits(ds, bits)
	if len(data.Payload)+count+1 > qrMaxPayload {
		return ErrDataOverflow
	}
	for count >= 3 {
		if numericTuple(data, ds, 10, 3) < 0 {
			return ErrDataUnderflow
		}
		count -= 3
	}
	if count >= 2 {
		if numericTuple(data, ds, 7, 2) < 0 {
			return ErrDataUnderflow

		}
		count -= 2
	}
	if count > 0 {
		if numericTuple(data, ds, 4, 1) < 0 {
			return ErrDataUnderflow
		}
	}
	return nil
}

var alphaMap = []byte("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:")

func alphaTuple(data *QRData, ds *datastream, bits, digits int) int {
	if bitsRemaining(ds) < bits {
		return -1
	}
	tuple := takeBits(ds, bits)
	payloadLen := len(data.Payload)
	data.Payload = append(data.Payload, make([]uint8, digits)...)
	for i := 0; i < digits; i++ {
		data.Payload[payloadLen+digits-i-1] = alphaMap[tuple%45]
		tuple /= 45
	}
	return 0
}

func decodeAlpha(data *QRData, ds *datastream) error {
	bits := 13
	if data.Version < 10 {
		bits = 9
	} else if data.Version < 27 {
		bits = 11
	}
	count := takeBits(ds, bits)
	if len(data.Payload)+count+1 > qrMaxPayload {
		return ErrDataOverflow
	}
	for count >= 2 {
		if alphaTuple(data, ds, 11, 2) < 0 {
			return ErrDataUnderflow
		}
		count -= 2
	}
	if count > 0 {
		if alphaTuple(data, ds, 6, 1) < 0 {
			return ErrDataUnderflow
		}
	}
	return nil
}

func decodeByte(data *QRData, ds *datastream) error {
	bits := 16
	if data.Version < 10 {
		bits = 8
	}
	count := takeBits(ds, bits)
	if len(data.Payload)+count+1 > qrMaxPayload {
		return ErrDataOverflow
	}
	if bitsRemaining(ds) < count*8 {
		return ErrDataUnderflow
	}
	for i := 0; i < count; i++ {
		data.Payload = append(data.Payload, uint8(takeBits(ds, 8)))
	}
	return nil
}

func decodeKanji(data *QRData, ds *datastream) error {
	bits := 12

	if data.Version < 10 {
		bits = 8
	} else if data.Version < 27 {
		bits = 10
	}
	count := takeBits(ds, bits)

	if len(data.Payload)+count*2+1 > qrMaxPayload {
		return ErrDataOverflow
	}

	if bitsRemaining(ds) < count*13 {
		return ErrDataUnderflow
	}

	for i := 0; i < count; i++ {
		d := takeBits(ds, 13)
		msB := d / 0xc0
		lsB := d % 0xc0
		intermediate := uint16((msB << 8) | lsB)
		var sjw uint16

		if intermediate+0x8140 <= 0x9ffc {
			// bytes are in the range 0x8140 to 0x9FFC
			sjw = intermediate + 0x8140
		} else {
			// bytes are in the range 0xE040 to 0xEBBF
			sjw = intermediate + 0xc140
		}
		data.Payload = append(data.Payload, uint8(sjw>>8))
		data.Payload = append(data.Payload, uint8(sjw&0xff))
	}
	return nil
}

func decodeEci(data *QRData, ds *datastream) error {
	if bitsRemaining(ds) < 8 {
		return ErrDataOverflow
	}
	data.Eci = uint32(takeBits(ds, 8))
	if (data.Eci & 0xc0) == 0x80 {
		if bitsRemaining(ds) < 8 {
			return ErrDataUnderflow
		}
		data.Eci = (data.Eci << 8) | uint32(takeBits(ds, 8))
	} else if (data.Eci & 0xe0) == 0xc0 {
		if bitsRemaining(ds) < 16 {
			return ErrDataUnderflow
		}
		data.Eci = (data.Eci << 16) | uint32(takeBits(ds, 16))
	}
	return nil
}

func decodePayload(data *QRData, ds *datastream) error {

	for bitsRemaining(ds) >= 4 {
		var err error
		_type := takeBits(ds, 4)
		switch _type {
		case qrDataTypeNumeric:
			err = decodeNumeric(data, ds)
		case qrDataTypeAlpha:
			err = decodeAlpha(data, ds)
		case qrDataTypeByte:
			err = decodeByte(data, ds)
		case qrDataTypeKanji:
			err = decodeKanji(data, ds)
		case 7:
			err = decodeEci(data, ds)
		default:
			goto done
		}

		if err != nil {
			return err
		}
		if 0 == (_type&(_type-1)) && (_type > data.DataType) {
			data.DataType = _type
		}
	}

done:
	return nil
}
