# Copyright (c) 2016 Jamie Bull
# =======================================================================
#  Distributed under the MIT License.
#  (See accompanying file LICENSE or copy at
#  http://opensource.org/licenses/MIT)
# =======================================================================
"""Run functions for EnergyPlus.
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import os
import platform
import pydoc
import shutil
from subprocess import CalledProcessError, check_call
import sys
import tempfile

try:
    import multiprocessing as mp
except ImportError:
    pass


def install_paths(version=None, iddname=None):
    """Get the install paths for EnergyPlus executable and weather files.

    We prefer to get the install path from the IDD name but fall back to
    getting it from the version number for backwards compatibility and to
    simplify tests.

    Parameters
    ----------
    version : str, optional
        EnergyPlus version in the format "X-X-X", e.g. "8-7-0".
    iddname : str, optional
        File path to the IDD.

    Returns
    -------
    eplus_exe : str
        Full path to the EnergyPlus executable.
    eplus_weather : str
        Full path to the EnergyPlus weather directory.

    """
    try:
        eplus_exe, eplus_home = paths_from_iddname(iddname)
    except (AttributeError, TypeError, ValueError):
        eplus_exe, eplus_home = paths_from_version(version)
    eplus_weather = os.path.join(eplus_home, 'WeatherData')

    return eplus_exe, eplus_weather


def paths_from_iddname(iddname):
    """Get the EnergyPlus install directory and executable path.

    Parameters
    ----------
    iddname : str, optional
        File path to the IDD.

    Returns
    -------
    eplus_exe : str
        Full path to the EnergyPlus executable.
    eplus_home : str
        Full path to the EnergyPlus install directory.

    Raises
    ------
    AttributeError (TypeError on Windows)
        If iddname does not have a directory component (e.g. if None).
    ValueError
        If eplus_exe is not a file.

    """
    eplus_home = os.path.abspath(os.path.dirname(iddname))
    if platform.system() == 'Windows':
        eplus_exe = os.path.join(eplus_home, 'energyplus.exe')
    elif platform.system() == "Linux":
        eplus_exe = os.path.join(eplus_home, 'energyplus')
    else:
        eplus_exe = os.path.join(eplus_home, 'energyplus')
    if not os.path.isfile(eplus_exe):
        raise ValueError
    return eplus_exe, eplus_home


def paths_from_version(version):
    """Get the EnergyPlus install directory and executable path.

    Parameters
    ----------
    version : str, optional
        EnergyPlus version in the format "X-X-X", e.g. "8-7-0".

    Returns
    -------
    eplus_exe : str
        Full path to the EnergyPlus executable.
    eplus_home : str
        Full path to the EnergyPlus install directory.

    """
    if platform.system() == 'Windows':
        eplus_home = "C:/EnergyPlusV{version}".format(version=version)
        eplus_exe = os.path.join(eplus_home, 'energyplus.exe')
    elif platform.system() == "Linux":
        eplus_home = "/usr/local/EnergyPlus-{version}".format(version=version)
        eplus_exe = os.path.join(eplus_home, 'energyplus')
    else:
        eplus_home = "/Applications/EnergyPlus-{version}".format(version=version)
        eplus_exe = os.path.join(eplus_home, 'energyplus')
    return eplus_exe, eplus_home


def wrapped_help_text(wrapped_func):
    """Decorator to pass through the documentation from a wrapped function.
    """
    def decorator(wrapper_func):
        """The decorator.

        Parameters
        ----------
        f : callable
            The wrapped function.

        """
        wrapper_func.__doc__ = ('This method wraps the following method:\n\n' +
                                pydoc.text.document(wrapped_func))
        return wrapper_func
    return decorator


def runIDFs(jobs, processors=1):
    """Wrapper for run() to be used when running IDF5 runs in parallel.

    Parameters
    ----------
    jobs : iterable
        A list or generator made up of an IDF5 object and a kwargs dict
        (see `run_functions.run` for valid keywords).
    processors : int, optional
        Number of processors to run on (default: 1). If 0 is passed then
        the process will run on all CPUs, -1 means one less than all CPUs, etc.

    """
    if processors <= 0:
        processors = max(1, mp.cpu_count() - processors)

    shutil.rmtree("multi_runs", ignore_errors=True)
    os.mkdir("multi_runs")

    prepared_runs = (prepare_run(run_id, run_data) for run_id, run_data in enumerate(jobs))
    try:
        pool = mp.Pool(processors)
        pool.map(multirunner, prepared_runs)
        pool.close()
    except NameError:
        # multiprocessing not present so pass the jobs one at a time
        for job in prepared_runs:
            multirunner([job])
    shutil.rmtree("multi_runs", ignore_errors=True)


def prepare_run(run_id, run_data):
    """Prepare run inputs for one of multiple EnergyPlus runs.

    :param run_id: An ID number for naming the IDF.
    :param run_data: Tuple of the IDF and keyword args to pass to EnergyPlus executable.
    :return: Tuple of the IDF path and EPW, and the keyword args.
    """
    idf, kwargs = run_data
    epw = idf.epw
    idf_dir = os.path.join('multi_runs', 'idf_%i' % run_id)
    os.mkdir(idf_dir)
    idf_path = os.path.join(idf_dir, 'in.idf')
    idf.saveas(idf_path)
    return (idf_path, epw), kwargs


def multirunner(args):
    """Wrapper for run() to be used when running IDF and EPW runs in parallel.

    Parameters
    ----------
    args : list
        A list made up of a two-item list (IDF and EPW) and a kwargs dict.

    """
    run(*args[0], **args[1])


def run(idf=None, weather=None, output_directory='', annual=False,
        design_day=False, idd=None, epmacro=False, expandobjects=False,
        readvars=False, output_prefix=None, output_suffix=None, version=False,
        verbose='v', ep_version=None):
    """
    Wrapper around the EnergyPlus command line interface.

    Parameters
    ----------
    idf : str