#!/usr/bin/python
# -*- coding: utf-8 -*-

import appier

import xml.dom.minidom

from . import ast

DEFAULT_CHARSET = "utf-8"
""" The default charset """


class Parser(object):
    """
    The abstract parser class
    """

    def __init__(self):
        """
        Constructor of the class
        """

        pass

    def parse(self):
        """
        Parses the defined file
        """

        pass

    def get_value(self):
        """
        Retrieves the result of the parse

        :rtype: Object
        :return: The result of the parse
        """

        pass

    def parse_element_attributes(self, node, element):
        attributes = node.attributes

        for index in range(attributes.length):
            attribute_node = attributes.item(index)
            attribute_node_name = attribute_node.name
            attribute_node_value = attribute_node.value
            setattr(element, attribute_node_name, attribute_node_value)


class PrintingLanguageParser(Parser):
    """
    The printing language parser class.
    """

    file = None
    """ The file path """

    string = None
    """ The string contents """

    printing_document = None
    """ The printing document """

    def __init__(self, file=None, string="none"):
        Parser.__init__(self)
        self.file = file
        self.string = string

    def parse(self):
        self.load_printing_language_file(self.file)

    def parse_string(self):
        self.load_printing_language_string(self.string)

    def get_value(self):
        return self.printing_document

    def get_build_automation(self):
        return self.printing_document

    def load_printing_language_file(self, file):
        # creates the XML document DOM object
        xml_document = xml.dom.minidom.parse(file)

        self.load_printing_language(xml_document)

    def load_printing_language_string(self, string):
        # creates the XML document DOM object, the provided
        # string is encoded to avoid possible parsing problems
        string = (
            type(string) == appier.legacy.UNICODE
            and string.encode(DEFAULT_CHARSET)
            or string
        )
        xml_document = xml.dom.minidom.parseString(string)

        self.load_printing_language(xml_document)

    def load_printing_language(self, xml_document):
        child_nodes = xml_document.childNodes

        for child_node in child_nodes:
            if valid_node(child_node):
                self.printing_document = self.parse_printing_document(child_node)

    def parse_printing_document(self, printing_document):
        printing_document_structure = ast.PrintingDocument()
        child_nodes = printing_document.childNodes

        # parses the element attributes
        self.parse_element_attributes(printing_document, printing_document_structure)

        for child_node in child_nodes:
            if valid_node(child_node):
                self.parse_printing_document_element(
                    child_node, printing_document_structure
                )

        return printing_document_structure

    def parse_printing_document_element(
        self, printing_document_element, printing_document
    ):
        node_name = printing_document_element.nodeName
        printing_document_child_nodes = printing_document.child_nodes

        if node_name == "block":
            printing_document_child_nodes.append(
                self.parse_block(printing_document_element)
            )
        elif node_name == "paragraph":
            printing_document_child_nodes.append(
                self.parse_paragraph(printing_document_element)
            )
        elif node_name == "line":
            printing_document_child_nodes.append(
                self.parse_line(printing_document_element)
            )
        elif node_name == "text":
            printing_document_child_nodes.append(
                self.parse_text(printing_document_element)
            )
        elif node_name == "image":
            printing_document_child_nodes.append(
                self.parse_image(printing_document_element)
            )

    def parse_block(self, block):
        block_structure = ast.Block()
        child_nodes = block.childNodes

        # parses the element attributes
        self.parse_element_attributes(block, block_structure)

        for child_node in child_nodes:
            if valid_node(child_node):
                self.parse_block_element(child_node, block_structure)

        return block_structure

    def parse_block_element(self, block_element, block):
        node_name = block_element.nodeName
        block_child_nodes = block.child_nodes

        if node_name == "text":
            block_child_nodes.append(self.parse_text(block_element))
        elif node_name == "image":
            block_child_nodes.append(self.parse_image(block_element))

    def parse_paragraph(self, paragraph):
        paragraph_structure = ast.Paragraph()
        child_nodes = paragraph.childNodes

        # parses the element attributes
        self.parse_element_attributes(paragraph, paragraph_structure)

        for child_node in child_nodes:
            if valid_node(child_node):
                self.parse_paragraph_element(child_node, paragraph_structure)

        return paragraph_structure

    def parse_paragraph_element(self, paragraph_element, paragraph):
        node_name = paragraph_element.nodeName
        paragraph_child_nodes = paragraph.child_nodes

        if node_name == "line":
            paragraph_child_nodes.append(self.parse_line(paragraph_element))
        elif node_name == "text":
            paragraph_child_nodes.append(self.parse_text(paragraph_element))
        elif node_name == "image":
            paragraph_child_nodes.append(self.parse_image(paragraph_element))

    def parse_line(self, line):
        line_structure = ast.Line()
        child_nodes = line.childNodes

        # parses the element attributes
        self.parse_element_attributes(line, line_structure)

        for child_node in child_nodes:
            if valid_node(child_node):
                self.parse_line_elements(child_node, line_structure)

        return line_structure

    def parse_line_elements(self, line_element, line):
        node_name = line_element.nodeName
        line_child_nodes = line.child_nodes

        if node_name == "text":
            line_child_nodes.append(self.parse_text(line_element))
        elif node_name == "image":
            line_child_nodes.append(self.parse_image(line_element))

    def parse_text(self, text):
        text_structure = ast.Text()

        # parses the element attributes
        self.parse_element_attributes(text, text_structure)

        if text.firstChild:
            text_structure.text = text.firstChild.data.strip()
        else:
            text_structure.text = str()

        return text_structure

    def parse_image(self, image):
        image_structure = ast.Image()

        # parses the element attributes
        self.parse_element_attributes(image, image_structure)

        return image_structure


def valid_node(node):
    """
    Gets if a node is valid or not for parsing.

    :type node: Node
    :param node: The Xml node to be validated.
    :rtype: bool
    :return: The valid or not valid value.
    """

    # in case the node is of type element
    if node.nodeType == xml.dom.minidom.Node.ELEMENT_NODE:
        # returns true (valid)
        return True
    # otherwise
    else:
        # returns false (invalid)
        return False
