# vim: ai ts=4 sts=4 et sw=4
#
# Copyright (C) 2010, 2011, 2012, 2013, 2014 Intel, Inc.
#
#    This program is free software; you can redistribute it and/or
#    modify it under the terms of the GNU General Public License
#    as published by the Free Software Foundation; version 2 of the License.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
"""
This module takes care of handling/generating/parsing of build.xml
"""

import re

from collections import OrderedDict
from xml.dom import minidom

class BuildDataError(Exception):
    """Custom BuildData exception."""
    pass

class BuildData(object):
    """Class for handling build data."""

    # fixing of buggy xml.dom.minidom.toprettyxml
    XMLTEXT_RE = re.compile(r'>\n\s+([^<>\s].*?)\n\s+</', re.DOTALL)

    def __init__(self, build_id=None):
        self.build_id = build_id
        self.base_id = None
        self.ref_id = None
        self.targets = OrderedDict()

    def add_target(self, target):
        """Add (or update) target to the list of targets."""
        self.targets[target["name"]] = target

    def set_base_id(self, base_id):
        """ Set Base id """
        self.base_id = base_id

    def set_ref_id(self, ref_id):
        """ Set Ref id """
        self.ref_id = ref_id

    def load(self, xml):
        """Load build data from string."""

        def get_elem(node, name):
            """Helper: get first element by tag name."""
            try:
                return node.getElementsByTagName(name)[0]
            except IndexError:
                raise BuildDataError("Error: <%s><%s> not found" % \
                                     (node.nodeName, name))
        dom = minidom.parseString(xml)
        btargets = get_elem(dom, "buildtargets")

        # load archs list
        archs = {}
        for arch_index, arch_value in \
                enumerate(btargets.getElementsByTagName('archs')):
            arch_list = []
            for arch in arch_value.getElementsByTagName('arch'):
                if arch.firstChild.data:
                    arch_list.append(arch.firstChild.data)
            archs[arch_index] = arch_list
        # load buildtarget
        for btarget_index, btarget in \
                enumerate(btargets.getElementsByTagName("buildtarget")):
            target = {
                    'name': btarget.getAttribute("name"),
                    'repos': [],
                    'archs': archs.get(btarget_index)
                    }

            try:
                bconf = get_elem(btarget, "buildconf")
                target["buildconf"] = {
                    "location":
                        get_elem(bconf, "location").getAttribute("href"),
                    "checksum":
                        {"type":
                             get_elem(bconf, "checksum").getAttribute("type"),
                         "value":
                             get_elem(bconf, "checksum").firstChild.data}
                    }
            except BuildDataError:
                pass

            # Get archs
            for repo in btarget.getElementsByTagName("repo"):
                barch = repo.getAttribute("arch")
                target["repos"].append((repo.getAttribute("type"), barch,
                                        repo.firstChild.data))

            self.targets[target["name"]] = target

    def to_xml(self, hreadable=True):
        """Format build data as xml."""
        content = '<?xml version="1.0"?><build version="1.0">'\
                  '<id>%s</id>' % self.build_id
        # set base id
        if self.base_id:
            content += '<base_id>%s</base_id>' % self.base_id

        # set ref id
        if self.ref_id:
            content += '<ref_id>%s</ref_id>' % self.ref_id

        # build targets
        content += '<buildtargets>'
        for name in self.targets:
            target = self.targets[name]
            content += '<buildtarget name="%s">' % name

            if 'buildconf' in target:
                # buildconf
                content += '<buildconf>'
                buildconf = target['buildconf']
                content += '<location href="%s"/>' % buildconf['location']
                content += '<checksum type="%s">%s</checksum>' % \
                    (buildconf['checksum']['type'],
                     buildconf['checksum']['value'])
                content += '</buildconf>'

            # list of architectures
            content += '<archs>'
            for arch in set(target['archs']):
                content += '<arch>%s</arch>' % arch
            content += '</archs>'

            # repos
            # pylint: disable=W0612
            # disable unused variable
            for rtype, rarch, rpath in target["repos"]:
                if rtype == 'noarch':
                    rtype = 'binary'
                target_repo_line = '<repo type="%s">%s</repo>' % (rtype, rpath)
                if target_repo_line in content:
                    continue
                content += target_repo_line

            content += '</buildtarget>'
            # pylint: enable=W0612
        content += '</buildtargets></build>'

        # make it human readable
        if hreadable:
            dom = minidom.parseString(content)
            content = self.XMLTEXT_RE.sub(r'>\g<1></',
                                          dom.toprettyxml(indent='  '))

        return content

    def save(self, fname, hreadable=True):
        """Save builddata to file."""
        # Write content down
        with open(fname, 'w') as out:
            out.write(self.to_xml(hreadable))
