from numbers import Integral
from pathlib import Path
import subprocess
from typing import Iterable
import warnings

import pygriffin.checkvalue as cv
from pygriffin.config import PyGriffinConfig

class PyGriffin:
    """
    Class for connecting PyGriffin inputs and running the problems

    Parameters
    ----------
    input : str or Path object
        Location of Griffin input file
    n_procs : int
        Number of processes in Griffin run
    """

    def __init__(self, input, mesh=None, n_procs=1):
        self.config = PyGriffinConfig()
        self.input = input
        self.n_procs = n_procs

    def run(self, cwd=None, other_args=None):
        """
        Execute Griffin

        Parameters
        ----------
        cwd : str or Path
            Directory from which to run Griffin

        Returns
        -------
        int
            Errorcode of the Griffin run (0 is success)
        """

        cmd = []

        if self.n_procs > 1:
            if self.config.mpi_enabled:
                cmd += ['mpirun', '-n', str(self.n_procs)]
            else:
                msg = ("MPI is not enabled for this Griffin executable. "
                       "The n_procs argument will be ignored.")
                warnings.warn(msg)

        # add griffin executable to command line
        cmd.append(str(self.config.griffin_exec))

        cmd += ['-i', str(self.input)]

        if cwd is not None:
            cwd = Path(cwd)
        else:
            cwd = Path('.')

        if cwd and not cwd.is_dir():
            msg = ("Specified working directory "
            "doesn't exist: {}".format(cwd))
            raise ValueError(msg)

        if other_args is not None:
            cv.check_type('Additional Griffin arguments', other_args, Iterable)
            cmd += other_args

        p = subprocess.Popen(cmd, universal_newlines=True, cwd=str(cwd))

        return p.wait()

    def __call__(self, cwd=None):
        return self.run(cwd)

    @property
    def input(self):
        return self._input

    @input.setter
    def input(self, input):
        cv.check_type('Griffin input', input, (str, Path))
        self._input = Path(input).absolute()

    @property
    def mesh(self):
        return self._mesh

    @mesh.setter
    def mesh(self, mesh):
        cv.check_type('Griffin mesh', mesh, (str, Path))

    @property
    def xs(self):
        return self._xs

    @xs.setter
    def xs(self, xs):
        cv.check_type('Cross section file', xs, (str, Path))
        self._xs = xs

    @property
    def n_procs(self):
        return self._n_procs

    @n_procs.setter
    def n_procs(self, procs):
        cv.check_type('Griffin processes', procs, Integral)
        cv.check_greater_than('Griffin processes', procs, 0)
        self._n_procs = procs