from __future__ import absolute_import
from __future__ import print_function
import os
from os.path import join, dirname, abspath
import sys
import json
import platform
from functools import partial
from hashlib import md5
from threading import Thread, Lock
from copy import deepcopy

from gi.repository import GObject

from pychess.System import conf, fident
from pychess.System.Log import log
from pychess.System.command import Command
from pychess.System.SubProcess import SubProcess, searchPath, SubProcessError
from pychess.System.prefix import addUserConfigPrefix, getDataPrefix, getEngineDataPrefix
from pychess.Players.Player import PlayerIsDead
from pychess.Utils import createStoryTextAppEvent
from pychess.Utils.const import BLACK, UNKNOWN_REASON, SUBPROCESS_SUBPROCESS, WHITE, \
    HINT, ANALYZING, INVERSE_ANALYZING
from .CECPEngine import CECPEngine
from .UCIEngine import UCIEngine
from pychess.Variants import variants

attrToProtocol = {"uci": UCIEngine, "xboard": CECPEngine}

PYTHONBIN = sys.executable.split("/")[-1]
BITNESS = "64" if platform.machine().endswith('64') else "32"

if sys.platform == "win32":
    backup = [
        {"protocol": "uci",
         "name": "stockfish-7-%s.exe" % BITNESS,
         "country": "no"},
        {"protocol": "xboard",
         "name": "sjaakii_win%s_ms.exe" % BITNESS,
         "country": "nl"},
    ]
    if getattr(sys, 'frozen', False):
        backup.append({"protocol": "xboard",
                       "name": "pychess-engine",
                       "country": "dk"})
    else:
        backup.append({"protocol": "xboard",
                       "name": "PyChess.py",
                       "country": "dk",
                       "vm_name": PYTHONBIN,
                       "vm_args": ["-u"]})
else:
    backup = [
        {"protocol": "xboard",
         "name": "pychess-engine",
         "country": "dk"},
        {"protocol": "xboard",
         "name": "PyChess.py",
         "country": "dk",
         "vm_name": PYTHONBIN,
         "vm_args": ["-u"]},
        #    {"protocol": "xboard", "name": "shatranj.py", "country": "us",
        #        "vm_name": PYTHONBIN, "vm_args": ["-u"], "args": ["-xboard"]},
        {"protocol": "xboard",
         "name": "gnuchess",
         "country": "us"},
        {"protocol": "xboard",
         "name": "gnome-gnuchess",
         "country": "us"},
        {"protocol": "xboard",
         "name": "crafty",
         "country": "us"},
        {"protocol": "xboard",
         "name": "faile",
         "country": "ca",
         "protover": 1},
        {"protocol": "xboard",
         "name": "phalanx",
         "country": "cz",
         "protover": 1},
        {"protocol": "xboard",
         "name": "sjeng",
         "country": "be"},
        {"protocol": "xboard",
         "name": "hoichess",
         "country": "de"},
        {"protocol": "xboard",
         "name": "boochess",
         "country": "de",
         "protover": 1},
        {"protocol": "xboard",
         "name": "amy",
         "country": "de"},
        {"protocol": "xboard",
         "name": "amundsen",
         "country": "sw"},
        {"protocol": "uci",
         "name": "gnuchessu",
         "country": "us"},
        {"protocol": "uci",
         "name": "robbolito",
         "country": "ru"},
        {"protocol": "uci",
         "name": "glaurung",
         "country": "no"},
        {"protocol": "uci",
         "name": "stockfish",
         "country": "no"},
        {"protocol": "uci",
         "name": "ShredderClassicLinux",
         "country": "de"},
        {"protocol": "uci",
         "name": "fruit_21_static",
         "country": "fr"},
        {"protocol": "uci",
         "name": "fruit",
         "country": "fr"},
        {"protocol": "uci",
         "name": "toga2",
         "country": "de"},
        {"protocol": "uci",
         "name": "hiarcs",
         "country": "gb"},
        {"protocol": "uci",
         "name": "diablo",
         "country": "us"},
        {"protocol": "uci",
         "name": "Houdini.exe",
         "country": "be",
         "vm_name": "wine"},
        {"protocol": "uci",
         "name": "Rybka.exe",
         "country": "ru",
         "vm_name": "wine"},
    ]


def md5_sum(filename):
    with open(filename, mode='rb') as file_handle:
        md5sum = md5()
        for buf in iter(partial(file_handle.read, 4096), b''):
            md5sum.update(buf)
    return md5sum.hexdigest()


class EngineDiscoverer(GObject.GObject):

    __gsignals__ = {
        "discovering_started": (GObject.SignalFlags.RUN_FIRST, None,
                                (object, )),
        "engine_discovered": (GObject.SignalFlags.RUN_FIRST, None,
                              (str, object)),
        "engine_failed": (GObject.SignalFlags.RUN_FIRST, None, (str, object)),
        "all_engines_discovered": (GObject.SignalFlags.RUN_FIRST, None, ()),
    }

    def __init__(self):
        GObject.GObject.__init__(self)
        self.engines = []
        self.jsonpath = addUserConfigPrefix("engines.json")
        try:
            self._engines = json.load(open(self.jsonpath))
        except ValueError as err:
            log.warning(
                "engineNest: Couldn\'t read engines.json, renamed it to .bak\n%s\n%s"
                % (self.jsonpath, err))
            os.rename(self.jsonpath, self.jsonpath + ".bak")
            self._engines = deepcopy(backup)
        except IOError as err:
            log.info(
                "engineNest: Couldn\'t open engines.json, creating a new.\n%s"
                % err)
            self._engines = deepcopy(backup)

        # Try to detect engines shipping .eng files on Linux (suggested by HGM on talkcess.com forum)
        for protocol in ("xboard", "uci"):
            for path in ("/usr/local/share/games/plugins",
                         "/usr/share/games/plugins"):
                path = os.path.join(path, protocol)
                if os.path.isdir(path):
                    for entry in os.listdir(path):
                        ext = os.path.splitext(entry)[1]
                        if ext == ".eng":
                            with
 open(os.path.join(path, entry)) as file_handle:
                                engine_command = file_handle.readline().strip()
                                new_engine = {}
                                if engine_command.startswith(
                                        "cd ") and engine_command.find(
            
                                ";") > 0:
                                    parts = engine_command.split(";")
                                    working_directory = parts[0][3:]
                                    engine_command = parts[1]
                                    new_engine[
                                        "workingDirectory"] = working_directory

                                find = False
                                for engine in self._engines:
                                    if engine["name"] == engine_command:
                                        find = True
                                        break
                                if not find:
                                    new_engine["protocol"] = protocol
                                    new_engine["name"] = engine_command
                                    self._engines.append(new_engine)

                                    ############################################################################
                                    # Discover methods                                                         #
                                    ############################################################################

    def __findRundata(self, engine):
        """ Searches for a readable, executable named 'name' in the PATH.
            For the PyChess engine, special handling is taken, and we search
            PYTHONPATH as well as the directory from where the 'os' module is
            imported """
        if engine.get("vm_name") is not None:
            altpath = engine.get("vm_command")
            if getattr(sys, 'frozen', False) and engine["vm_name"] == "wine":
                vmpath =