
import re
import sys
import os
import os.path as osp
from fnmatch import fnmatch

from pytd.gui.dialogs import promptDialog
from pytd.util.logutils import logMsg
from pytd.util.sysutils import importModule, toStr, inDevMode, getCaller
from pytd.util.fsutils import pathSplitDirs, pathResolve, pathNorm, pathJoin
from pytd.util.fsutils import jsonRead, jsonWrite, isDirStat, parseDirContent

from pytd.util.strutils import padded


_VERS_SPLIT_REXP = re.compile(r'-(v[0-9]+)')

def getConfigModule(sProjectName):

    try:
        sConfPkg = os.environ.get("DAVOS_CONF_PACKAGE", "davos.config")
        sConfigModule = sConfPkg + '.' + sProjectName
        modobj = importModule(sConfigModule)
    except ImportError:
        raise ImportError("No config module named '{}'".format(sConfigModule))

    return modobj

def versionFromName(sFileName):
    vers = _VERS_SPLIT_REXP.findall(sFileName)
    return int(vers[-1].strip('v')) if vers else None

def mkVersionSuffix(v):

    if not isinstance(v, int):
        raise TypeError("argument must be of type <int>. Got {}.".format(type(v)))

    return "".join(('-v', padded(v)))

def findVersionFields(s):
    return _VERS_SPLIT_REXP.findall(s)

def promptForComment(**kwargs):

    sComment = ""
    bOk = False

    result = promptDialog(title='Please...',
                          message='Leave a comment: ',
                          button=['OK', 'Cancel'],
                          defaultButton='OK',
                          cancelButton='Cancel',
                          dismissString='Cancel',
                          scrollableField=True,
                          **kwargs)

    if result == 'Cancel':
        logMsg("Cancelled !" , warning=True)
    elif result == 'OK':
        sComment = promptDialog(query=True, text=True)
        bOk = True

    return sComment, bOk

def projectNameFromPath(p):

    sConfPkg = os.environ.get("DAVOS_CONF_PACKAGE", "davos.config")
    pkg = importModule(sConfPkg)
    sPkgDirPath = os.path.dirname(pkg.__file__)

    sDirList = pathSplitDirs(p)

    for sFilename in os.listdir(sPkgDirPath):

        bIgnored = False
        for sPatrn in ("__*", ".*", "*.pyc"):
            if fnmatch(sFilename, sPatrn):
                bIgnored = True
                break

        if bIgnored:
            continue

        sModName = os.path.splitext(sFilename)[0]
        m = importModule(sConfPkg + '.' + sModName)

        sProjDir = m.project.dir_name
        if sProjDir in sDirList:
            return sModName

    return ""

def splitStep(sTaskName):
    return sTaskName.rsplit("|", 1) if ("|" in sTaskName) else ("", sTaskName)

def damasServerPort():
    return os.getenv("DAMAS_DEV_PORT", "8443") if inDevMode() else "8443"

def loadPrefs():

    global DAVOS_PREFS

    try:
        p = pathResolve(r"%USERPROFILE%\davos_prefs.json")
        DAVOS_PREFS = jsonRead(p)
    except EnvironmentError:
        DAVOS_PREFS = {}

    return DAVOS_PREFS

def savePrefs():

    global DAVOS_PREFS

    if DAVOS_PREFS:
        p = pathResolve(r"%USERPROFILE%\davos_prefs.json")
        jsonWrite(p, DAVOS_PREFS)

def setPref(in_sKey, value):

    global DAVOS_PREFS

    if "|" not in in_sKey:
        DAVOS_PREFS[in_sKey] = value
        return

    sKeyList = in_sKey.split("|")
    iLastKey = len(sKeyList) - 1
    currPrefs = DAVOS_PREFS
    sPrevKey = ""
    prevPrefs = None
    for i, sKey in enumerate(sKeyList):

        if not isinstance(currPrefs, dict):
            prevPrefs[sPrevKey] = {}
            currPrefs = prevPrefs[sPrevKey]

        if i == iLastKey:
            currPrefs[sKey] = value
            return

        if sKey not in currPrefs:
            currPrefs[sKey] = {}

        prevPrefs = currPrefs
        sPrevKey = sKey
        currPrefs = currPrefs[sKey]

