"""
Utils for codegen with entrypoint compiler
"""

import json
import os
import re
import keyword
from enum import Enum
from textwrap import dedent, indent
from jinja2 import Template

tabsize = 4

COPYRIGHT_NOTICE = dedent(
    """# ---------------------------------------------------------------------\
-----------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
# ---------------------------------------------------------------------------\
-----------------
""")
CODEGEN_WARNING = \
    "#- Generated by tools/entrypoint_compiler.py: do not edit by hand\n"


class ManifestIds(Enum):
    Name = 'Name'
    Inputs = 'Inputs'
    Kind = 'Kind'


def load_json(file_path):
    with open(file_path) as f:
        lines = f.readlines()
        lines = [l for l in lines if not l.strip().startswith('#')]
        content_without_comments = '\n'.join(lines)
        return json.loads(content_without_comments)


def update_json_object(json_obj, json_diff_obj, excluded_keys=set()):
    if not json_diff_obj:
        return
    keys = set(json_diff_obj.keys()) - excluded_keys
    for k in keys:
        json_obj[k] = json_diff_obj[k]


class JsonArrayWithIdLookup:
    def __init__(self, json_array, _id):
        self._id = _id
        self._check_ids(json_array)
        self._dict = {json_obj[_id]: json_obj for json_obj in json_array}
        self.ids = set(sorted(self._dict.keys()))

    def __contains__(self, _id):
        return _id in self.ids

    def __getitem__(self, id):
        # Do not throw. Use get() instead of [].
        return self._dict.get(id)

    def _check_ids(self, json_array):
        for json_obj in json_array:
            if self._id not in json_obj:
                raise ValueError(
                    'Could not find required "{}" field in {}'.format(
                        self._id, json_obj))


class JsonArrayWithNameLookup(JsonArrayWithIdLookup):
    def __init__(self, json_array):
        super(
            JsonArrayWithNameLookup,
            self).__init__(
            json_array,
            _id=ManifestIds.Name.value)
        self.names = self.ids


def convert_name(name):
    """
    Convert C# style into Python style.

    :param name: name to convert
    :returns: converted name

    Example:

    ::

        convertName --> convert_name
    """
    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
    s2 = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
    return s2 if not keyword.iskeyword(s2) else s2 + "_"


def module_to_path(module_namespace, root=''):
    return os.path.join(root, *module_namespace.split('.'))


def get_files_recursively(dir_path):
    all_files = []
    for root, directories, filenames in os.walk(dir_path):
        files = (os.path.join(root, f) for f in filenames)
        files = (f for f in files if os.path.isfile(f))
        all_files.extend(files)
    return all_files


_presteps_template = Template('''
def _nodes_with_presteps(self):
    """
    Inserts preprocessing before this one.
    """
{{ body }}''')

int_to_r4_converter = \
    '''from ..schema import TypeConverter
return [TypeConverter(result_type='R4')._steal_io(self), self]'''

_presteps = {
    'MinMaxScaler': int_to_r4_converter,
    'MeanVarianceScaler': int_to_r4_converter,
    'LogMeanVarianceScaler': int_to_r4_converter,
    'Binner': int_to_r4_converter,
    # 'SupervisedBinner': int_to_r4_converter, # not exist in nimbusml

    'PcaTransformer':
    '''from ..preprocessing.schema import TypeConverter
if type(self._columns) == dict:
    from copy import deepcopy
    self_copy = deepcopy(self)
    self_copy._columns = [y for x in self._columns.values() for y in x]
    return [TypeConverter(result_type='R4')._steal_io(self_copy), self]
return [TypeConverter(result_type='R4')._steal_io(self), self]'''
}


def get_presteps(class_name):
    tab = ' ' * tabsize
    presteps_func = ''
    if class_name in _presteps:
        body = indent(_presteps[class_name], tab)
        presteps_func = indent(
            _presteps_template.render(
                body=body), tab) + '\n'
    return presteps_func
