#!/usr/bin/env python3
import os, glob, sys, inspect, shutil, subprocess


def loadNEATCorrectPath():
    """
    Loads neat from a custom paths list where the current working directory
    only occurs as the final path, so that neat is first loaded from the install
    path and only from the current path if we are working in development mode.

    Required to run `compilechannels default` from package root directory after
    installation via `setup.py`
    """
    # get new paths list without the current working directory
    current_path = os.path.realpath(os.getcwd())
    paths = [path for path in sys.path if os.path.realpath(path) != current_path]
    paths += [current_path]

    # load module with importlib according to
    # https://stackoverflow.com/questions/35288021/what-is-the-equivalent-of-imp-find-module-in-importlib
    # and to
    # https://import-sig.python.narkive.com/JW8eIKSg/how-best-to-replace-imp-load-module
    import importlib
    neatspec = importlib.machinery.PathFinder().find_spec("neat", paths)
    neat = neatspec.loader.load_module()
    neatspec.loader.exec_module(neat)

    return neat

neat = loadNEATCorrectPath()
from neat import IonChannel

path_neat = neat.__path__[0]
path_for_mod_files = os.path.join(path_neat, 'tools/simtools/neuron/mech/')
path_for_compilation = os.path.join(path_neat, 'tools/simtools/neuron/')


def resetDefaultMech():
    """
    Create the "mech/" directory in a clean state with only the default
    mechanisms and channels
    """
    path_default_mech = os.path.join(path_neat, 'tools/simtools/neuron/mech_storage/')

    # recreate the "mech/" directory in a clean state
    if os.path.exists(path_for_mod_files):
        shutil.rmtree(path_for_mod_files)

    # copy default mechanisms
    shutil.copytree(path_default_mech, path_for_mod_files)

if sys.argv[1] == 'default':
    resetDefaultMech()
    path_for_channels = os.path.join(path_neat, 'channels/channelcollection/')
else:
    resetDefaultMech()
    path_for_channels = sys.argv[1]

print('--- writing channels from \n' + path_for_channels + '\nto \n' + path_for_mod_files)


def allBaseClasses(cls):
    """
    Return list get all base classes from a given class
    """
    return [cls.__base__] + allBaseClasses(cls.__base__) if cls is not None else []


if not os.path.exists(path_for_mod_files):
        os.makedirs(path_for_mod_files)

sys.path.insert(0, path_for_channels)
for channel_module in glob.glob(os.path.join(path_for_channels, '*.py')):
    # import channel modules
    # convert names from glob to something susceptible to python import
    channel_module = os.path.split(channel_module)[1]
    channel_module = channel_module.replace('.py', '')
    print('Reading channels from:', channel_module)
    exec('import ' + channel_module + ' as chans')

    for name, obj in inspect.getmembers(chans):
        # if an object is a class and inheriting from IonChannel, write its mod-file
        if inspect.isclass(obj) and IonChannel in allBaseClasses(obj):
            chan = obj()
            print(' - write .mod file for:', chan.__class__.__name__)
            chan.writeModFile(path_for_mod_files)

# copy the mod-files within the source directory to the compile directory
for mod_file in glob.glob(os.path.join(path_for_channels, '*.mod')):
    shutil.copy2(mod_file, path_for_mod_files)

# change to directory where 'mech/' folder is located and compile the mechanisms
os.chdir(path_for_compilation)
if os.path.exists("x86_64/"):  # delete old compiled files if exist
    shutil.rmtree("x86_64/")
subprocess.call(["nrnivmodl", "mech/"])  # compile all mod files
