from __future__ import unicode_literals
import xml.etree.ElementTree as ET

from clldutils.inifile import INI
from clldutils.path import Path, as_posix

# The standard library XML parser does not give access to comments, which we
# need.  The following extended parser remedies this.  # Code taken from
# https://stackoverflow.com/a/27333347, which was in turn based on Fredrik Lundh's
# code at http://effbot.org/zone/element-pi.htm

_generated_str = "Generated by BEASTling"
_config_file_str = "Original config file:"
_proggen_str = "Configuration built programmatically"
_do_not_edit_str = "Please DO NOT manually edit this file"
_data_file_str = "BEASTling embedded data file"


if getattr(ET, 'XMLTreeBuilder', None):  # pragma: no cover
    # Ensure compatibility with Python 2.7
    class CommentParser(ET.XMLTreeBuilder):
        def __init__(self):
            ET.XMLTreeBuilder.__init__(self)
            self._parser.CommentHandler = self.comment

        def comment(self, data):
            self._target.start(ET.Comment, {})
            self._target.data(data)
            self._target.end(ET.Comment)

        @classmethod
        def get_parser(cls):
            return CommentParser()
else:  # pragma: no cover
    class CommentParser(ET.TreeBuilder):
        def comment(self, data):
            self.start(ET.Comment, {})
            self.data(data)
            self.end(ET.Comment)

        @classmethod
        def get_parser(cls):
            return ET.XMLParser(target=cls())


def read_comments(filename):
    parser = CommentParser.get_parser()
    with open(as_posix(filename), "rb") as fp:
        parser.feed(fp.read())
    return [e for e in parser.close() if e.tag == ET.Comment]


def extract(filename, overwrite=False):
    messages = []
    comments = read_comments(filename)
    beastling_confs = [c for c in comments if c.text.startswith(_generated_str)]
    if not len(beastling_confs) == 1:
        # Zero or several embedded configs - is this one of our files?!
        raise ValueError("%s doesn't look like a BEASTling-generated XML file" % filename)
    messages.append(write_config(beastling_confs[0].text, overwrite))
    
    data_files = [c for c in comments if c.text.startswith(_data_file_str)]
    for data_file in data_files:
        messages.append(write_data_file(data_file.text, overwrite))

    return [msg for msg in messages if msg]


def write_config(comment_text, overwrite):
    lines = comment_text.split("\n")
    lines = [l for l in lines if l]
    assert lines[1] in (_config_file_str, _proggen_str)
    if lines[1] == _proggen_str:
        return "Original configuration was generated programmatically, no configuration to extract."
    truths = [_do_not_edit_str in line for line in lines]
    if any(truths):
        lines = lines[0:truths.index(True)]
    config_text = "\n".join(lines[2:])
    p = INI()
    p.read_string(config_text)
    filename = p.get("admin", "basename") \
        if p.has_option("admin", "basename") else 'beastling'
    filename = Path(filename + '.conf')
    if filename.exists() and not overwrite:
        return "BEASTling configuration file %s already exists!  Run beastling with the --overwrite option if you wish to overwrite it.\n" % filename
    if not filename.parent.exists():
        filename.parent.mkdir()

    p.write(filename)
    return "Wrote BEASTling configuration file %s.\n" % filename


def write_data_file(comment_text, overwrite):
    lines = comment_text.split("\n")
    filename = Path(lines[0].split(":",1)[1].strip())
    if filename.exists() and not overwrite:
        return "Embedded data file %s already exists!  Run beastling with the --overwrite option if you wish to overwrite it.\n" % filename
    if not filename.parent.exists():
        filename.parent.mkdir()
    with filename.open("w", encoding='utf8') as fp:
        fp.write("\n".join(lines[1:]))
    return "Wrote embedded data file %s.\n" % filename