def getPref(in_sKey, default=None):

    global DAVOS_PREFS

    if "|" not in in_sKey:
        return DAVOS_PREFS.get(in_sKey, default)

    sKeyList = in_sKey.split("|")
    iLastKey = len(sKeyList) - 1
    currPrefs = DAVOS_PREFS
    for i, sKey in enumerate(sKeyList):

        if not isinstance(currPrefs, dict):
            k = "|".join(sKeyList[:(i + 1)])
            logMsg("Not a pref dictionary: '{}'.".format(k), warning=True)
            return default

        if i == iLastKey:
            return currPrefs.get(sKey, default)

        if sKey in currPrefs:
            currPrefs = currPrefs[sKey]
        else:
            logMsg("No such pref: '{}'.".format(in_sKey), warning=True)
            return default


_ICON_DIR_PATH = ""

def mkIconPath(sRelPath):

    global _ICON_DIR_PATH

    if (not _ICON_DIR_PATH) or (not osp.exists(_ICON_DIR_PATH)):
        p = sys.modules["davos"].__file__
        p = osp.abspath(osp.join(osp.dirname(p), "..", "resources", "icon"))
        _ICON_DIR_PATH = p

    return pathJoin(_ICON_DIR_PATH, sRelPath)

def writePackContent(sPackDirPath, dirStat=None):

    sPackDirPath = pathNorm(sPackDirPath)

    if not dirStat:
        dirStat = os.stat(sPackDirPath)

    sJsonPath = mkPackFilePath(sPackDirPath)

    iMtime = 0
    if not osp.exists(sJsonPath):
        iMtime = dirStat.st_mtime
        iAtime = dirStat.st_atime

    try:
        open(sJsonPath, 'a+b').close() # create json file so it is listed by parseDirContent()
        dirContent = parseDirContent(sPackDirPath)
        jsonWrite(sJsonPath, dirContent, sort_keys=True)
    finally:
        if iMtime:
            os.utime(sPackDirPath, (iAtime, iMtime))

    return dirContent

def readPackContent(sPackDirPath, fail=True):
    try:
        dirContent = jsonRead(mkPackFilePath(sPackDirPath))
    except EnvironmentError as e:
        if fail:
            raise
        logMsg(toStr(e), warning=True)
        dirContent = parseDirContent(sPackDirPath)

    return dirContent

def mkPackFilePath(sPackDirPath):
    return pathJoin(sPackDirPath, "_package.json")

_ISPACK_REXP = re.compile(r".+_pkg[^/\w].+", re.I)

def assertPack(p, dirStat=None):

    if not dirStat:
        dirStat = os.stat(pathNorm(p))

    if isPack(p, fail=True, dirStat=dirStat):
        return dirStat

    return None

def belowPack(p):

    p = pathNorm(p)

    if os.environ["IN_SEB_MODE"]:
        return True if _belowPack(p) else _belowOldPack(p)
    else:
        return _belowPack(p)

def isPack(p, fail=False, dirStat=None):

    p = pathNorm(p)

    if os.environ["IN_SEB_MODE"]:
        bPackPath = True if _isPack(p) else _isOldPack(p)
    else:
        bPackPath = _isPack(p)

    if not bPackPath:
        if fail:
            sMsg = ("Directory NOT a package (should start with 'pkg_' or 'lyr_'): '{}'."
                    .format(osp.basename(p)))
            raise EnvironmentError(sMsg)
        else:
            return False

    if dirStat and not isDirStat(dirStat):
        if fail:
            raise EnvironmentError("Package path NOT a directory: '{}'".format(p))
        else:
            return False

    return True

def _belowPack(p):

    p = osp.dirname(p)
    for sDirName in pathSplitDirs(p):
        if _isPack(sDirName):
            return True

    return False

def _isPack(p):

    sBaseName = osp.basename(p) if "/" in p else p

    if "_" not in sBaseName:
        return False

    sPrefix = sBaseName.split("_", 1)[0]
    if not sPrefix:
        return False

    return (sPrefix.lower() + "_") in ("pkg_", "lyr_")

def _belowOldPack(p):

    p = osp.dirname(p)
    if "_pkg/" in p.lower():
        return True

    if _ISPACK_REXP.match(p):
        return True

    return False

def _isOldPack(p):

    sName = osp.basename(p)
    if sName.lower().endswith("_pkg"):
        return True

    if _ISPACK_REXP.match(sName):
        return True

    return False
