# Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
# All rights reserved.
# This component and the accompanying materials are made available
# under the terms of the License "Eclipse Public License v1.0"
# which accompanies this distribution, and is available
# at the URL "http://www.eclipse.org/legal/epl-v10.html".
#
# Initial Contributors:
# Nokia Corporation - initial contribution.
#
# Contributors:
#
from __future__ import with_statement
try:
    from xml.etree import cElementTree as etree
except ImportError:
    from xml.etree import ElementTree as etree
from orb.lib import xml_decl
import logging
import os
import copy


logger = logging.getLogger('orb.mapcreators.toplevel')


def component_map_lookup(rel_dir, mapname):
    "Checks the file system to see if a component level map exists"
    exists = os.path.exists(os.path.join(rel_dir, mapname))
    if not exists:
        logger.debug("Asked for a component map for '%s' but it does not exist" % mapname)
    return exists


class TopLevelMapCreator(object):
    """
    Uses the information in a SystemDefinition object to create a top level map that
    links to all the component level maps together.
    """
    def __init__(self, sysdef, rel_dir, lookupmethod=component_map_lookup):
        self.sysdef = sysdef
        self._rel_dir = rel_dir
        self._lookup = lookupmethod
        self._root = etree.Element("map", title=sysdef.name, id=sysdef.name)

    def _add_subelems(self, parent, item):
        return etree.SubElement(parent, "topichead", navtitle=item.name, id=item.id)

    def getmap(self):
        def sort(item):
            return item.name
        layers = self.sysdef.layers
        layers.sort(key=sort)
        for layer in layers:
            layer_elem = self._add_subelems(self._root, layer)
            packages = layer.packages
            packages.sort(key=sort)
            for package in packages:
                pkg_elem = self._add_subelems(layer_elem, package)
                collections = package.collections
                collections.sort(key=sort)                
                for collection in collections:                    
                    col_elem = self._add_subelems(pkg_elem, collection)
                    components = collection.components
                    components.sort(key=sort)
                    for item in components:                        
                        if self._lookup(self._rel_dir, "cmp_%s.ditamap" % item.id):
                            col_header = self._add_subelems(col_elem, item) 
                            etree.SubElement(col_header, "topicref", navtitle=item.name, href="cmp_%s.ditamap" % item.id, format="ditamap")
        self._remove_empty_headings()
        return self._root

    def write(self, path):
        with open(path, "w") as f:
            f.write(xml_decl()+"\n")
            f.write(etree.tostring(self.getmap()))
            
    def _remove_empty_headings(self):
        new_root = copy.deepcopy(self._root)
        empty_nodes = True
        nodes_removed = 0
        while empty_nodes:
            nodes_removed = 0
            for parent, newparent in zip(self._root.getiterator(), new_root.getiterator()):
                for child, newchild in zip(parent, newparent):
                    if len(list(child)) == 0 and not 'href' in child.attrib:
                        newparent.remove(newchild)
                        nodes_removed += 1
            self._root = copy.deepcopy(new_root)
            if nodes_removed == 0:
                empty_nodes = False
        self._root = new_root  


################################################################
# Unit test code
################################################################
import unittest
from _shared import StubSysdef


def mock_component_map_lookup(mapname, rel_dir):
    return True


