# -*- coding: utf-8 -*-
#BEGIN_HEADER
import logging
import os
import urllib.request
import json
import uuid

from fliu_pyfilter.wut import mkdir_p
from fliu_pyfilter.escher_curation import EscherModel, EscherMap, KBaseEscherViewer
from fliu_pyfilter.bios_utils import read_json, write_json
import shutil
import escher
import cobra
import cobrakbase

from kbase_python import fetch_models_from_url
from kbase_python import convert_sbml_to_kbase

from installed_clients.KBaseReportClient import KBaseReport
from installed_clients.WorkspaceClient import Workspace
from installed_clients.DataFileUtilClient import DataFileUtil
#END_HEADER


class fliu_pyfilter:
    '''
    Module Name:
    fliu_pyfilter

    Module Description:
    A KBase module: fliu_pyfilter
    '''

    ######## WARNING FOR GEVENT USERS ####### noqa
    # Since asynchronous IO can lead to methods - even the same method -
    # interrupting each other, you must be *very* careful when using global
    # state. A method could easily clobber the state set by another while
    # the latter method is running.
    ######################################### noqa
    VERSION = "0.0.1"
    GIT_URL = "git@github.com:Fxe/kbase_python_module.git"
    GIT_COMMIT_HASH = "cee1e2e9ad3cd2e3e8be1ef1211ec68536dd084e"

    #BEGIN_CLASS_HEADER
    #END_CLASS_HEADER

    # config contains contents of config file in a hash or None if it couldn't
    # be found
    def __init__(self, config):
        #BEGIN_CONSTRUCTOR
        self.config = config
        self.callback_url = os.environ['SDK_CALLBACK_URL']
        self.shared_folder = config['scratch']
        logging.basicConfig(format='%(created)s %(levelname)s: %(message)s',
                            level=logging.INFO)
        
        self.dfu = DataFileUtil(self.callback_url)
        self.report = KBaseReport(self.callback_url)
        #END_CONSTRUCTOR
        pass


    def run_fliu_pyfilter(self, ctx, params):
        """
        This example function accepts any number of parameters and returns results in a KBaseReport
        :param params: instance of mapping from String to unspecified object
        :returns: instance of type "ReportWithModel" -> structure: parameter
           "report_name" of String, parameter "report_ref" of String,
           parameter "fbamodel_id" of String
        """
        # ctx is the context object
        # return variables are: output
        #BEGIN run_fliu_pyfilter
        ws = params['workspace_name']
        report = KBaseReport(self.callback_url)
        report_info = report.create({'report': {'objects_created':[],
                                                'text_message': params['parameter_1']},
                                                'workspace_name': params['workspace_name']})
        output = {
            'report_name': report_info['name'],
            'report_ref': report_info['ref'],
        }
        #END run_fliu_pyfilter

        # At some point might do deeper type checking...
        if not isinstance(output, dict):
            raise ValueError('Method run_fliu_pyfilter return value ' +
                             'output is not type dict as required.')
        # return the results
        return [output]

    def sbml_importer(self, ctx, params):
        """
        :param params: instance of mapping from String to unspecified object
        :returns: instance of type "ReportWithModel" -> structure: parameter
           "report_name" of String, parameter "report_ref" of String,
           parameter "fbamodel_id" of String
        """
        # ctx is the context object
        # return variables are: output
        #BEGIN sbml_importer
        print(params)
        ws = params['workspace_name']
#{'biomass': [], 'model_name': 'test', 
# 'sbml_url': 'http://bigg.ucsd.edu/static/models/e_coli_core.xml', 
# 'automatically_integrate': 1, 'workspace_name': 'filipeliu:narrative_1554172974237', 
# 'remove_boundary': 1}

        def valid_string_arg(key, params):
            return key in params and not params[key] == None and len(params[key].strip()) > 0

        dfu = DataFileUtil(self.callback_url)

