
import os
import sys
from struct import Struct
from collections import namedtuple

from pychess.System import conf
from pychess.System.prefix import addDataPrefix
from pychess.Utils.lutils.lmove import parsePolyglot
from pychess.System.Log import log

if getattr(sys, 'frozen', False):
    # pyinstaller specific!
    if hasattr(sys, "_MEIPASS"):
        base_path = sys._MEIPASS
    else:
        base_path = os.path.dirname(sys.executable)
    default_path = os.path.join(base_path, "pychess_book.bin")
else:
    default_path = os.path.join(addDataPrefix("pychess_book.bin"))

path = conf.get("opening_file_entry", default_path)

if os.path.isfile(path):
    bookfile = True
else:
    bookfile = False
    log.warning("Could not find %s" % path)

# The book probing code is based on that of PolyGlot by Fabien Letouzey.
# PolyGlot is available under the GNU GPL from http://wbec-ridderkerk.nl

BookEntry = namedtuple('BookEntry', 'key move weight learn')
# 'key' c_uint64      the position's hash
# 'move' c_uint16     the candidate move
# 'weight' c_uint16   proportional to prob. we should play it
# The following terms are not always available:
# 'learn' c_uint32    we use this NOT the polyglot way but as in
#                     https://github.com/mcostalba/chess_db

entrystruct = Struct(">QHHI")
entrysize = entrystruct.size


def getOpenings(board):
    """ Return a tuple (move, weight, learn) for each opening move
        in the given position. The weight is proportional to the probability
        that a move should be played. By convention, games is the number of
        times a move has been tried, and score the number of points it has
        scored (with 2 per victory and 1 per draw). However, opening books
        aren't required to keep this information. """

    openings = []
    if not bookfile:
        return openings

    with open(path, "rb") as bookFile:
        key = board.hash
        # Find the first entry whose key is >= the position's hash
        bookFile.seek(0, os.SEEK_END)
        low, high = 0, bookFile.tell() // 16 - 1
        if high < 0:
            return openings
        while low < high:
            mid = (low + high) // 2
            bookFile.seek(mid * 16)
            entry = bookFile.read(entrysize)
            if len(entry) != entrysize:
                return openings
            entry = BookEntry._make(entrystruct.unpack(entry))
            if entry.key < key:
                low = mid + 1
            else:
                high = mid

        bookFile.seek(low * 16)
        while True:
            entry = bookFile.read(entrysize)
            if len(entry) != entrysize:
                return openings
            entry = BookEntry._make(entrystruct.unpack(entry))
            if entry.key != key:
                break
            move = parsePolyglot(board, entry.move)
            openings.append((move, entry.weight, entry.learn))
    return openings
