# #START_LICENSE###########################################################
#
#
# This file is part of the Environment for Tree Exploration program
# (ETE).  http://etetoolkit.org
#
# ETE 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.
#
# ETE 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 ETE.  If not, see <http://www.gnu.org/licenses/>.
#
#
#                     ABOUT THE ETE PACKAGE
#                     =====================
#
# ETE is distributed under the GPL copyleft license (2008-2015).
#
# If you make use of ETE in published work, please cite:
#
# Jaime Huerta-Cepas, Joaquin Dopazo and Toni Gabaldon.
# ETE: a python Environment for Tree Exploration. Jaime BMC
# Bioinformatics 2010,:24doi:10.1186/1471-2105-11-24
#
# Note that extra references to the specific methods implemented in
# the toolkit may be available in the documentation.
#
# More info at http://etetoolkit.org. Contact: huerta@embl.de
#
#
# #END_LICENSE#############################################################
from __future__ import absolute_import
from __future__ import print_function

import os
from ...utils import print_table
from .configobj import ConfigObj
from .errors import ConfigError
from .utils import colorify
from .apps import APP2CLASS, OPTION2APPTYPE, APPTYPES

import six
from six.moves import map

def build_supermatrix_workflow(wname):
    try:
        cog_selector, alg_concatenator, treebuilder = map(lambda x: "@%s" %x, wname.split("-"))
    except ValueError:
        raise ConfigError("Invalid supermatrix workflow: %s" %wname)
    
    workflow = {wname: {
        "_app": "supermatrix",
        "_cog_selector": cog_selector,
        "_alg_concatenator": alg_concatenator, 
        "_aa_tree_builder": treebuilder,
        "_nt_tree_builder": treebuilder, 
        "_appset":"@builtin_apps"}
    }
    return workflow

def build_genetree_workflow(wname):
    try:
        aligner, trimmer, modeltester, treebuilder = map(lambda x: "none" if x.lower() == "none" else "@%s"%x,
                                                         wname.split("-"))
    except ValueError:
        raise ConfigError("Invalid genetree workflow: %s" %wname)
    
    workflow = {wname: {
        "_app": "genetree",
        "_aa_aligner": aligner,
        "_aa_alg_cleaner": trimmer, 
        "_aa_model_tester": modeltester,
        "_aa_tree_builder": treebuilder,
        "_nt_aligner": aligner,
        "_nt_alg_cleaner": trimmer,
        "_nt_model_tester": modeltester,
        "_nt_tree_builder": treebuilder, 
        "_appset":"@builtin_apps"}
    }
    return workflow

def list_workflows(config, target_type=None):

    if not target_type or target_type == 'supermatrix':
        print()
        avail_meta = [(k, config["workflow_desc"].get(k, ""), len(v)) for k,v in six.iteritems(config.get('supermatrix_meta_workflow', {}))]
        print_table(avail_meta, fix_col_width=[45, 60, 10], header=["Worflow name", "Description", "threads"], title="Supermatrix shortcut workflow names", row_line=True)
        
    if not target_type or target_type == 'genetree':        
        print()
        avail_meta = [(k, config["workflow_desc"].get(k, ""), len(v)) for k,v in six.iteritems(config.get('genetree_meta_workflow', {}))]
        print_table(avail_meta, fix_col_width=[45, 60, 10], header=["Worflow name", "Description", "threads"], title="GeneTree shortcut workflow names", row_line=True)


def list_apps(config, target_apps = None):
    for appname, validapps in APPTYPES.items():
        if target_apps:
            if appname not in target_apps and target_apps & validapps:
                validapps = target_apps & validapps
            elif appname in target_apps:
                pass
            else:
                continue
                        
        avail_blocks = [[blockname, block["_app"], block.get("_desc", "")] for blockname, block in config.items() if block.get("_app") in validapps]
        print_table(avail_blocks, header=["name", "app type", "desc."], max_col_width=70, title=appname, row_line=True)
        print()

def block_detail(block_name, config, color=True):
    blocks_to_show = {}
    iterable_types = set([set, list, tuple, frozenset])
    if block_name not in config:
        try:
            next_block = [x.lstrip('@') for x in config.get('genetree_meta_workflow', {})[block_name]]
            metaworkflow = True
        except Exception as e:
            print(e)
            raise ValueError('block name not found [%s]' %block_name)
    else:
        metaworkflow = False
        next_block = [block_name]

    pos = 0
    while next_block:
        block = next_block.pop()
        blocks_to_show[block] = pos
        for k1, v1 in six.iteritems(config[block]):
            if type(v1) in iterable_types:
                for v2 in v1:
                    if isinstance(v2, str) and v2.startswith('@'):
                        next_block.append(v2[1:])
            elif isinstance(v1, str) and v1.startswith('@'):
                next_block.append(v1[1:])
        pos += 1

    if metaworkflow and color:
        print(colorify('[genetree_meta_workflow]', 'yellow'))
        print("%s = %s" %(block_name, ', '.join(config["genetree_meta_workflow"][block_name])))
        print()
    elif metaworkflow:
        print('[genetree_meta_workflow]')
        print("%s = %s" %(block_name, ', '.join(config["genetree_meta_workflow"][block_name])))
        print()

    for b, pos in sorted(list(blocks_to_show.items()), key=lambda x: x[1]):
        if b == "builtin_apps":
            continue
        if color:
            print(colorify('[%s]' %b, 'green'))
        else:
            print('[%s]' %b)

        for k,v in sorted(config[b].items()):
            if k == "_inherits":
                continue
            if type(v) in iterable_types:
                v = ', '.join(map(str, v))+','

            if color:
                if k == '_app':
                    print(colorify('% 35s = %s' %(k, v), "lblue"))
                else:
                    print('%s = %s' %(colorify("% 35s" %k, "orange"), v))
            else:
                print('% 40s = %s' %(k, v))
        print()

def parse_block(blockname, conf):
    blocktype = conf[blockname].get('_app', 'unknown')
    for attr, v in list(conf[blockname].items()):
        conf[blockname][attr] = check_type(blocktype, attr, v)
        if isinstance(conf[blockname][attr], list):
            for i in conf[blockname][attr]:
                check_block_link(conf, blockname, i, attr)
        else:
            check_block_link(conf, blockname, conf[blockname][attr], attr)
            
    # Check for missing attributes     
    for tag, tester in six.iteritems(CHECKERS):
        if tag[0] == blocktype and (tester[2] and tag[1] not in conf[blockname]):
            raise ConfigError('[%s] attribute expected in block [%s]' %(tag[1], blockname))
            
def check_config(fname):
    conf = ConfigObj(fname, list_values=True)

    # expand meta_workflows
    for meta_name, meta_wf in conf["genetree_meta_workflow"].items():
        for wkname in meta_wf:        
            conf.update(build_genetree_workflow(wkname.lstrip("@")))
    for meta_name, meta_wf in conf["supermatrix_meta_workflow"].items():
        for wkname in meta_wf:        
            conf.update(build_supermatrix_workflow(wkname.lstrip("@")))
            
    # expand inherits options 
    for k, v in list(conf.items()):
        if 
'_inherits' in v:
            base = v['_inherits']
            try:
                new_dict = dict(conf[base])
            except KeyError:
                raise ConfigError('[%s] config block is referred in [%s] but not present in config file' %(bas
e, k))
            new_dict.update(v)
            conf