import fnmatch, os, sys, tempfile, shutil, webbrowser

join = os.path.join
norm = os.path.normpath

from pymxs import runtime as rt

sys.path.append(os.path.dirname(os.path.abspath(__file__)))

# append the path to the maxcpp module
ver = rt.maxVersion()[0]
if ver <= 26000: libDir = '2024'
elif ver == 27000: libDir = '2025'
elif ver == 28000: libDir = '2026'
else: libDir = '2027'
sys.path.append(join(os.path.dirname(os.path.abspath(__file__)), libDir))

import importlib

if 'utils' in locals():
    importlib.reload(utils)
if 'extract' in locals():
    importlib.reload(extract)
if 'collect' in locals():
    importlib.reload(collect)
if 'generate' in locals():
    importlib.reload(generate)
if 'maxUtils' in locals():
    importlib.reload(maxUtils)
if 'progressDialog' in locals():
    importlib.reload(progressDialog)

import pluginUtils
from pluginUtils.manager import AppManagerConn
from pluginUtils.path import getAppManagerHost, getRoot, findExportedAssetPath

log = pluginUtils.log.getLogger('V3D-MX')

import collect
import extract
import generate
import progressDialog
import maxUtils
import utils

extractCustomProp = extract.extractCustomProp

_trackBarExportState = {
    'playbackRange': rt.interval(0, 100),
    'playbackTime': 0,
    'setKeyMode': False
}

# prevent export path reset during importlib.reload()
try:
    _currentMaxPath
    _defaultExportPath
except:
    _currentMaxPath = ''
    _defaultExportPath = ''

def setDefaultExportPath(path):
    global _defaultExportPath

    _defaultExportPath = os.path.splitext(path)[0]

def getDefaultExportPath():
    global _currentMaxPath, _defaultExportPath

    maxPath = rt.maxFilePath + rt.maxFileName

    # return cached version
    if _defaultExportPath and maxPath == _currentMaxPath:
        return _defaultExportPath

    _currentMaxPath = maxPath

    return os.path.splitext(maxPath)[0]

def exportGLTF():

    filename = getDefaultExportPath().replace('\\', '\\\\')

    filepath = rt.getSaveFileName(caption='Export glTF:', filename='{}'.format(filename),
            types='glTF file (*.gltf)|*.gltf|glTF binary file (*.glb)|*.glb|')

    if filepath is not None:
        exportGLTFPath(filepath)
        setDefaultExportPath(filepath)

def exportGLTFPath(filepath, sneakPeek=False):

    progressDialog.ProgressDialog.open(title='Exporting GLTF...')

    name, ext = os.path.splitext(os.path.basename(filepath))

    if ext == '.glb':
        gltfFormat = 'BINARY'
    elif ext == '.html':
        gltfFormat = 'HTML'
    else:
        gltfFormat = 'ASCII'

    filepathBin = name + '.bin'
    filedir = os.path.dirname(filepath) + '/'

    log.info('Exporting glTF 2.0 asset (' + gltfFormat + ')')

    tmpDir = tempfile.mkdtemp(suffix='verge3d')

    exportPrepare()

    exportSettings = {
        'anim_playback_original_from': _trackBarExportState['playbackRange'].start,
        'anim_playback_original_to': _trackBarExportState['playbackRange'].end,
        'format' : gltfFormat,
        'binary' : bytearray(),
        'filepath' : filepath,
        'binaryfilename' : filepathBin,
        'filedirectory': filedir,
        'strip' : True,
        'uri_cache' : { 'uri': [], 'obj': [] },
        'tmp_dir': tmpDir,
        'sneak_peek': sneakPeek,
        'skinned_mesh_use_aux_bone': True
    }

    try:
        gltf = generate.GLTF(exportSettings)
        collector = collect.Collector(exportSettings)
        collector.collect()

        gltf.generate(collector)
        gltf.save(collector)

        shutil.rmtree(tmpDir)
    except:
        exportCleanup(collector)
        raise
    else:
        exportCleanup(collector)

def exportPrepare():

    animExport = extractCustomProp(rt.rootNode, 'V3DExportSettingsData', 'animExport', True)
    animUsePlaybackRange = extractCustomProp(rt.rootNode, 'V3DExportSettingsData', 'animUsePlaybackRange', False)

    animRange = rt.animationRange

    # create new, do not use reference
    _trackBarExportState['playbackRange'] = rt.interval(animRange.start, animRange.end)

    # convert MXSWrapperBase to float
    _trackBarExportState['playbackTime'] = float(rt.sliderTime)

    _trackBarExportState['setKeyMode'] = rt.animButtonState

    if _trackBarExportState['setKeyMode']:
        rt.animButtonState = False

    if animExport and not animUsePlaybackRange:
        maxUtils.disableViewportRedraw()
        # use the 0 frame for retreiving objects' state, otherwise the current
        # frame will be used
        rt.animationRange = rt.interval(0, 100)
        rt.sliderTime = 0

