#!/usr/bin/env python
# Copyright (c) 2018, Clemence Frioux <clemence.frioux@inria.fr>
#
# This file is part of miscoto.
#
# miscoto is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# miscoto is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with miscoto.  If not, see <http://www.gnu.org/licenses/>.
# -*- coding: utf-8 -*-

import argparse
import sys
import os
import time
import logging
from miscoto import query, sbml, commons, utils
from os import listdir
from os.path import isfile, join
from xml.etree.ElementTree import ParseError
from clyngor.as_pyasp import TermSet, Atom

logger = logging.getLogger(__name__)

###############################################################################
#
message = """
Compute the scope and target produciblity of a host (optional) and the added-value
of a microbiome regarding scope and target producibility. The microbiome result part
gives the targets and compounds that are producible providing cooperation occurs
within the community of host + all symbionts and that were not producible with
the host alone.
Computation from SBML models or an instance pre-created with miscoto_instance.py
"""

requires = """
requires clyngor package: "pip install clyngor"
"""

pusage="""
**1** from SBML2 files \n
python miscoto_scopes.py -m host.sbml -b symbiont_directory -s seeds.sbml -t targets.sbml
\n
**2** from SBML2 files \n
python miscoto_scopes.py -b symbiont_directory -s seeds.sbml -t targets.sbml
\n
**3** from a pre-computed instance with possibly (additional) seeds or targets \n
python miscoto_scopes.py -a instance.lp [-s seeds.sbml] [-t targets.sbml]
"""
#
###############################################################################


def cmd_scopes():
    """run directly miscoto_scopes from the shell
    """
    parser = argparse.ArgumentParser(description=message, usage=pusage, epilog=requires)
    parser.add_argument("-a", "--asp",
                        help="instance if already created with miscoto_instance", required=False)
    parser.add_argument("-m", "--modelhost",
                        help="host model in SBML format, ignored if -a instance is provided",
                        required=False)
    parser.add_argument("-b", "--bactsymbionts",
                        help="directory of symbionts models, all in sbml format, ignored if -a instance is provided",
                        required=False)
    parser.add_argument("-s", "--seeds",
                        help="seeds in SBML format",
                        required=False)
    parser.add_argument("-t", "--targets",
                        help="targets in SBML format",
                        required=False)

    args = parser.parse_args()
    lp_instance_file_arg = args.asp
    targets_sbml = args.targets
    seeds_sbml = args.seeds
    bacterium_met =  args.bactsymbionts
    draft_sbml = args.modelhost
    run_scopes(lp_instance_file_arg, targets_sbml, seeds_sbml, bacterium_met, draft_sbml)

