#!/usr/bin/env python3

import sys
import os
import errno
import getopt
import stat
import re
import operator

from   xml.etree import ElementTree, ElementInclude
import xml.sax


def info(message, level=1):
    """ @brief Print info to stdout
        @param message string to print
    """
    if VERBOSE >= level:
        sys.stderr.write("*II* %s\n" % message)


def warning(message):
    """ @brief Print warning to stdout
        @param message string to print
    """
    if ERROR_ON_WARNING:
        error(message)

    if WARNING:
        sys.stderr.write("*WW* %s\n" % message)


def error(message):
    """ @brief Print error to stdout
        @param message string to print
    """
    print("*EE* %s " % message)
    sys.exit(1)


def usage(myname):
    print("\nUsage:   %s OPTIONS <XML_FILE>\n" % myname)

    print("Read configuration from an XML file and create config files.")

    print("""
%s reads a data base of configuration parameters from an XML file.
Subsequently, a number of template files are processed in order to create
configuration files (targets). See ec-conf User Guide for more information.""" % myname)

    print("""
Options: -h|--help            Print this help screen.
         -p|--platform        Set the active platform.
         -l|--list-platforms  Lists all platforms, which are defined in the XML file, on stdout.
         -d|--prefix=<PATH>   Creates the directory PATH (if non-existing) and writes all target
                                files within that directory. Also sets a configuration parameter
                                named PLT:ACTIVE:PREFIXDIR for use in the template files.
         -o|--overwrite-parameter <NAME>=<VALUE> 
                              Allows to set new values to configuration parameters from the
                                command line, overwriting the values from the XML file.
                                <NAME> must be given in the form
                                'COMPONENT_TYPE:COMPONENT_NAME:PARAMETER_NAME' corresponding to
                                the place-holder syntax of ec-conf. See ec-conf user guide for details.
                                Multiple parameters can be overwritten by repeating this option.
         -x|--write-xml       Writes the content of <XML_FILE> in XML format to stdout.
                              This can be used to normalise the XML file and for test purposes.
         -g|--gui             Starts the graphical user interface. Turns off -x and -l.
         -v|--verbose         Produces verbose output (stderr). To increase verbosity,
                                use more than once.
         -w|--no-warning      Turns off warnings (however, errors are displayed).
         -e|--error-on-warning Turns warnings into errors (i.e. ec-conf will stop).
          """)
    sys.exit(2)


class TextNode(object):
    """Represents an XML node that contains only text, i.e. is not named and has no child nodes"""

    def __init__(self):
        self.text = ""

    def add_text(self, content):
        self.text += content


class NamedNode(object):
    """Represents an XML node with a certain type according to the document
       definition. Base class for Configuration, Translation, Platform, Model,
       and Parameter"""

    def __init__(self, attr=None):

        self.name = None
        self.description = None

        if attr:
            if 'name' in list(attr.keys()):
                self.name = attr['name']

        info('Create named node: ' + self.name +
             ' (' + self.__class__.__name__ + ')', level=2)

    def set(self, key, value):
        self.__dict__.__setitem__(key, value)

    def xml(self, level=0):
        """Provides XML representation of the NamedNode as a string"""

        tabwidth = 4

        xml_string = ' ' * level * tabwidth
        xml_string += '<' + self.__class__.__name__
        if 'name' in self.__dict__ and self.name:
            xml_string += ' name="' + self.name + '"'
        xml_string += '>\n'

        for element in 'description', 'template', 'target', 'properties', 'type', 'value':
            if element in self.__dict__:
                xml_string += ' ' * (level + 1) * tabwidth
                xml_string += '<' + element.capitalize() + '>'
                if self.__dict__[element]:
                    xml_string += self.__dict__[element]
                xml_string += '</' + element.capitalize() + '>\n'

        if level <= 1:
            xml_string += '\n'

        for element in 'translation', 'platform', 'model', 'parameter':
            if element in self.__dict__:
                for child_element in self.__dict__[element]:
                    xml_string += child_element.xml(level + 1)

        xml_string += ' ' * level * tabwidth
        xml_string += '</' + self.__class__.__name__ + '>\n'

        if level > 0 and level <= 2:
            xml_string += '\n'

        return xml_string

    def plt(self, name=None):
        """Provides short-hand access to the Platform member with name 'name'."""

        if 'platform' in self.__dict__:
            if name:
                for plt in self.platform:
                    if plt.name == name:
                        return plt
            else:
                return self.platform
        return None

    def mod(self, name=None):
        """Provides short-hand access to the Model member with name 'name'."""

        if 'model' in self.__dict__:
            if name:
                for mod in self.model:
                    if mod.name == name:
                        return mod
            else:
                return self.model
        return None

    def par(self, name=None):
        """Provides short-hand access to the Parameter member with name 'name'."""

        if 'parameter' in self.__dict__:
            if name:
                for par in self.parameter:
                    if par.name == name:
                        return par
            else:
                return self.parameter
        return None


