// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
// Use of this file is governed by the BSD 3-clause license that
// can be found in the LICENSE.txt file in the project root.

package antlr

import (
	"bytes"
	"errors"
	"fmt"
	"math/bits"
	"os"
	"strconv"
	"strings"
	"syscall"
)

func intMin(a, b int) int {
	if a < b {
		return a
	}
	return b
}

func intMax(a, b int) int {
	if a > b {
		return a
	}
	return b
}

// A simple integer stack

type IntStack []int

var ErrEmptyStack = errors.New("stack is empty")

func (s *IntStack) Pop() (int, error) {
	l := len(*s) - 1
	if l < 0 {
		return 0, ErrEmptyStack
	}
	v := (*s)[l]
	*s = (*s)[0:l]
	return v, nil
}

func (s *IntStack) Push(e int) {
	*s = append(*s, e)
}

const bitsPerWord = 64

func indexForBit(bit int) int {
	return bit / bitsPerWord
}

//goland:noinspection GoUnusedExportedFunction,GoUnusedFunction
func wordForBit(data []uint64, bit int) uint64 {
	idx := indexForBit(bit)
	if idx >= len(data) {
		return 0
	}
	return data[idx]
}

func maskForBit(bit int) uint64 {
	return uint64(1) << (bit % bitsPerWord)
}

func wordsNeeded(bit int) int {
	return indexForBit(bit) + 1
}

type BitSet struct {
	data []uint64
}

// NewBitSet creates a new bitwise set
// TODO: See if we can replace with the standard library's BitSet
func NewBitSet() *BitSet {
	return &BitSet{}
}

func (b *BitSet) add(value int) {
	idx := indexForBit(value)
	if idx >= len(b.data) {
		size := wordsNeeded(value)
		data := make([]uint64, size)
		copy(data, b.data)
		b.data = data
	}
	b.data[idx] |= maskForBit(value)
}

func (b *BitSet) clear(index int) {
	idx := indexForBit(index)
	if idx >= len(b.data) {
		return
	}
	b.data[idx] &= ^maskForBit(index)
}

func (b *BitSet) or(set *BitSet) {
	// Get min size necessary to represent the bits in both sets.
	bLen := b.minLen()
	setLen := set.minLen()
	maxLen := intMax(bLen, setLen)
	if maxLen > len(b.data) {
		// Increase the size of len(b.data) to represent the bits in both sets.
		data := make([]uint64, maxLen)
		copy(data, b.data)
		b.data = data
	}
	// len(b.data) is at least setLen.
	for i := 0; i < setLen; i++ {
		b.data[i] |= set.data[i]
	}
}

func (b *BitSet) remove(value int) {
	b.clear(value)
}

func (b *BitSet) contains(value int) bool {
	idx := indexForBit(value)
	if idx >= len(b.data) {
		return false
	}
	return (b.data[idx] & maskForBit(value)) != 0
}

func (b *BitSet) minValue() int {
	for i, v := range b.data {
		if v == 0 {
			continue
		}
		return i*bitsPerWord + bits.TrailingZeros64(v)
	}
	return 2147483647
}

func (b *BitSet) equals(other interface{}) bool {
	otherBitSet, ok := other.(*BitSet)
	if !ok {
		return false
	}

	if b == otherBitSet {
		return true
	}

	// We only compare set bits, so we cannot rely on the two slices having the same size. Its
	// possible for two BitSets to have different slice lengths but the same set bits. So we only
	// compare the relevant words and ignore the trailing zeros.
	bLen := b.minLen()
	otherLen := otherBitSet.minLen()

	if bLen != otherLen {
		return false
	}

	for i := 0; i < bLen; i++ {
		if b.data[i] != otherBitSet.data[i] {
			return false
		}
	}

	return true
}

func (b *BitSet) minLen() int {
	for i := len(b.data); i > 0; i-- {
		if b.data[i-1] != 0 {
			return i
		}
	}
	return 0
}

func (b *BitSet) length() int {
	cnt := 0
	for _, val := range b.data {
		cnt += bits.OnesCount64(val)
	}
	return cnt
}

func (b *BitSet) String() string {
	vals := make([]string, 0, b.length())

	for i, v := range b.data {
		for v != 0 {
			n := bits.TrailingZeros64(v)
			vals = append(vals, strconv.Itoa(i*bitsPerWord+n))
			v &= ^(uint64(1) << n)
		}
	}

	return "{" + strings.Join(vals, ", ") + "}"
}

