from .geometry import ComGeometry
from ._commandline import CommandLine
from typing import List

class GaussianCom(object):
    """
    Object storing the info about a gaussian .com file

    Parameters
    ----------

    filename : str or None (optional)
        The filename of the com file. If None an empty com file will
        be generated. Default None
    """

    def __init__(self, filename=None):
        self._gaussianconfig = GaussianConfig()
        self._commandline = None
        self._title = None
        self._com_geometry = None
        self._extralines = []
        self._filename = filename

        if filename is None:
            self._commandline = CommandLine()
            self._title = "Generated by nagausspy"
            self._com_geometry = ComGeometry(iter([""]))

        else:
            self._load_file()

    def _load_file(self):
        with open(self._filename, "r") as openfile:
            self._load_config_and_command(openfile)

            self._title = next(openfile).strip()

            # Skip an extra blank line
            _ = next(openfile)

            # Load the charge and the multiplicity
            line = next(openfile)
            charge, multiplicity = [int(i) for i in line.split()]

            self._com_geometry = ComGeometry(openfile, charge=charge,
                                             multiplicity=multiplicity)

            self._load_extra_lines(openfile)

    def _load_extra_lines(self, openfile):
        for line in openfile:
            self._extralines.append(line.strip())
        # Remove blank lines at the end
        while (self.extralines) and (not self._extralines[-1]):
            self._extralines.pop()


    def _load_config_and_command(self, openfile):
        ###############
        # LOAD CONFIG #
        ###############
        line = next(openfile).strip()
        while line.startswith("%"):
            line = line[1:]
            key, value = line.split("=")
            self._gaussianconfig[key] = value
            line = next(openfile).strip()

        ####################
        # LOAD COMMANDLINE #
        ####################
        while not line.startswith("#"):
            line = next(openfile).strip()
        if not line.startswith("#"):
            text = "Gaussian Command line not found after the last config line"
            raise RuntimeError(text)
        line = line[1:].strip()
        commandlines = []
        while line:
            commandlines.append(line)
            line = next(openfile).strip()
        self._commandline = CommandLine(" ".join(commandlines))

    def save(self, filename: str):
        """
        Saves the com object into a file

        Parameters
        ----------

        filename : str
            File where the com object will be written

        """

        with open(filename, "w") as openfile:
            openfile.write(str(self.configuration)+"\n")
            openfile.write(str(self.commandline)+"\n")
            openfile.write("\n")
            openfile.write(self.title+"\n")
            openfile.write("\n")
            openfile.write(str(self.geometry)+"\n")
            openfile.write("\n")

            if self.extralines:
                openfile.write("\n".join(self.extralines))
                openfile.write("\n\n")

    @property
    def configuration(self) -> 'GaussianConfig':
        """
        Returns the lines of the cofniguration (the ones that stat with %)
        """
        return self._gaussianconfig

    @configuration.setter
    def configuration(self, value: 'GaussianConfig'):
        self._gaussianconfig = value

    @property
    def commandline(self) -> CommandLine:
        """
        Commandline object of the cm file
        """
        return self._commandline

    @commandline.setter
    def commandline(self, value: CommandLine):
        self._commandline = value

    @property
    def title(self) -> str:
        """
        Title of the gaussian job
        """
        return self._title

    @title.setter
    def title(self, value: str):
        self._title = value

    @property
    def geometry(self) -> ComGeometry:
        """
        The geometry stored on the file
        """
        return self._com_geometry

    @geometry.setter
    def geometry(self, value: ComGeometry):
        self._com_geometry = value

    @property
    def extralines(self) -> List[str]:
        """
        List with the extra configuration lines after the geometry
        """
        return self._extralines

    @extralines.setter
    def extralines(self, value: List[str]):
        self._extralines = value

    @property
    def charge(self) -> int:
        """
        Charge of the geometry defined in the file
        """
        return self.geometry.charge

    @charge.setter
    def charge(self, value: int):
        self.geometry.charge = value

    @property
    def multiplicity(self) -> int:
        """
        Multiplicity of the geometry defined in the file
        """
        return self.geometry.multiplicity

    @multiplicity.setter
    def multiplicity(self, value: int):
        self.geometry.multiplicity = value


class GaussianConfig(dict):
    """
    Object storing the configuration for the gaussian execution
    """

    def __repr__(self):
        options = ["%{}={}".format(key, value) for key, value in self.items()]
        return "\n".join(options)
    __str__ = __repr__
