#!/usr/bin/python -B
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.
#
#
# asfreader.py -- Pelican plugin that processes ezt template Markdown through ezt and  then GitHub Flavored Markdown.
#

import sys
import io
import os
import traceback

import re
import ezt

import pelican.plugins.signals
import pelican.readers
import pelican.settings

# The gfm plugin should have been loaded before this one.
### TODO: maybe buildsite.py can sort gfm to the front of the list.
import gfm


METADATA_RE = re.compile(r'\[{\s*(?P<meta>[-._:a-zA-Z0-9\[\]]+)\s*}\]')


class ASFTemplateReader(ezt.Reader):
    """Enables inserts relative to the template we loaded."""

    def __init__(self, source_path, text):
        self.source_dir, self.fname = os.path.split(source_path)
        self.text = text

    def read_other(self, relative):
        return ezt._FileReader(os.path.join(self.source_dir, relative)) # pylint: disable=protected-access

    def filename(self):
        return self.fname


class ASFReader(gfm.GFMReader):
    """GFM-flavored Reader for the Pelican system that adds ASF data and ezt
    generation prior to processing the GFM
    """

    def add_data(self, text, metadata):
        "Mix in ASF data as metadata"

        asf_metadata = self.settings.get('ASF_DATA', { }).get('metadata')
        if asf_metadata:
            metadata.update(asf_metadata)
            # insert any direct references
            m = 1
            while m:
                m = METADATA_RE.search(text)
                if m:
                    this_data = m.group(1).strip()
                    format_string = '{{{0}}}'.format(this_data)
                    try:
                        new_string = format_string.format(**metadata)
                        print(f'{{{{{m.group(1)}}}}} -> {new_string}')
                    except Exception:
                        # the data expression was not found
                        new_string = format_string
                        print(f'{{{{{m.group(1)}}}}} is not found')
                    text = re.sub(METADATA_RE, new_string, text, count=1)
        return text, metadata

    def read(self, source_path):
        "Read metadata and content, process content as ezt template, then render into HTML."
        try:
            # read content with embedded ezt - use GFMReader
            text, metadata = super().read_source(source_path)
            assert text
            assert metadata
            # supplement metadata with ASFData if available
            text, metadata = self.add_data(text, metadata)
            # prepare text as an ezt template
            # compress_whitespace=0 is required as blank lines and indentation have meaning in markdown.
            template = ezt.Template(compress_whitespace=0)
            assert template
            reader = ASFTemplateReader(source_path, text)
            template.parse(reader, base_format=ezt.FORMAT_HTML)
            # generate content from ezt template with metadata
            fp = io.StringIO()
            template.generate(fp, metadata)
            # Render the markdown into HTML
            content = super().render(fp.getvalue().encode('utf-8')).decode('utf-8')
            assert content
        except Exception:
            print('-----', file=sys.stderr)
            print('ERROR: %s' % (source_path), file=sys.stderr)
            traceback.print_exc()
            raise

        return content, metadata


# The following are required or ezmd files are not read instead they are static.
# For direct subclasses of BaseReader like GFMReader the following two
# callables are optional if the class includes enabled=True and file_extenaions.
def add_readers(readers):
    readers.reader_classes['ezmd'] = ASFReader


def register():
    pelican.plugins.signals.readers_init.connect(add_readers)