type AltDict struct {
	data map[string]interface{}
}

func NewAltDict() *AltDict {
	d := new(AltDict)
	d.data = make(map[string]interface{})
	return d
}

func (a *AltDict) Get(key string) interface{} {
	key = "k-" + key
	return a.data[key]
}

func (a *AltDict) put(key string, value interface{}) {
	key = "k-" + key
	a.data[key] = value
}

func (a *AltDict) values() []interface{} {
	vs := make([]interface{}, len(a.data))
	i := 0
	for _, v := range a.data {
		vs[i] = v
		i++
	}
	return vs
}

func EscapeWhitespace(s string, escapeSpaces bool) string {

	s = strings.Replace(s, "\t", "\\t", -1)
	s = strings.Replace(s, "\n", "\\n", -1)
	s = strings.Replace(s, "\r", "\\r", -1)
	if escapeSpaces {
		s = strings.Replace(s, " ", "\u00B7", -1)
	}
	return s
}

//goland:noinspection GoUnusedExportedFunction
func TerminalNodeToStringArray(sa []TerminalNode) []string {
	st := make([]string, len(sa))

	for i, s := range sa {
		st[i] = fmt.Sprintf("%v", s)
	}

	return st
}

//goland:noinspection GoUnusedExportedFunction
func PrintArrayJavaStyle(sa []string) string {
	var buffer bytes.Buffer

	buffer.WriteString("[")

	for i, s := range sa {
		buffer.WriteString(s)
		if i != len(sa)-1 {
			buffer.WriteString(", ")
		}
	}

	buffer.WriteString("]")

	return buffer.String()
}

// murmur hash
func murmurInit(seed int) int {
	return seed
}

func murmurUpdate(h int, value int) int {
	const c1 uint32 = 0xCC9E2D51
	const c2 uint32 = 0x1B873593
	const r1 uint32 = 15
	const r2 uint32 = 13
	const m uint32 = 5
	const n uint32 = 0xE6546B64

	k := uint32(value)
	k *= c1
	k = (k << r1) | (k >> (32 - r1))
	k *= c2

	hash := uint32(h) ^ k
	hash = (hash << r2) | (hash >> (32 - r2))
	hash = hash*m + n
	return int(hash)
}

func murmurFinish(h int, numberOfWords int) int {
	var hash = uint32(h)
	hash ^= uint32(numberOfWords) << 2
	hash ^= hash >> 16
	hash *= 0x85ebca6b
	hash ^= hash >> 13
	hash *= 0xc2b2ae35
	hash ^= hash >> 16

	return int(hash)
}

func isDirectory(dir string) (bool, error) {
	fileInfo, err := os.Stat(dir)
	if err != nil {
		switch {
		case errors.Is(err, syscall.ENOENT):
			// The given directory does not exist, so we will try to create it
			//
			err = os.MkdirAll(dir, 0755)
			if err != nil {
				return false, err
			}

			return true, nil
		case err != nil:
			return false, err
		default:
		}
	}
	return fileInfo.IsDir(), err
}

// intSlicesEqual returns true if the two slices of ints are equal, and is a little
// faster than slices.Equal.
func intSlicesEqual(s1, s2 []int) bool {
	if s1 == nil && s2 == nil {
		return true
	}
	if s1 == nil || s2 == nil {
		return false
	}
	if len(s1) == 0 && len(s2) == 0 {
		return true
	}

	if len(s1) == 0 || len(s2) == 0 || len(s1) != len(s2) {
		return false
	}
	// If the slices are using the same memory, then they are the same slice
	if &s1[0] == &s2[0] {
		return true
	}
	for i, v := range s1 {
		if v != s2[i] {
			return false
		}
	}
	return true
}

func pcSliceEqual(s1, s2 []*PredictionContext) bool {
	if s1 == nil && s2 == nil {
		return true
	}
	if s1 == nil || s2 == nil {
		return false
	}
	if len(s1) == 0 && len(s2) == 0 {
		return true
	}
	if len(s1) == 0 || len(s2) == 0 || len(s1) != len(s2) {
		return false
	}
	// If the slices are using the same memory, then they are the same slice
	if &s1[0] == &s2[0] {
		return true
	}
	for i, v := range s1 {
		if !v.Equals(s2[i]) {
			return false
		}
	}
	return true
}