class Translation(NamedNode):
    """Represents a Translation element of the XML document type"""

    def __init__(self, attr):
        super(Translation, self).__init__(attr)
        self.template = None
        self.target = None
        self.properties = ''
        self.is_active = 1

    def set_target(self, target):
        self.target = target


class Platform(NamedNode):
    """Represents a Platform element of the XML document type"""

    def __init__(self, attr):
        super(Platform, self).__init__(attr)
        self.parameter = []
        self.translation = []


class Model(NamedNode):
    """Represents a Parameter element of the XML document type"""

    def __init__(self, attr):
        super(Model, self).__init__(attr)
        self.parameter = []


class Parameter(NamedNode):
    """Represents a Parameter element of the XML document type"""

    def __init__(self, attr):
        super(Parameter, self).__init__(attr)
        self.type = None
        self.value = None


class Configuration(NamedNode, xml.sax.handler.ContentHandler):
    """Represents a Parameter element (which is the root element) of the XML document type"""

    def __init__(self):

        self.translation = []
        self.platform = []
        self.model = []

        self.xml_file = None
        self.active_platform = None

        self.__stack = []
        self.__types = {'Translation': Translation,
                        'Platform': Platform,
                        'Model': Model,
                        'Parameter': Parameter}

    def xml(self, level=0):

        return '<?xml version="1.0"?>\n\n' \
               + super(Configuration, self).xml(level)

    def startElement(self, tag, attributes):

        info("Processing XML element '" + str(tag) + "'", level=3)

        if self.__stack:
            if tag in self.__types:
                info("Adding NamedNode for element '" + str(tag) + "'", level=3)
                self.__stack.append(self.__types[tag](attributes))
            else:
                info("Adding TextNode for element '" + str(tag) + "'", level=3)
                self.__stack.append(TextNode())
        else:
            self.__stack.append(self)

    def characters(self, content):
        if isinstance(self.__stack[-1], TextNode):
            self.__stack[-1].add_text(content)

    def endElement(self, tag):
        element = self.__stack.pop()
        if self.__stack:
            if isinstance(element, TextNode):
                self.__stack[-1].__dict__[tag.lower()] = element.text
            else:
                self.__stack[-1].__dict__[tag.lower()].append(element)

    def parse(self, file):

        info("Parsing XML file '%s'" % file)

        # A short helper function for xml.etree.ElementInclude.include
        # that parses include files and catches parse errors
        def include_loader(href, parse, encoding=None):
            if parse != "xml":
                error("Only XML includes allowed in xi:include! (see file '%s')" % href)
            try:
                with open(href) as file:
                    data = ElementTree.parse(file).getroot()
            except IOError as e:
                error("Can't open include file '%s' for parsing: %s" % (href, e))
            except ElementTree.ParseError as e:
                error("XML parse error in include file '%s': %s" % (href, e))
            return data

        # First parsing stage with xml.etree for include processing
        try:
            tree = ElementTree.parse(file)
        except IOError:
            error("Can't open file '%s' for parsing" % file)
        except ElementTree.ParseError as e:
            error("XML parse error in file '%s': %s" % (file, e))

        # Process XML include files
        tree_root = tree.getroot()
        ElementInclude.include(tree_root, loader=include_loader)

        # Second parsing stage with xml.sax to fill data structures
        # Since errors are catched in the first parsing stage, we assume
        # everything is fine here.
        xml.sax.parseString(ElementTree.tostring(tree_root, encoding="UTF-8"),self)

        info("Finished parsing '%s': Translation: %d Platform: %d Model: %d" % (
            file, len(self.translation), len(self.platform), len(self.model)))
        self.xml_file = file

    def translate(self, translation):

        rpm_ops = {
            'ADD': operator.add,
            'SUB': operator.sub,
            'MUL': operator.mul,
            'DIV': operator.truediv,
            'POW': operator.pow,
            'MOD': operator.mod
        }

        subst_re = re.compile(r"\[\[\[(?P<var>[a-zA-Z0-9_:,]+)\]\]\]")
        var_re = re.compile(r"^(\w{3}):([a-zA-Z0-9_]+):([a-zA-Z0-9_]+)$")

        def parse_var(string):

            # Shortcut for the 'prefixdir' parameter in template file
            if string.lower() == 'plt:active:prefixdir':
                return targetPrefixDir if targetPrefixDir else '[[[' + string + ']]]'

            while True:

                match = var_re.search(string)

                if not match:
                    break

                (category, component, parameter) = match.groups()

                if category.lower() == 'plt' and component.lower() == 'active':
                    component = self.active_platform

                try:
                    string = getattr(self, category.lower())(
                        component).par(parameter).value
                except BaseException:
                    warning(
                        "Unable to process '%s' (Line %d in '%s')" %
                        (string, line_number, template))
                    return '[[[' + string + ']]]'
            return string

        def parse_rpn(string):

            stack = []

            for token in string.split(','):

                token = parse_var(token)
                if not token:
                    info(
                        "Substitute expression with empty string (Line %d in '%s')" %
                        (line_number, template))

                try:
                    result = int(token)
                except ValueError:

                    try:
                        result = float(token)
                    except ValueError:

                        if token in list(rpm_ops.keys()):

                            try:
                                result = rpm_ops[token](
                                    stack.pop(-2), stack.pop())
                            except IndexError:
                                warning(
                                    "Too few arguments to execute '%s' (Line %d in '%s')" %
                                    (token, line_number, template))
                                return "[[[" + string + "]]]"
                            except BaseException:
                                warning(
                                    "Unable to execute '%s' (Line %d in '%s')" %
                                    (token, line_number, template))
                                return "[[[" + string + "]]]"
                        else:
                            result = token

                stack.append(result)

            if len(stack) > 1:
                warning(
                    "Too many operands in '%s' (Line %d in '%s')" %
                    (string, line_number, template))
                return "[[[" + string + "]]]"

            return result

        template = translation.template
        target = translation.target.strip()

        info("Translate: '%s' --> '%s'" % (template, target))

        try:
            input = open(template, 'r')
        except IOError:
            error("Can't open template file '%s' for reading" % template)

        if target:

            if targetPrefixDir:
                target = os.path.join(targetPrefixDir, target)
                path = os.path.dirname(target)

                try:
                    os.makedirs(path)
                    info("Created target prefix directory '%s'" % path)
                except OSError as exc:
                    if exc.errno == errno.EEXIST and os.path.isdir(path):
                        info(
                            "Target prefix directory '%s' exists already" %
                            (path))
                    else:
                        error(
                            "Could not create target prefix directory '%s'" %
                            (path))

            try:
                output = open(target, 'w')
            except IOError:
                error("Can't open target file '%s' for writing" % target)

            if 'executable' in translation.properties.split(','):
                os.chmod(target, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
                         stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH |
                         stat.S_IWUSR)
        else:
            output = sys.stdout

        line_number = 0
        for line in input:
            line_number += 1
            ptr = 0
            buf = ''
            for match in subst_re.finditer(line):
                buf += line[ptr:match.start()]
                var = match.groupdict()['var']
                try:
                    buf += str(parse_rpn(var))
                except UnicodeEncodeError as e:
                    error('Invalid character in XML file!\n'
                          '*EE* Look up the \\uXXXX character code from the following message:\n'
                          '*EE* %s' % str(e))
                ptr = match.end()
            buf += line[ptr:]

            output.write(buf)

        input.close()
        if target:
            output.close()

    def translate_all(self):
        for t in self.translation:
            self.translate(t)

        if self.active_platform:
            for t in self.plt(self.active_platform).translation:
                self.translate(t)