def run_scopes(lp_instance_file=None, targets_file=None, seeds_file=None, bacteria_dir=None, host_file=None):
    """[summary]
        lp_instance_file ([str], optional): Defaults to None. [ASP facts instance of the problem]
        targets_file ([str], optional): Defaults to None. [targets file]
        seeds_file ([str], optional): Defaults to None. [seeds file]
        bacteria_dir ([str], optional): Defaults to None. [directory of bacterial metabolic networks]
        host_file ([str], optional): Defaults to None. [host metabolic network]
    
    Returns:
        [dic]: [all information related to scope computation]
    """

    start_time = time.time()
    results = {}
    # case 1: instance is provided, just read targets and seeds if given
    input_instance = False
    if lp_instance_file:
        if not os.path.isfile(lp_instance_file) :
            logger.info('Instance file not found')
            sys.exit(1)

        input_instance = True
        delete_lp_instance = False
        logger.info(
            "Instance provided, only seeds and targets will be added if given")

        if targets_file:
            logger.info('Reading targets from ' + targets_file)
            try:
                targetsfacts = sbml.readSBMLspecies_clyngor(targets_file, 'target')
            except FileNotFoundError:
                logger.critical('Targets file not found')
                sys.exit(1)
            except ParseError:
                logger.critical("Invalid syntax in SBML file: "+targets_file)
                sys.exit(1)
        else:
            targetsfacts = TermSet()

        if seeds_file:
            logger.info('Reading targets from ' + seeds_file)
            try:
                seedsfacts = sbml.readSBMLspecies_clyngor(seeds_file, 'seed')
            except FileNotFoundError:
                logger.critical('Seeds file not found')
                sys.exit(1)
            except ParseError:
                logger.critical("Invalid syntax in SBML file: " + seeds_file)
                sys.exit(1)
        else:
            seedsfacts = TermSet()

        with open(lp_instance_file, "a") as f:
            for elem in targetsfacts:
                f.write(str(elem) + '.\n')
            for elem in seedsfacts:
                f.write(str(elem) + '.\n')

    # case 2: read inputs from SBML files
    elif bacteria_dir and seeds_file:
        if not os.path.isdir(bacteria_dir):
            logger.info("Symbiont directory not found")
            sys.exit(1)

        delete_lp_instance = True

        if host_file:
            logger.info('Reading host network from ' + host_file)
            try:
                draftnet = sbml.readSBMLnetwork_symbionts_clyngor(host_file, 'host_metab_mod')
            except FileNotFoundError:
                logger.critical('Host file not found')
                sys.exit(1)
            except ParseError:
                logger.critical("Invalid syntax in SBML file: " + host_file)
                sys.exit(1)
            draftnet.add(Atom('draft', ["\"" + 'host_metab_mod' + "\""]))
        else:
            logger.warning('No host provided.')
            draftnet = TermSet()

        logger.info('Reading seeds from ' + seeds_file)
        try:
            seeds = sbml.readSBMLspecies_clyngor(seeds_file, 'seed')
        except FileNotFoundError:
            logger.critical('Seeds file not found')
            sys.exit(1)
        except ParseError:
            logger.critical("Invalid syntax in SBML file: "+seeds_file)
            sys.exit(1)
        lp_instance = TermSet(draftnet.union(seeds))

        if targets_file:
            logger.info('Reading targets from ' + targets_file)
            try:
                targets = sbml.readSBMLspecies_clyngor(targets_file, 'target')
            except FileNotFoundError:
                logger.critical('Targets file not found')
                sys.exit(1)
            except ParseError:
                logger.critical("Invalid syntax in SBML file: " + targets_file)
                sys.exit(1)
            lp_instance = TermSet(lp_instance.union(targets))
        else:
            logger.info("No targets provided.")

        if not os.path.isdir(bacteria_dir):
            logger.critical("Symbiont directory not found")
            sys.exit(1)

        lp_instance_file = utils.to_file(lp_instance)

        logger.info('Reading bacterial networks from ' + bacteria_dir + '...')
        bactfacts = TermSet()
        onlyfiles = [f for f in listdir(bacteria_dir) if isfile(join(bacteria_dir, f))]
        for bacteria_file in onlyfiles:
            name = os.path.splitext(bacteria_file)[0]
            try:
                one_bact_model = sbml.readSBMLnetwork_symbionts_clyngor(bacteria_dir+'/'+bacteria_file, name)
                one_bact_model.add(Atom('bacteria', ["\"" + name + "\""]))
                utils.to_file(one_bact_model, lp_instance_file)
                logger.info('Done for ' + name)
            except:
                logger.info('Could not read file ' + name + ', will ignore it')

    else:
        logger.critical("ERROR missing input")
        logger.info(pusage)
        quit()

    logger.info("Computing scopes...")

    model = query.get_scopes(lp_instance_file, commons.ASP_SRC_SCOPES)

    host_scope = []
    host_prodtargets = []
    host_unprodtargets = []
    com_scope = []
    comhost_scope = []
    com_prodtargets = []
    com_unprodtargets = []
    for pred in model:
        if pred == 'dscope':
            for a in model[pred, 1]:
                host_scope.append(a[0])
        elif pred == 'dproducible':
            for a in model[pred, 1]:
                host_prodtargets.append(a[0])
        elif pred == 'dunproducible':
            for a in model[pred, 1]:
                host_unprodtargets.append(a[0])
        elif pred == 'newscope_microbiome':
            for a in model[pred, 1]:
                com_scope.append(a[0])
        elif pred == 'newscope_with_host':
            for a in model[pred, 1]:
                comhost_scope.append(a[0])
        elif pred == 'newlyproducible':
            for a in model[pred, 1]:
                com_prodtargets.append(a[0])
        elif pred == 'aunproducible':
            for a in model[pred, 1]:
                com_unprodtargets.append(a[0])

    if host_file or input_instance:
        logger.info('*** HOST model producibility check ***')

        logger.info('Host producible targets => ' + str(len(host_prodtargets)))
        logger.info("\n".join(host_prodtargets))
        results['host_prodtargets'] = host_prodtargets

        logger.info('Host unproducible targets => ' +
                    str(len(host_unprodtargets)))
        logger.info("\n".join(host_unprodtargets))
        results['host_unprodtargets'] = host_unprodtargets

        logger.info('Host scope => ' + str(len(host_scope)))
        logger.info("\n".join(host_scope))
        results['host_scope'] = host_scope

    logger.info('*** MICROBIOME added-value ***')
    logger.info('Microbiome only producible targets => ' +
                str(len(com_prodtargets)))
    logger.info("\n".join(com_prodtargets))
    results['com_prodtargets'] = com_prodtargets

    logger.info('Microbiome unproducible targets => ' +
                str(len(com_unprodtargets)))
    logger.info("\n".join(com_unprodtargets))
    results['com_unprodtargets'] = com_unprodtargets

    if host_file or input_instance:
        logger.info(
            'Microbiome only (host + symbionts) scope (host metabolites only producible with the microbiome) => '
            + str(len(comhost_scope)))
        logger.info("\n".join(comhost_scope))
        results['comhost_scope'] = comhost_scope
    if input_instance or not host_file:
        logger.info(
            'Microbiome only (symbionts) scope (metabolites only producible with the microbiome) => '
            + str(len(com_scope)))
        logger.info("\n".join(com_scope))
        results['com_scope'] = com_scope

    if delete_lp_instance == True:
        os.unlink(lp_instance_file)

    logger.info("--- %s seconds ---" % (time.time() - start_time))
    utils.clean_up()
    return results

if __name__ == '__main__':
    cmd_scopes()