# pylint: disable=C0103,E0401
"""
Synbiopython (c) Global BioFoundry Alliance 2020

Synbiopython is licensed under the MIT License.

This module is to generate SEDML and COMBINE OMEX files.
"""

import os
import re
import tempfile
import io
import zipfile
import requests
import tellurium as te
import matplotlib.pyplot as plt
import phrasedml
from synbiopython.genbabel import utilities

te.setDefaultPlottingEngine("matplotlib")

plt.close("all")

plt.rcdefaults()
plt.rcParams["font.family"] = "Arial"
plt.rcParams["font.weight"] = "normal"
plt.rcParams["font.size"] = 16
plt.rcParams["axes.labelsize"] = 16
plt.rcParams["axes.labelweight"] = "normal"
plt.rcParams["axes.linewidth"] = 2
plt.rcParams["axes.formatter.limits"] = -3, 3
plt.rcParams["legend.frameon"] = False
params = {"mathtext.default": "regular"}
plt.rcParams.update(params)
plt.rcParams.update({"axes.spines.top": False, "axes.spines.right": False})


class SEDMLOMEXgen:
    """Class to generate the SEDML and COMBINE OMEX files."""

    # get current working directory
    def __init__(self):
        self.workingdir0 = os.getcwd()

    @staticmethod
    def get_sbml_biomodel(Biomodels_ID, **kwargs):
        """Get SBML model from biomodel. Use outputfile keyword argument to
        export the SBML model into .xml file at the specific path.
        A default .xml file will be generated by default at the temp directory.

        :param Biomodels_ID: the ID for the Biomodels
        :type Biomodels_ID: str
        :return: the sbml in string format
        :rtype: str
        :Keyword Arguments:
            * *outputfile*: specify outputfile
        """

        temppath = tempfile.gettempdir()

        url = "https://www.ebi.ac.uk/biomodels/search/download?models=" + Biomodels_ID
        r = requests.get(url, stream=True)
        z = zipfile.ZipFile(io.BytesIO(r.content))
        z.extractall(temppath)

        filepath = os.path.join(temppath, Biomodels_ID + ".xml")

        with open(filepath, "r") as f:
            biomodelsbml_str = f.read()

        for key, value in kwargs.items():
            if "outputfile" in key:
                filepath = value
                with open(filepath, "wb") as f:
                    f.write(biomodelsbml_str.encode("utf-8"))

        print("The SBML file path: ", filepath)

        return biomodelsbml_str

    @staticmethod
    def sbmltoantimony(sbmlfile):
        """Get the sbml file and return the antimony string."""

        antimony_str = te.sbmlToAntimony(sbmlfile)
        basename = os.path.basename(sbmlfile)
        sbmlfilename = basename.split(".")[0]
        antimony_str = antimony_str.replace("doc0", sbmlfilename)

        return antimony_str

    @staticmethod
    def find_between(s, first, last):
        """Get the substring from string based on indexes.

        :param s: string to be searched
        :type s: str
        :param first: part of the string at the front
        :type first: str
        :param last: part of the string at the end
        :type last: str
        """

        try:
            start = s.index(first) + len(first)
            end = s.index(last, start)
            return s[start:end]
        except ValueError:
            return ""

    def export_omex(self, antimony_str, phrasedml_str, **kwargs):
        """Generate COMBINE OMEX file.

        :param antimony_str: represent the SBML
        :type antimony_str: str
        :param phrasedml_str: represent the SEDML
        :type phrasedml_str: str
        :return: omex inline
        :rtype: str
        :Keyword Arguments:
            * *outputfile*: specify outputfile
        """

        model = re.search("model (.*)\n", antimony_str)

        if model.group(1)[0] == "*":
            model = self.find_between(model.group(1), "*", "()")
            phrasedml_str = phrasedml_str.format(model)
        else:
            phrasedml_str = phrasedml_str.format(model.group(1))

        inline_omex = "\n".join([antimony_str, phrasedml_str])

        if "outputfile" in kwargs:
            filepath = kwargs["outputfile"]

        else:
            dirName = self.get_omexfilename()

            try:
                os.mkdir(dirName)
                print("Directory ", dirName, " Created ")
            except FileExistsError:
                print("Directory ", dirName, " already exists")

            workingDir = os.path.join(self.workingdir0, dirName)

            filepath = os.path.join(workingDir, "archive.omex")

        print("The output file path: ", filepath)

        te.exportInlineOmex(inline_omex, filepath)

        return inline_omex

    @staticmethod
    def execute_inlineomex(inline_omex):
        """Execute the inline omex and generate the simulation figures.
        """

        return te.executeInlineOmex(inline_omex)

    def phrasedmltosedml(self, phrasedml_str, sbml_file, **kwargs):
        """Generate SEDML file from phrasedml.
        Example of phrasedml_str:
        phrasedml_str = '''
        model1 = model "{}"
        ...
        ...
        '''

        :param phrasedml_str: text-based way to represent SEDML
        :type phrasedml_str: str
        :param sbml_file: the SBML xml file/path to the file
        :type sbml_file: str
        :return: the sedml string
        :rtype: str
        """

        try:
            with open(sbml_file, "r+") as f:
                sbml_str = f.read()
        except IOError:
            print("Error in opening sbml file")

        phrasedml_str = phrasedml_str.format(sbml_file)

        phrasedml.setReferencedSBML(sbml_file, sbml_str)
        sedml_str = phrasedml.convertString(phrasedml_str)

        if sedml_str is None:
            raise RuntimeError(phrasedml.getLastError())

        sedml_file = os.path.join(self.workingdir0, "sedml.xml")

        for key, value in kwargs.items():
            if "outputfile" in key:
                sedml_file = value

        with open(sedml_file, "wb") as f:
            f.write(sedml_str.encode("utf-8"))

        return sedml_str

    @staticmethod
    def get_omexfilename():
        """Return filename to the OMEX file according to the export time."""

        omexfilename = "OMEX" + utilities.getfilename()

        return omexfilename