def start_gui(cfg):

    import tkinter
    import tkinter.filedialog
    import tkinter.messagebox

    class VerticalScrolledFrame(tkinter.Frame):
        """A pure Tkinter scrollable frame that actually works!

        * Use the 'interior' attribute to place widgets inside the scrollable frame
        * Construct and pack/place/grid normally
        * This frame only allows vertical scrolling

        http://tkinter.unpythonic.net/wiki/VerticalScrolledFrame

        """

        def __init__(self, parent, *args, **kw):
            tkinter.Frame.__init__(self, parent, *args, **kw)

            # create a canvas object and a vertical scrollbar for scrolling it
            vscrollbar = tkinter.Scrollbar(self, orient=tkinter.VERTICAL)
            vscrollbar.pack(
                fill=tkinter.Y,
                side=tkinter.RIGHT,
                expand=tkinter.FALSE)
            canvas = tkinter.Canvas(self, bd=0, highlightthickness=0,
                                    yscrollcommand=vscrollbar.set)
            canvas.pack(
                side=tkinter.LEFT,
                fill=tkinter.BOTH,
                expand=tkinter.TRUE)
            vscrollbar.config(command=canvas.yview)

            # reset the view
            canvas.xview_moveto(0)
            canvas.yview_moveto(0)

            # create a frame inside the canvas which will be scrolled with it
            self.interior = interior = tkinter.Frame(canvas)
            interior_id = canvas.create_window(0, 0, window=interior,
                                               anchor=tkinter.NW)

            # track changes to the canvas and frame width and sync them,
            # also updating the scrollbar
            def _configure_interior(event):
                # update the scrollbars to match the size of the inner frame
                size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
                canvas.config(scrollregion="0 0 %s %s" % size)
                if interior.winfo_reqwidth() != canvas.winfo_width():
                    # update the canvas's width to fit the inner frame
                    canvas.config(width=interior.winfo_reqwidth())
            interior.bind('<Configure>', _configure_interior)

            def _configure_canvas(event):
                if interior.winfo_reqwidth() != canvas.winfo_width():
                    # update the inner frame's width to fill the canvas
                    canvas.itemconfigure(
                        interior_id, width=canvas.winfo_width())
            canvas.bind('<Configure>', _configure_canvas)

            return

    class GUI(tkinter.Tk):

        def __init__(self, cfg):

            tkinter.Tk.__init__(self)

            self.columnconfigure(1, weight=1)
            self.rowconfigure(0, pad=10)
            self.rowconfigure(2, weight=1)

            self.__cfg = cfg
            self.__var_list = []
            self.__var_dict = {}

            for t in self.__cfg.translation + \
                    [t for p in self.__cfg.platform for t in p.translation]:
                v = tkinter.IntVar()
                v.set(1)
                v.trace(
                    'w', lambda n, i, m, t=t: t.set(
                        'is_active', int(
                            self.globalgetvar(n))))
                self.__add_var(v, t)

            self.__active_component = tkinter.StringVar()

            self.__status = tkinter.StringVar()

            self.__component_frame = None
            self.__parameter_frame = None
            self.__translation_frame = None

            self.__init_top_panel()
            self.__init_status_panel()
            self.__init_main_panel()

            self.grid_propagate(flag=0)

            self.__set_status(
                "Welcome to EC-CONF's graphical user interface!",
                time=3000)

        def __add_var(self, var, obj=None):
            self.__var_list.append(var)
            if obj:
                self.__var_dict[obj] = len(self.__var_list) - 1

        def __set_status(self, message, time=8000):

            try:
                self.after_cancel(self.__status_after_id)
            except BaseException:
                pass

            self.__default_status_message = 'Basic usage: SELECT XML database' \
                ' and template/target files, CONFIGURE parameters, and CREATE' \
                ' the configuration files.'
            self.__status.set(message)
            self.__status_after_id = self.after(
                time, self.__status.set, self.__default_status_message)

        def __init_top_panel(self):

            w = tkinter.Label(text='The active platform is:')
            w.grid(row=0, column=0)

            if not self.__cfg.active_platform:
                self.__cfg.active_platform = self.__cfg.platform[0].name

            v = tkinter.StringVar()
            v.set(self.__cfg.active_platform)
            v.trace(
                'w',
                lambda n, i, m: self.__set_status(
                    "Active platform changed to '"
                    + self.__cfg.active_platform
                    + "'"))
            v.trace(
                'w',
                lambda n, i, m: self.__fill_parameter_frame(
                    self.__parameter_frame))
            v.trace(
                'w',
                lambda n, i, m: self.__fill_component_frame(
                    self.__component_frame))
            v.trace(
                'w',
                lambda n, i, m: self.__fill_translation_frame(
                    self.__translation_frame))
            v.trace('w', lambda n, i, m: self.__cfg.set(
                'active_platform', self.globalgetvar(n)))
            self.__add_var(v)

            w = tkinter.OptionMenu(None, v, *
                                   [p.name for p in self.__cfg.platform])
            w.grid(row=0, column=1, sticky='W', padx=8)

            w = tkinter.Button(text='Select', width=10, height=2, bg='tan')
            w['command'] = self.__do_select
            w.grid(row=0, column=2, padx=8)

            w = tkinter.Button(text='Configure', width=10, height=2, bg='tan')
            w['command'] = self.__do_configure
            w.grid(row=0, column=3, padx=8)

            w = tkinter.Button(
                text='Create!',
                width=10,
                height=2,
                bg='darkgrey',
                fg='white')
            w['command'] = self.__do_create
            w.grid(row=0, column=4, padx=8)

        def __init_status_panel(self):
            w = tkinter.Label(
                textvariable=self.__status,
                height=2,
                bg='orange')
            w.grid(row=1, column=0, columnspan=5, sticky='EW', pady=5)

        def __init_main_panel(self):

            self.__init_select_panel()
            self.__init_configure_panel()

            self.__select_panel.grid_remove()
            self.__configure_panel.grid_remove()

            self.__active_main_panel = self.__select_panel
            self.__active_main_panel.grid()

        def __init_select_panel(self):

            self.__select_panel = tkinter.Frame()
            self.__select_panel.grid(
                row=2, column=0, columnspan=5, sticky='NEWS')
            self.__select_panel.columnconfigure(0, weight=1)
            self.__select_panel.rowconfigure(2, weight=1)

            f = tkinter.LabelFrame(
                self.__select_panel,
                text='XML database file')
            f.grid(sticky='NEWS')
            f.columnconfigure(1, weight=1)

            w = tkinter.Label(f, text='The current file is: ')
            w.grid(row=0, column=0, sticky='W')

            v = tkinter.StringVar()
            v.set(self.__cfg.xml_file)
            v.trace(
                'w', lambda n, i, m: self.__cfg.set(
                    'xml_file', self.globalgetvar(n)))
            self.__add_var(v, 'xml_file')

            w = tkinter.Label(f, textvariable=v, bg='darkgrey', fg='white')
            w.grid(row=0, column=1, sticky='W')

            w = tkinter.Button(f, text='Save as', width=8)
            w['command'] = lambda: self.__save_as_xml_file()
            w.grid(row=0, column=2, sticky='E', padx=4, pady=5)

            w = tkinter.Button(f, text='Save', width=8)
            w['command'] = lambda: self.__save_xml_file()
            w.grid(row=0, column=3, sticky='E', padx=4, pady=5)

            tkinter.Frame(self.__select_panel).grid(pady=5)

            self.__translation_frame = tkinter.LabelFrame(
                self.__select_panel, text='Templates and Targets')
            self.__translation_frame.grid(sticky='NEWS')
            self.__translation_frame.columnconfigure(1, weight=1)
            self.__translation_frame.columnconfigure(3, weight=9)

            self.__fill_translation_frame(self.__translation_frame)

        def __init_configure_panel(self):

            self.__configure_panel = tkinter.Frame()
            self.__configure_panel.grid(
                row=2, column=0, columnspan=5, sticky='NEWS')
            self.__configure_panel.rowconfigure(0, weight=1)
            self.__configure_panel.columnconfigure(2, weight=1)

            # Component frame
            self.__component_frame = tkinter.LabelFrame(
                self.__configure_panel, text='Configurable components')
            self.__component_frame.grid(sticky='NEWS')

            self.__fill_component_frame(self.__component_frame)

            # Spacer frame
            f = tkinter.Frame(self.__configure_panel)
            f.grid(row=0, column=1, padx=2)

            # Parameter frame
            f = tkinter.LabelFrame(
                self.__configure_panel,
                text='Configuration parameters')
            f.grid(row=0, column=2, sticky='NEWS')

            f = VerticalScrolledFrame(f)
            f.pack(fill=tkinter.BOTH, expand=tkinter.TRUE)

            self.__parameter_frame = f.interior
            self.__parameter_frame.columnconfigure(2, weight=1)

            self.__fill_parameter_frame(self.__parameter_frame)

        def __fill_translation_frame(self, frame):

            for w in list(frame.children.values()):
                w.destroy()

            v = tkinter.IntVar()
            v.set(1)
            v.trace('w', lambda n,
                                i,
                                m: [self.__var_list[self.__var_dict[t]].set(self.globalgetvar(n))
                                    for t in self.__cfg.translation +
                                    self.__cfg.plt(self.__cfg.active_platform).translation])
            self.__add_var(v)

            w = tkinter.Checkbutton(
                frame, text='Activate/deactivate all', variable=v)
            w.grid(row=0, column=0, sticky='W', pady=5)

            r = 1
            for t in self.__cfg.translation + \
                    self.__cfg.plt(self.__cfg.active_platform).translation:

                w = tkinter.Checkbutton(
                    frame, text=t.description, variable=self.__var_list[self.__var_dict[t]])
                w.grid(row=r, column=0, sticky='W', pady=5)

                w = tkinter.Label(
                    frame,
                    text=t.template,
                    bg='darkgrey',
                    fg='white')
                w.grid(row=r, column=1, sticky='E')

                w = tkinter.Label(frame, text=' --> ')
                w.grid(row=r, column=2)

                v = tkinter.StringVar()
                v.set(t.target)
                v.trace(
                    'w', lambda n, i, m, t=t: t.set_target(
                        self.globalgetvar(n)))
                self.__add_var(v)
                w = tkinter.Entry(frame, textvariable=v)

                w.grid(row=r, column=3, sticky='EW')
                r += 1

        def __fill_component_frame(self, frame):

            for w in list(frame.children.values()):
                w.destroy()

            if self.__cfg.active_platform:

                self.__active_component.set(self.__cfg.active_platform)

                w = tkinter.Label(frame, text='Active platform')
                w.pack(anchor='w')

                c = self.__cfg.plt(self.__cfg.active_platform)
                w = tkinter.Radiobutton(
                    frame,
                    text=c.name,
                    variable=self.__active_component,
                    value=c.name)
                w['command'] = lambda: self.__fill_parameter_frame(
                    self.__parameter_frame)
                w.pack(anchor='w', pady=5)

            if self.__cfg.model:

                if not self.__active_component.get():
                    self.set(self.__cfg.model[0].name)

                w = tkinter.Label(frame, text='Configurable models')
                w.pack(anchor='w')

                for c in self.__cfg.model:
                    w = tkinter.Radiobutton(
                        frame, text=c.name, variable=self.__active_component, value=c.name)
                    w['command'] = lambda: self.__fill_parameter_frame(
                        self.__parameter_frame)
                    w.pack(anchor='w', pady=5)

        def __fill_parameter_frame(self, frame):

            for w in list(frame.children.values()):
                w.destroy()

            for (name, component) in [(c.name, c)
                                      for c in self.__cfg.platform + self.__cfg.model]:
                if self.__active_component.get() == name:
                    break

            r = 0
            for p in component.par():

                w = tkinter.Label(
                    frame, text=p.description, anchor="w", width=35)
                w.grid(row=r, column=0, sticky='W')

                w = tkinter.Label(frame, text='[' + str(p.name) + ']')
                w.grid(row=r, column=1, sticky='W', padx=20)

                v = tkinter.StringVar()
                v.set(p.value)
                v.trace(
                    'w', lambda n, i, m, p=p: p.set(
                        'value', self.globalgetvar(n)))
                self.__add_var(v)
                w = tkinter.Entry(frame, textvariable=v)
                w.grid(row=r, column=2, sticky='EW')

                r += 1

            self.__set_status(
                'Configure parameters for component \'' +
                self.__active_component.get() +
                '\'')

        def __do_select(self):
            self.__active_main_panel.grid_remove()
            self.__active_main_panel = self.__select_panel
            self.__active_main_panel.grid()
            self.__set_status(
                'Select the XML data base file and active translations in the panel below.')

        def __do_configure(self):
            self.__active_main_panel.grid_remove()
            self.__active_main_panel = self.__configure_panel
            self.__active_main_panel.grid()
            self.__set_status('Configure the configuration parameters for the'
                              ' available components in the panel below.')

        def __do_create(self):

            fw = []
            for t in self.__cfg.translation + \
                    self.__cfg.plt(self.__cfg.active_platform).translation:
                if t.is_active:
                    self.__cfg.translate(t)
                    fw.append(t.target)

            if fw:
                msg = 'Active target files written: ' + fw.pop()
                while fw:
                    msg += ', ' + fw.pop()
            else:
                msg = 'No targets where written'

            self.__set_status(msg)

        def __save_as_xml_file(self):
            f = tkinter.filedialog.asksaveasfilename(
                title='Select a file name for saving:', filetypes=[
                    ('XML files', '*.xml'), ('All files', '*')])
            if f:
                try:
                    self.__var_list[self.__var_dict['xml_file']].set(
                        os.path.relpath(f))
                except AttributeError:
                    self.__var_list[self.__var_dict['xml_file']].set(
                        os.path.realpath(f))
                self.__save_xml_file()
            else:
                self.__set_status("Current XML file NOT saved")

        def __save_xml_file(self):

            if os.path.isfile(self.__cfg.xml_file):
                msg = "The file '" + self.__cfg.xml_file + \
                    "' exists. Do you want to replace it?"
                if not tkinter.messagebox.askyesno('Save XML file', msg):
                    return

            try:
                f = open(self.__cfg.xml_file, 'w')
            except IOError:
                msg = "The file '" + self.__cfg.xml_file + "' could not be opened for writing"
                tkinter.messagebox.showerror('Save XML file', msg)
                self.__set_status("XML database NOT saved")
                return

            f.write(self.__cfg.xml())
            f.close()
            self.__set_status(
                "XML database saved to file '" +
                self.__cfg.xml_file +
                "'")

    root = GUI(cfg)
    root.title('ec-conf GUI')

    min_window_width = min(900, int(0.9 * root.winfo_screenwidth()))
    min_window_height = min(800, int(0.9 * root.winfo_screenheight()))
    root.minsize(min_window_width, min_window_height)

    root.resizable()
    root.mainloop()