#        if valid_string_arg('sbml_url', params):
#            urllib.request.urlretrieve(params['sbml_url'], '/kb/module/data/sbml.xml')
#
#        elif valid_string_arg('input_staging_file_path', params):
#            logging.info('pulling from staging: %s' % params['input_staging_file_path'])
#            retval = dfu.download_staging_file(
#                {
#                    'staging_file_subdir_path': params['input_staging_file_path']
#                }
#            )
#
#            logging.info('download_staging_file: %s' % retval)
#        else:
#            logging.info('invalid file arguments: %s' % params)

        model_id = params['model_name']
        sbml_url = params['sbml_url']
        biomass_ids = params['biomass']
        
        config = {
            'jar_path' : '/kb/module/data/bin',
            'out_path' : '/kb/module/data/json',
            'download_path' : '/kb/module/data/download',
        }
        
        fbamodel_files = fetch_models_from_url(sbml_url, model_id, biomass_ids, config)

        print('model', fbamodel_files)

        fbamodel_objects = []
        for model in fbamodel_files:
            with open(model, 'r') as f:
                fbamodel = json.loads(f.read())
                fbamodel_objects.append(fbamodel)

        objects_created = []

        kbase = cobrakbase.KBaseAPI(ctx['token'], config=self.config)

        for fbamodel in fbamodel_objects:
            kbase.save_object(fbamodel['id'], fbamodel, 'FBA', ws)
            1

        report = KBaseReport(self.callback_url)
        report_info = report.create({'report': {'objects_created' : objects_created,
                                                'text_message': params['sbml_url']},
                                                'workspace_name': params['workspace_name']})
        
        output = {
            'report_name': report_info['name'],
            'report_ref': report_info['ref'],
        }
        #END sbml_importer

        # At some point might do deeper type checking...
        if not isinstance(output, dict):
            raise ValueError('Method sbml_importer return value ' +
                             'output is not type dict as required.')
        # return the results
        return [output]

    def sbml_to_kbase(self, ctx, params):
        """
        :param params: instance of mapping from String to unspecified object
        :returns: instance of type "ReportWithModel" -> structure: parameter
           "report_name" of String, parameter "report_ref" of String,
           parameter "fbamodel_id" of String
        """
        # ctx is the context object
        # return variables are: output
        #BEGIN sbml_to_kbase
        
        kbase = cobrakbase.KBaseAPI(ctx['token'], config=self.config)
        
        config = {
            'jar_path' : '/kb/module/data/bin',
            'out_path' : '/kb/module/data/json',
            'download_path' : '/kb/module/data/download',
        }
        
        model_objects = convert_sbml_to_kbase(params['filename'], 
                                              params['model_id'], 
                                              params['auto_integrate'], 
                                              params['remove_boundary'],
                                              params['biomass_ids'],
                                              config)
        
        output = model_objects[0]
        
        #END sbml_to_kbase

        # At some point might do deeper type checking...
        if not isinstance(output, dict):
            raise ValueError('Method sbml_to_kbase return value ' +
                             'output is not type dict as required.')
        # return the results
        return [output]

    def integrate_model(self, ctx, params):
        """
        :param params: instance of mapping from String to unspecified object
        :returns: instance of type "ReportWithModel" -> structure: parameter
           "report_name" of String, parameter "report_ref" of String,
           parameter "fbamodel_id" of String
        """
        # ctx is the context object
        # return variables are: output
        #BEGIN integrate_model
        print(params)

        report = KBaseReport(self.callback_url)
        report_info = report.create({'report': {'objects_created':[],
                                                'text_message': params['parameter_1']},
                                                'workspace_name': params['workspace_name']})
        output = {
            'report_name': report_info['name'],
            'report_ref': report_info['ref'],
        }
        #END integrate_model

        # At some point might do deeper type checking...
        if not isinstance(output, dict):
            raise ValueError('Method integrate_model return value ' +
                             'output is not type dict as required.')
        # return the results
        return [output]

    def auto_propagate_genome(self, ctx, params):
        """
        :param params: instance of mapping from String to unspecified object
        :returns: instance of type "ReportWithModel" -> structure: parameter
           "report_name" of String, parameter "report_ref" of String,
           parameter "fbamodel_id" of String
        """
        # ctx is the context object
        # return variables are: output
        #BEGIN auto_propagate_genome

        report = KBaseReport(self.callback_url)
        report_info = report.create({'report': {'objects_created':[],
                                                'text_message': params['parameter_1']},
                                                'workspace_name': params['workspace_name']})
        output = {
            'report_name': report_info['name'],
            'report_ref': report_info['ref'],
        }
        #END auto_propagate_genome

        # At some point might do deeper type checking...
        if not isinstance(output, dict):
            raise ValueError('Method auto_propagate_genome return value ' +
                             'output is not type dict as required.')
        # return the results
        return [output]

    def escher_fbamodel(self, ctx, params):
        """
        :param params: instance of mapping from String to unspecified object
        :returns: instance of type "ReportWithModel" -> structure: parameter
           "report_name" of String, parameter "report_ref" of String,
           parameter "fbamodel_id" of String
        """
        # ctx is the context object
        # return variables are: output
        #BEGIN escher_fbamodel
        ws = params['workspace_name']
        kbase = cobrakbase.KBaseAPI(ctx['token'], dev=True)
        kmodel = kbase.get_object(params['model_id'], ws)
        fbamodel = cobrakbase.core.model.KBaseFBAModel(kmodel)
        fba = None
        
        if 'fba_id' in params:
            kfba = kbase.get_object(params['fba_id'], ws)
            fba = cobrakbase.core.model.KBaseFBASolution(kfba)
        
        catalog = {}
        
        kbase_escher = KBaseEscherViewer(fbamodel, 'BIOS', 'bios7.json', fba, ESCHER_HOME='/root/.cache/escher/1-0-0/5/')
        kbase_escher.load_model()
        kbase_escher.generate_maps('c')
        catalog_bios = kbase_escher.save_maps('/kb/module/data/report/maps/')
        
        kbase_escher = KBaseEscherViewer(fbamodel, 'ModelSEED', 'ModelSEED.json', fba, ESCHER_HOME='/root/.cache/escher/1-0-0/5/')
        kbase_escher.load_model()
        kbase_escher.generate_maps('c')
        catalog_seed = kbase_escher.save_maps('/kb/module/data/report/maps/')
        
        catalog.update(catalog_bios)
        catalog.update(catalog_seed)
        print('catalog', catalog)
        write_json(catalog, '/kb/module/data/report/catalog.json')
        
        print(params)
        
        output_directory = os.path.join(self.shared_folder, str(uuid.uuid4()))
        mkdir_p(output_directory)
        
        print('output_directory', output_directory, os.listdir(output_directory))
        shutil.copytree('/kb/module/data/report', output_directory + '/report')
        
        print(output_directory)
        
        shock_id = self.dfu.file_to_shock({
            'file_path': output_directory + '/report',
            'pack': 'zip'
        })['shock_id']
        
        html_report = []
        html_report.append({
            'shock_id': shock_id,
            'name': 'index.html',
            'label': 'viewer',
            'description': 'description'
        })
        
        report = KBaseReport(self.callback_url)
        
        report_params = {
            'message': 'message_in_app ' + output_directory,
            'warnings': ['warnings_in_app'],
            'workspace_name': ws,
            'objects_created': [],
            'html_links': html_report,
            'direct_html_link_index': 0,
            'html_window_height': 800,
        }
        
        print(report_params)
        report_info = report.create_extended_report(report_params)
        
        output = {
            'report_name': report_info['name'], 
            'report_ref': report_info['ref']
        }
        
        #END escher_fbamodel

        # At some point might do deeper type checking...
        if not isinstance(output, dict):
            raise ValueError('Method escher_fbamodel return value ' +
                             'output is not type dict as required.')
        # return the results
        return [output]
    def status(self, ctx):
        #BEGIN_STATUS
        returnVal = {'state': "OK",
                     'message': "",
                     'version': self.VERSION,
                     'git_url': self.GIT_URL,
                     'git_commit_hash': self.GIT_COMMIT_HASH}
        #END_STATUS
        return [returnVal]
