# -*- coding: utf-8 -*-

"""
biothings_explorer.id_converter
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This module contains code that biothings_explorer use to resolve
different identifiers
"""
import time
from .registry import Registry
from .apicall import BioThingsCaller
from .api_output_parser import OutputParser


class IDConverter():
    def __init__(self, registry=None):
        if not registry:
            self.registry = Registry()
        else:
            self.registry = registry
        self.caller = BioThingsCaller(batch_mode=True)
        self.semantic_type_api_mapping = {'Gene': 'mygene.info',
                                          'Variant': 'myvariant.info',
                                          'ChemicalSubstance': 'mychem.info',
                                          'DiseaseOrPhenotypicFeature': "mydisease.info"}

    def fetch_schema_mapping_file(self, api):
        """Fetch schema mapping file from the registry"""
        return self.registry.registry[api]['mapping']

    def subset_mapping_file(self, mapping_file):
        return {k:v for (k,v) in mapping_file.items() if k in (["@context", "@type"] + self.registry.mp.id_list)}

    def get_output_fields(self, mapping_file):
        fields = []
        for k, v in mapping_file.items():
            if k in self.registry.mp.id_list:
                if isinstance(v, list):
                    fields += v
                elif isinstance(v, str):
                    fields.append(v)
        return ','.join(fields)

    def get_input_fields(self, mapping_file, _type):
        input_fields = mapping_file.get(_type)
        if isinstance(input_fields, list):
            return input_fields[0]
        else:
            return input_fields

    def convert_ids(self, inputs):
        results = {}
        api_call_inputs = []
        mapping_files = []
        apis = []
        types = []
        for _input in inputs:
            ids, _type, semantic_type = _input
            # convert id to list
            if isinstance(ids, str):
                ids = [ids]
            # make sure all ids in id list is str
            if isinstance(ids, list):
                ids = [str(i) for i in ids]
            api = self.semantic_type_api_mapping.get(semantic_type)
            # if id can not be converted, the equivalent id is itself
            if not api:
                if _type.startswith("bts:"):
                    _type = _type[4:]
                for _id in ids:
                    results[_type + ':' + _id] = {'bts:' + _type: [_id]}
            else:
                ids = ','.join(ids)
                types.append(_type)
                mapping_file = self.fetch_schema_mapping_file(api)
                mapping_file = self.subset_mapping_file(mapping_file)
                mapping_files.append(mapping_file)
                apis.append(api)
                if self.get_input_fields(mapping_file, _type):
                    api_call_inputs.append({"api": api,
                                            "input": self.get_input_fields(mapping_file, _type),
                                            "output": self.get_output_fields(mapping_file),
                                            "values": ids,
                                            "batch_mode": True
                                            })
                else:
                    if _type.startswith("bts:"):
                        _type = _type[4:]
                    for _id in ids.split(','):
                        results[_type + ':' + _id] = {'bts:' + _type: [_id]}
        # make API calls asynchronously and gather all outputs
        responses = self.caller.call_apis(api_call_inputs, size=10)
        # loop through outputs
        for _res, _map, _api, _type in zip(responses, mapping_files,
                                                 apis, types):
            # restructure API output based on mapping file
            new_res = OutputParser(_res, _map, True, _api).parse()
            # remove "@context" and "@type" from result
            for k, v in new_res.items():
                if '@context' in v:
                    v.pop("@context")
                if '@type' in v:
                    v.pop("@type")
                if _type.startswith("bts:"):
                    _type = _type[4:]
                # remove duplicates
                for m, n in v.items():
                    if n and type(n) == list:
                        v[m] = list(set(n))
                # after removing @context and @type, check if the dict is empty
                if v:
                    results[_type + ':' + k] = v
                # if the dict is empty, just return itself as its value
                else:
                    results[_type + ':' + k] = {'bts:' + _type: [k]}
        return results

@@ -2553,80 +2553,8 @@
 se:%0A
-                ids = ','.join(ids)%0A                types.append(_type)%0A
     
@@ -2706,62 +2706,255 @@
     
-mapping_files.append(mapping_file)%0A                api
+if self.get_input_fields(mapping_file, _type):%0A                    if type(ids) == list and len(ids) %3E 1000:%0A                        print(%22Yes%22)%0A                        for i in range(0, len(ids), 1000):%0A                            api_call_input
 s.ap
@@ -2958,20 +2958,28 @@
 .append(
+%7B%22
 api
-)
+%22: api,
 %0A       
@@ -2979,34 +2979,72 @@
                 
-if
+                                %22input%22:
  self.get_input_
@@ -3062,34 +3062,517 @@
 ing_file, _type)
-:%0A
+,%0A                                                %22output%22: self.get_output_fields(mapping_file),%0A                                                %22values%22: ','.join(ids%5Bi:i+1000%5D),%0A                                                %22batch_mode%22: True%0A                                                %7D)%0A                            types.append(_type)%0A                            mapping_files.append(mapping_file)%0A                            apis.append(api)%0A                    else:%0A    
                 
@@ -3603,32 +3603,36 @@
 nd(%7B%22api%22: api,%0A
+    
                 
@@ -3748,32 +3748,36 @@
                 
+ 
+   
 %22output%22: self.g
@@ -3844,32 +3844,36 @@
                 
+    
 %22values%22: ids,%0A 
@@ -3870,13 +3870,27 @@
 s%22: 
+','.join(
 ids
+)
 ,%0A
+    
     
@@ -3984,33 +3984,181 @@
                 
-%7D
+    %7D)%0A                        types.append(_type)%0A                        mapping_files.append(mapping_file)%0A                        apis.append(api
 )%0A              
@@ -4294,19 +4294,8 @@
  ids
-.split(',')
 :%0A  

set max id list to be 1000