def exportCleanup(collector=None):

    animExport = extractCustomProp(rt.rootNode, 'V3DExportSettingsData', 'animExport', True)
    animUsePlaybackRange = extractCustomProp(rt.rootNode, 'V3DExportSettingsData', 'animUsePlaybackRange', False)

    if animExport and not animUsePlaybackRange:
        rt.animationRange = _trackBarExportState['playbackRange']
        rt.sliderTime = _trackBarExportState['playbackTime']
        maxUtils.enableViewportRedraw()

    if _trackBarExportState['setKeyMode']:
         rt.animButtonState = True

    if collector:
        for mTex in collector.textures:
            if hasattr(mTex, 'compressionErrorStatus'):
                del mTex.compressionErrorStatus

    progressDialog.ProgressDialog.close()

def reportMaxOpenError(maxpath, maxpathRel):
    maxVer = maxUtils.maxVersionHuman(maxUtils.getMaxVersion())
    fileVer = maxUtils.maxVersionHuman(rt.getMaxFileVersionData(maxpath)[0])
    log.error(f'Failed to open "{maxpathRel}". Using 3ds Max {maxVer}, file saved in {fileVer}')

def updateCopyrightSetting():
    if hasattr(rt.rootNode, 'V3DExportSettingsData'):
        rt.rootNode.V3DExportSettingsData.copyright = pluginUtils.copyrightLine
    else:
        log.warning('Unable to update copyright, root node has no V3DExportSettingsData')

def reexportAll(folder, resaveMax, updateCopyright, forceGLB):
    apps = join(getRoot(), folder)

    for root, dirs, files in os.walk(apps):
        for name in files:
            if fnmatch.fnmatch(name, '*.max'):
                maxpath = norm(join(root, name))
                maxpathRel = os.path.relpath(maxpath, getRoot())

                gltfpath = findExportedAssetPath(maxpath)
                if gltfpath:
                    if forceGLB:
                        gltfpath = os.path.splitext(gltfpath)[0] + '.glb'

                    log.info(f'Reexporting "{maxpathRel}"')
                    if rt.loadMaxFile(maxpath, useFileUnits=True, quiet=True):
                        if updateCopyright:
                            updateCopyrightSetting()
                        exportGLTFPath(gltfpath)
                        if resaveMax:
                            log.info(f'Resaving "{maxpathRel}"')
                            rt.saveMaxFile(maxpath)
                    else:
                        reportMaxOpenError(maxpath, maxpathRel)
                elif resaveMax:
                    # no exported asset found, just resave Max file
                    if rt.loadMaxFile(maxpath, useFileUnits=True, quiet=True):
                        if updateCopyright:
                            updateCopyrightSetting()
                        log.info(f'Resaving "{maxpathRel}"')
                        rt.saveMaxFile(maxpath)
                    else:
                        reportMaxOpenError(maxpath, maxpathRel)

def execBrowser(url):
    try:
        webbrowser.open(url)
    except BaseException:
        log.error('Failed to open URL: ' + url)

def runAppManager():
    if not AppManagerConn.ping():
        AppManagerConn.start()
    execBrowser(getAppManagerHost('MAX'))

def runUserManual():
    execBrowser(AppManagerConn.getManualURL())

def sneakPeek():
    if not AppManagerConn.ping():
        AppManagerConn.start()

    try:
        prevDir = AppManagerConn.getPreviewDir(True)
    except (PermissionError, FileExistsError):
        rt.messageBox('Permission denied when attempting to access the preview folder.\n\n'
                'Make sure that you\'re not running the App Manager under different user.',
                title='Sneak Peek export failed', icon=rt.name('critical'))
        return

    exportGLTFPath(join(prevDir, 'sneak_peek.gltf'), sneakPeek=True)

    execBrowser(getAppManagerHost('MAX') +
            'player/player.html?load=/sneak_peek/sneak_peek.gltf')

def init():
    log.info('Initialize Verge3D plugin')

    AppManagerConn.init(getRoot(), 'MAX')

    if not AppManagerConn.ping():
        AppManagerConn.start()
