# Copyright 2016 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Contains all the sections for the spec."""
from batesian import AccessKeyStore
from batesian.sections import Sections
import inspect
import json
import os
import logging
import re


logger = logging.getLogger(__name__)

class MatrixSections(Sections):

    # pass through git ver so it'll be dropped in the input file
    def render_git_version(self):
        return self.units.get("git_version")["string"]

    def render_git_rev(self):
        return self.units.get("git_version")["revision"]

    def render_changelogs(self):
        rendered = {}
        changelogs = self.units.get("changelogs")
        for spec, changelog_text in changelogs.items():
            spec_var = "%s_changelog" % spec
            logger.info("Rendering changelog for spec: %s" % spec)
            rendered[spec_var] = changelog_text
        return rendered

    def _render_events(self, filterFn, sortFn):
        template = self.env.get_template("events.tmpl")
        examples = self.units.get("event_examples")
        schemas = self.units.get("event_schemas")
        subtitle_title_char = self.units.get("spec_targets")[
            "relative_title_styles"
        ]["subtitle"]
        sections = []
        for event_name in sortFn(schemas):
            if not filterFn(event_name):
                continue
            sections.append(template.render(
                examples=examples[event_name],
                event=schemas[event_name],
                title_kind=subtitle_title_char
            ))
        return "\n\n".join(sections)

    def _render_http_api_group(self, group, sortPathList=None):
        template = self.env.get_template("http-api.tmpl")
        http_api = self.units.get("swagger_apis")[group]["__meta"]
        subtitle_title_char = self.units.get("spec_targets")[
            "relative_title_styles"
        ]["subtitle"]
        sections = []
        endpoints = []
        if sortPathList:
                # list of substrings to sort by
                sorted_endpoints = []
                for path_substr in sortPathList:
                    for e in http_api["endpoints"]:
                        if path_substr in e["path"]:
                            sorted_endpoints.append(e)  # could have multiple
                # dump rest
                rest = [
                    e for e in http_api["endpoints"] if e not in sorted_endpoints
                ]
                endpoints = sorted_endpoints + rest
        else:
            # sort alphabetically based on path
            endpoints = http_api["endpoints"]

        for endpoint in endpoints:
            sections.append(template.render(
                endpoint=endpoint,
                title_kind=subtitle_title_char
            ))
        return "\n\n".join(sections)


    # Special function: Returning a dict will specify multiple sections where
    # the key is the section name and the value is the value of the section
    def render_group_http_apis(self):
        # map all swagger_apis to the form $GROUP_http_api
        swagger_groups = self.units.get("swagger_apis").keys()
        renders = {}
        for group in swagger_groups:
            sortFnOrPathList = None
            if group == "presence_cs":
                sortFnOrPathList = ["status"]
            elif group == "profile_cs":
                sortFnOrPathList=["displayname", "avatar_url"]
            renders[group + "_http_api"] = self._render_http_api_group(
                group, sortFnOrPathList
            )
        return renders

    # Special function: Returning a dict will specify multiple sections where
    # the key is the section name and the value is the value of the section
    def render_group_events(self):
        # map all event schemata to the form $EVENTTYPE_event with s/.#/_/g
        # e.g. m_room_topic_event or m_room_message_m_text_event
        schemas = self.units.get("event_schemas")
        renders = {}
        for event_type in schemas:
            underscored_event_type = event_type.replace(".", "_").replace("$", "_")
            renders[underscored_event_type + "_event"] = self._render_events(
                lambda x: x == event_type, sorted
            )
        return renders

    def render_room_events(self):
        def filterFn(eventType):
            return (
                eventType.startswith("m.room") and
                not eventType.startswith("m.room.message$m.")
            )
        return self._render_events(filterFn, sorted)

    def render_msgtype_events(self):
        template = self.env.get_template("msgtypes.tmpl")
        examples = self.units.get("event_examples")
        schemas = self.units.get("event_schemas")
        subtitle_title_char = self.units.get("spec_targets")[
            "relative_title_styles"
        ]["subtitle"]
        sections = []
        msgtype_order = [
            "m.room.message$m.text", "m.room.message$m.emote",
            "m.room.message$m.notice", "m.room.message$m.image",
            "m.room.message$m.file"
        ]
        excluded_types = [
            # We exclude server notices from here because we handle them in a
            # dedicated module. We do not want to confuse developers this early
            # in the spec.
            "m.room.message$m.server_notice",
        ]
        other_msgtypes = [
            k for k in schemas.keys() if k.startswith("m.room.message$") and
            k not in msgtype_order and k not in excluded_types
        ]
        for event_name in (msgtype_order + other_msgtypes):
            if not event_name.startswith("m.room.message$m."):
                continue
            sections.append(template.render(
                example=examples[event_name][0],
                event=schemas[event_name],
                title_kind=subtitle_title_char
            ))
        return "\n\n".join(sections)

    def render_voip_events(self):
        def filterFn(eventType):
            return eventType.startswith("m.call")
        def sortFn(eventTypes):
            ordering = [
                "m.call.invite", "m.call.candidates", "m.call.answer",
                "m.call.hangup"
            ]
            rest = [
                k for k in eventTypes if k not in ordering
            ]
            return ordering + rest
        return self._render_events(filterFn, sortFn)

    def render_presence_events(self):
        def filterFn(eventType):
            return eventType.startswith("m.presence")
        return self._render_events(filterFn, sorted)

    def _render_ce_type(self, type):
        template = self.env.get_template("common-event-fields.tmpl")
        ce_types = self.units.get("common_event_fields")
        subtitle_title_char = self.units.get("spec_targets")[
            "relative_title_styles"
        ]["subtitle"]
        return template.render(
            common_event=ce_types[type], title_kind=subtitle_title_char
        )

    def render_common_event_fields(self):
        return self._render_ce_type("event")

    def render_common_room_event_fields(self):
        return self._render_ce_type("room_event")

    def render_common_state_event_fields(self):
        return self._render_ce_type("state_event")

    def render_apis(self):
        template = self.env.get_template("apis.tmpl")
        apis = self.units.get("apis")
        return template.render(apis=apis)

    def render_unstable_warnings(self):
        rendered = {}
        blocks = self.units.get("unstable_warnings")
        for var, text in blocks.items():
            rendered["unstable_warning_block_" + var] = text
        return rendered

    def render_swagger_definition(self):
        rendered = {}
        template = self.env.get_template("schema-definition.tmpl")
        subtitle_title_char = self.units.get("spec_targets")[
            "relative_title_styles"
        ]["subtitle"]
        definitions = self.units.get("swagger_definitions")
        for group, swagger_def in definitions.items():
            rendered["definition_" + group] = template.render(
                definition=swagger_def['definition'],
                examples=swagger_def['examples'],
                title_kind=subtitle_title_char)
        return rendered

    def render_sas_emoji_table(self):
        emoji = self.units.get("sas_emoji")
        rendered = ".. csv-table::\n"
        rendered += "  :header: \"Number\", \"Emoji\", \"Unicode\", \"Description\"\n"
        rendered += "  :widths: 10, 10, 15, 20\n"
        rendered += "\n"
        for row in emoji:
            rendered += "  %d, \"%s\", \"``%s``\", \"%s\"\n" % (
                row['number'],
                row['emoji'],
                row['unicode'],
                row['description'],
            )
        rendered += "\n"
        return rendered