import re
from convert.base.generator import BaseGenerator
from convert.base.parsedobject import *
import datetime


class Generator(BaseGenerator):
    def _generate_default_constructor(self):
        if self.data.type == ParsedObjectType.Enum:
            return ""
        constructor = "    def __init__(self):\n"

        if self.data.data.__len__() == 0:
            constructor += "        pass\n"
        else:
            for member in self.data.data:
                if member.type == ParsedObjectType.Array:
                    constructor += "        self._{0} = []\n".format(_camel_case(member.name))
                elif member.type == ParsedObjectType.String:
                    constructor += "        self._{0} = \"\"\n".format(_camel_case(member.name))
                elif member.type == ParsedObjectType.Int:
                    constructor += "        self._{0} = 0\n".format(_camel_case(member.name))
                elif member.type == ParsedObjectType.Float:
                    constructor += "        self._{0} = 0.0\n".format(_camel_case(member.name))
                elif member.type == ParsedObjectType.Object:
                    constructor += "        self._{0} = None\n".format(_camel_case(member.name))
                elif member.type == ParsedObjectType.Bool:
                    constructor += "        self._{0} = False\n".format(_camel_case(member.name))
                elif member.type == ParsedObjectType.Enum:
                    constructor += "        self._{0} = {1}(0)\n".format(_camel_case(member.name), _capitalize(member.type_name))
        constructor += "\n"

        return constructor

    def _generate_footer(self):
        return ""

    def _generate_member_access(self):
        result = ""
        for member in self.data.data:
            result += self._generate_getter_setter(member)
        return result

    def _generate_header(self):
        result = ""

        # Enums only need to import enum, and won't have a factory
        if self.data.type == ParsedObjectType.Enum:
            result += "from enum import Enum\n"
        else:
            for factory in self.factories:
                result += factory.generate_import()

        for member in self.data.data:
            if _capitalize(member.type_name) == _capitalize(self.data.type_name):
                # if the member is the same class as the current class then we shouldn't import it
                continue
            if member.type == ParsedObjectType.Object or member.type == ParsedObjectType.Enum:
                result += "from {0} import {1}\n".format(member.type_name.lower(), _capitalize(member.type_name))
            elif member.type == ParsedObjectType.Array:
                child = member.data[0]
                if _capitalize(child.type_name) == _capitalize(self.data.type_name):
                    continue
                if child.type == ParsedObjectType.Object:
                    result += "from {0} import {1}\n".format(child.type_name.lower(), _capitalize(child.type_name))

        date_str = "Date: {0}".format(datetime.date.today())
        if BaseGenerator.skip_date_comment:
            date_str = ""
        date_str = date_str.ljust(82)

        result += ("#####################################################################################\n"
                   "# This file is generated by Json2Class (https://github.com/DragonSpawn/Json2Class)  #\n"
                   "# Modifications to this file will be lost the next time you run the tool.           #\n"
                   "# {0}#\n"
                   "#####################################################################################\n\n").format(date_str)

        inheritance_str = "object"
        if self.data.type == ParsedObjectType.Enum:
            inheritance_str = "Enum"
        result += "\nclass {0}({1}):\n".format(_capitalize(self.data.type_name), inheritance_str)
        return result

    def file_name(self, json_name):
        return json_name.lower() + ".py"

    def _generate_getter_setter(self, member):
        if self.data.type == ParsedObjectType.Enum:
            return "    {0} = {1}\n".format(_capitalize(member.name), member.data)
        return ("    @property\n"
                "    def {0}(self):\n"
                "        \"\"\":rtype: {1}\"\"\"\n"
                "        return self._{0}\n\n"
                "    @{0}.setter\n"
                "    def {0}(self, value):\n"
                "        \"\"\":type value: {1}\n"
                "           :rtype: None\"\"\"\n"
                "        self._{0} = value\n\n").format(_camel_case(member.name), _get_type_name(member))


def _camel_case(obj):
    a = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')
    return a.sub(r'_\1', obj).lower()


def _capitalize(obj):
    """
    Returns the object name with the first letter capitalized (all other untouched).
    :param obj:
    :return:
    """
    if obj.__len__() < 2:
        return obj
    if obj == "string" or obj == "float" or obj == "int":
        return obj
    return obj[0].upper() + obj[1:]


def _get_type_name(obj):
    if obj.type == ParsedObjectType.String:
        return "str"
    if obj.type == ParsedObjectType.Object or obj.type == ParsedObjectType.Enum:
        return _capitalize(obj.type_name)
    if obj.type == ParsedObjectType.Array:
        return "list of [{0}]".format(_get_type_name(obj.data[0]))
    return obj.type.name.lower()