if __name__ == "__main__":

    # Try to get command line options and arguments
    try:
        opts, args = getopt.getopt(sys.argv[1:],
                                   "hp:gd:o:xlvwe",
                                   ["help", "platform=", "gui", "prefix=",
                                    "overwrite-parameter=",
                                    "write-xml", "list-platforms",
                                    "verbose", "no-warning", "error-on-warning"])
    except getopt.GetoptError:
        usage(os.path.split(sys.argv[0])[-1])

    # Default values, to be overwritten by command line options
    WARNING = True
    ERROR_ON_WARNING = False
    VERBOSE = 0
    writeXML = False
    listPlatforms = False
    wantGUI = False
    platform = None
    targetPrefixDir = None
    overwriteParameters = []

    # Parse command line options
    for opt, arg in opts:
        if opt in ('-h', '--help'):
            usage(os.path.split(sys.argv[0])[-1])
        elif opt in ('-p', '--platform'):
            platform = arg
        elif opt in ('-g', '--gui'):
            wantGUI = True
        elif opt in ('-d', '--prefix'):
            targetPrefixDir = arg
        elif opt in ('-o', '--overwrite-parameter'):
            overwriteParameters.append(arg)
        elif opt in ('-x', '--write-xml'):
            writeXML = True
        elif opt in ('-l', '--list-platforms'):
            listPlatforms = True
        elif opt in ('-v', '--verbose'):
            VERBOSE += 1
        elif opt in ('-w', '--no-warning'):
            WARNING = False
        elif opt in ('-e', '--error-on-warning'):
            ERROR_ON_WARNING = True

    # The XML file is all that should be left on the command line
    if len(args) != 1:
        usage(os.path.split(sys.argv[0])[-1])

    # Create the Configuration object and fill the data structures by parsing
    # the XML file
    cfg = Configuration()
    cfg.parse(args[0])
    info(args[0])
    info(cfg.parse(args[0]))
    info(platform)

    # If a platform was given on the command line, try to set it
    if platform:
        info(cfg.plt(platform))
        if cfg.plt(platform):
            cfg.active_platform = platform
        else:
            error(
                "Platform '%s' not defined in the configuration file '%s'" %
                (platform, args[0]))
    elif not (wantGUI or listPlatforms):
        warning("No active platform given")

    # Overwrite parameters given explicitely on the command line
    for arg in overwriteParameters:

        # split the name=value pair but make sure additional '=' are preserved
        name = arg.split('=')[0]
        value = '='.join(arg.split('=')[1:])

        # Maybe we want to allow overwriting with empty values?!
        #   if so, remove the following if block
        if not value:
            warning("Parameter '%s' in --overwrite-parameter has no value" % name)
            continue

        try:
            (category, component, parameter) = name.split(':')
        except ValueError:
            warning(
                "Malformed parameter name given to --overwrite-parameter: '%s'" % name)
            continue

        if category.lower() == 'plt' and component.lower() == 'active':
            component = cfg.active_platform

        try:
            getattr(cfg, category.lower())(
                component).par(parameter).value = value
            info("Overwriting parameter '%s' with value '%s'" % (name, value))
        except AttributeError:
            warning(
                "Non-existing parameter name given to --overwrite-parameter: '%s'" % name)
            continue

    # Select activity to be done according to the command line options
    # Default is to translate all Translations in the Configuration
    if wantGUI:
        info("Starting GUI")
        start_gui(cfg)
    elif listPlatforms:
        if cfg.plt():
            print('\n'.join([p.name for p in cfg.plt()]))
        else:
            warning("No platforms defined in XML file")
    elif writeXML:
        sys.stdout.write(cfg.xml())
    else:
        cfg.translate_all()