class TestTopLevelMapCreator(unittest.TestCase):
    def setUp(self):
        self.mc = TopLevelMapCreator(StubSysdef(), ".", mock_component_map_lookup)
        
    def test_i_create_a_map_root_from_a_system_definition(self):                
        root = self.mc.getmap()
        self.assertTrue(root.tag == "map")
        self.assertTrue(root.attrib['title'] == "Symbian^3")
        self.assertTrue(root.attrib['id'] == "Symbian^3")

    def test_i_create_correct_topicheads_from_layers(self):
        root = self.mc.getmap()
        self.assertEquals(len(root.findall("topichead")), 1)
        os_elem = root.findall("topichead")[0]
        self.assertEqual(os_elem.attrib["navtitle"], "OS")
        self.assertEqual(os_elem.attrib["id"], "os")

    def test_i_create_correct_topicheads_from_packages(self):
        root = self.mc.getmap()
        os_elem = root.findall("topichead")[0]
        self.assertEquals(len(os_elem.findall("topichead")), 1)
        pkg_elem = os_elem.findall("topichead")[0]        
        self.assertEqual(pkg_elem.attrib["navtitle"], "Kernel and Hardware Services")
        self.assertEqual(pkg_elem.attrib["id"], "kernelhwsrv")

    def test_i_create_correct_topicheads_from_collections(self):
        root = self.mc.getmap()
        os_elem = root.findall("topichead")[0]
        pkg_elem = os_elem.findall("topichead")[0]
        self.assertEquals(len(pkg_elem.findall("topichead")), 1)
        bootldr_elem = pkg_elem.findall("topichead")[0]
        self.assertEqual(bootldr_elem.attrib["navtitle"], "Board Boot Loader")
        self.assertEqual(bootldr_elem.attrib["id"], "brdbootldr")

    def test_i_create_correct_topicheads_for_components(self):
        root = self.mc.getmap()
        os_elem = root.findall("topichead")[0]
        pkg_elem = os_elem.findall("topichead")[0]
        com_elem = pkg_elem.findall("topichead")[0]
        comp_elem_header = com_elem.findall("topichead")[0]        
        self.assertEqual(comp_elem_header.attrib["navtitle"], "Boot Loader")
        self.assertEqual(comp_elem_header.attrib["id"], "ubootldr")

    def test_i_create_correct_topicrefs_from_components(self):
        root = self.mc.getmap()
        os_elem = root.findall("topichead")[0]
        pkg_elem = os_elem.findall("topichead")[0]
        comp_elem = pkg_elem.findall("topichead")[0]
        comp_elem_header = comp_elem.findall("topichead")[0]
        self.assertEquals(len(comp_elem_header.findall("topicref")), 1)
        ubootldr_elem = comp_elem_header.findall("topicref")[0]
        self.assertEqual(ubootldr_elem.attrib["navtitle"], "Boot Loader")
        self.assertEqual(ubootldr_elem.attrib["href"], "cmp_ubootldr.ditamap")
        self.assertEqual(ubootldr_elem.attrib["format"], "ditamap")

    def test_i_can_remove_empty_headings_from_a_top_level_map(self):
        self.mc._root = etree.fromstring(empty_headings_toc)
        self.mc._remove_empty_headings()
        self.assertEquals(etree.tostring(self.mc._root), removed_empty_headings_toc)

    def test_i_sort_the_map(self):
        root = self.mc.getmap()
        os_elem = root.findall("topichead")[0]
        pkg_elem = os_elem.findall("topichead")[0]
        com_elem = pkg_elem.findall("topichead")[0]
        comp_elem_header = com_elem.findall("topichead")[0]        
        self.assertEqual(comp_elem_header.attrib["navtitle"], "Boot Loader")
        comp_elem_header = com_elem.findall("topichead")[1]        
        self.assertEqual(comp_elem_header.attrib["navtitle"], "Kernel Architecture")


empty_headings_toc = """\
<map id="Symbian^3" title="Symbian^3">
  <topichead id="os" navtitle="OS" >
    <topichead id="kernelhwsrv" navtitle="Kernel and Hardware Services">
      <topichead id="brdbootldr" navtitle="Board Boot Loader">
        <topichead id="ubootldr" navtitle="Boot Loader"/>
      </topichead>
      <topichead id="bsptemplate" navtitle="Board Support Package Template">
        <topichead id="asspandvariant" navtitle="Template ASSP and Variant">
          <topicref format="ditamap" href="cmp_template_variant.ditamap" navtitle="template_variant" />
        </topichead>
      </topichead>      
    </topichead>
  </topichead>
</map>"""


removed_empty_headings_toc = """\
<map id="Symbian^3" title="Symbian^3">
  <topichead id="os" navtitle="OS">
    <topichead id="kernelhwsrv" navtitle="Kernel and Hardware Services">
      <topichead id="bsptemplate" navtitle="Board Support Package Template">
        <topichead id="asspandvariant" navtitle="Template ASSP and Variant">
          <topicref format="ditamap" href="cmp_template_variant.ditamap" navtitle="template_variant" />
        </topichead>
      </topichead>      
    </topichead>
  </topichead>
</map>""" 