// Copyright 2016 by Sascha L. Teichmann
// Use of this source code is governed by the MIT license
// that can be found in the LICENSE file.

package common

import (
	"bytes"
	"sync"
)

type btKey struct {
	x int
	y int
}

type btHashEntry struct {
	prev *btHashEntry
	next *btHashEntry
	hash []byte
	btKey
}

type BaseTileHash struct {
	hashes     map[btKey]*btHashEntry
	maxEntries int
	root       btHashEntry
	sync.Mutex
}

func NewBaseTileHash(maxEntries int) *BaseTileHash {
	bth := &BaseTileHash{
		hashes:     map[btKey]*btHashEntry{},
		maxEntries: maxEntries}
	bth.root.next = &bth.root
	bth.root.prev = &bth.root
	return bth
}

func (bth *BaseTileHash) toFront(entry *btHashEntry) {
	if bth.root.next == entry {
		return
	}
	entry.prev.next = entry.next
	entry.next.prev = entry.prev

	entry.next = bth.root.next
	entry.prev = &bth.root

	bth.root.next.prev = entry
	bth.root.next = entry
}

func (bth *BaseTileHash) removeLast() *btHashEntry {
	last := bth.root.prev
	bth.root.prev = last.prev
	last.prev.next = &bth.root
	delete(bth.hashes, last.btKey)
	return last
}

func (bth *BaseTileHash) insertFront(entry *btHashEntry) {
	entry.next = bth.root.next
	entry.prev = &bth.root
	bth.root.next.prev = entry
	bth.root.next = entry
}

func (bth *BaseTileHash) Update(x, y int, hash []byte) bool {
	bth.Lock()
	defer bth.Unlock()
	key := btKey{x, y}
	if old, found := bth.hashes[key]; found {
		if !bytes.Equal(old.hash, hash) {
			old.hash = hash
			bth.toFront(old)
			return true
		}
		return false
	}
	var entry *btHashEntry
	if len(bth.hashes) >= bth.maxEntries {
		entry = bth.removeLast()
	} else {
		entry = new(btHashEntry)
	}
	entry.btKey = key
	entry.hash = hash
	bth.hashes[key] = entry
	bth.insertFront(entry)
	return true
}