
from .attribute import AttributesOwner
from .context import ToJsonContext

import json


class ZonesOwner(object):
    children = {}

    def add_zone(self, child_zone, zone_content=None):
        """
            Add zone to document; if zone(s) with name child_zone already exists, just appends new one
            Examples:
                doc.add_zone('z_some', 'foo')
                    or
                doc.add_zone(SaasZone('z_some', 'foo'))
            :param child_zone: child zone name or SaasZone object
            :type child_zone: str or SaasZone
            :type zone_content: str
        """
        if isinstance(child_zone, SaasZone):
            self.add_saas_zone(child_zone)
        else:
            self.add_saas_zone(SaasZone(child_zone, zone_content))

    def get_zone(self, name):
        return self.children.get(name, None)

    def set_zone(self, zone, zone_content=None):
        """
            Set value for zone; if zones with the same name already exist, removes them
            Examples:
                doc.set_zone('z_some', 'foo')
                    or
                doc.set_zone(SaasZone('z_some', 'foo'))
                    or
                doc.set_zone([SaasZone('z_some', 'foo'), SaasZone('z_some', 'bar')])
            :param zone: child zone name, or SaasZone object, or list of SaasZone's with the same name
            :type zone: str or SaasZone or list
            :type zone_content: str
        """
        if isinstance(zone, SaasZone) or isinstance(zone, list):
            self.set_saas_zone(zone)
        else:
            self.set_saas_zone(SaasZone(zone, zone_content))

    def add_saas_zone(self, child_zone):
        if not isinstance(child_zone, SaasZone):
            raise Exception('child_zone must be instance of SaasZone here')
        if child_zone.name not in self.children:
            self.children[child_zone.name] = [child_zone]
        else:
            self.children[child_zone.name].append(child_zone)

    def set_saas_zone(self, zone):
        if isinstance(zone, SaasZone):
            self.children[zone.name] = [zone]
        elif isinstance(zone, list):
            if not zone:
                raise Exception('zone is empty')
            zone_names = list(set([z.name for z in zone]))
            if len(zone_names) != 1:
                raise Exception('names in attribute must be the same')
            zone_name = zone_names[0]
            self.children[zone_name] = zone
        else:
            raise Exception('incorrect zone, %s' % zone)

    def zones_to_json(self, context):
        json_repr = {}
        for child_name, zones in self.children.items():
            json_repr[child_name] = [zone.to_json(context) for zone in zones]
        return json_repr


class SaasZone(AttributesOwner, ZonesOwner):
    __slots__ = ('name', 'value', 'attributes', 'children')

    def __init__(self, name, value):
        self.name = name
        self.value = value
        self.attributes = {}
        self.children = {}

    def to_json(self, context):
        json_repr = {
            'value': context.serialize_text(self.value),
            'type': '#z'
        }

        if self.attributes or self.children:
            json_repr['children'] = {}

        if self.attributes:
            json_repr['children'].update(self.attributes_to_json(context))

        if self.children:
            json_repr['children'].update(self.zones_to_json(context))

        if 'children' in json_repr and not json_repr['children']:
            del json_repr['children']

        return json_repr

    def __str__(self):
        return json.dumps(self.to_json(ToJsonContext()), indent=4)
