# Copyright 1999-2016. Parallels IP Holdings GmbH. All Rights Reserved.
from sys import argv, exit
from xml.sax.handler import ContentHandler
from xml.sax import make_parser, saxutils
import os
import time

class StopSaxProcessing(Exception):
    def __init__(self):
        Exception.__init__(self)


#
# Base class for XmlHandler worker classes
#
class XmlWorker:

    def startElement(self, name, attrs):
        pass

    def endElement(self, name, value):
        pass


#
# Class to make copy if input XML
# returns string in 'get_content' method
#
class XMLContentSimpleWriter(XmlWorker):
    def __init__(self):
        self.__content = []

    def addContent(self, string, doNotEscape=False):
        self.__content.append(string if doNotEscape else saxutils.escape(string))

    def normalize(self,string):
        return string

    def startElement(self, name, attrs):
        element_string =  "<" + name
        for at_name in attrs.keys():
            element_string = element_string + " " + at_name + "=" + saxutils.quoteattr(attrs[at_name])
        element_string = element_string + ">"
        self.__content.append(element_string)

    def endElement(self, name, value):
        self.__content.append(saxutils.escape(value))
        self.__content.append("</" + name + ">\n")

    def get_content(self):
        return ''.join(self.__content)


class XMLSimpleWriter(XMLContentSimpleWriter):
    def __init__(self):
        XMLContentSimpleWriter.__init__(self)
        self.addContent('<?xml version="1.0" encoding="UTF-8"?>\n', True)

    def get_content(self):
        content = XMLContentSimpleWriter.get_content(self)
        return content.encode('utf-8')


#
# Class to extract an element text content from XML
#
class XmlElementTextGetter(XmlWorker):
    def __init__(self, element_name):
        self.__element_name = element_name
        self.__element_inner_text = ''
        self.__in_element = False
        self.__content_writer = XMLContentSimpleWriter()
        self.__start_element_writer = XMLContentSimpleWriter()
        self.__end_element_writer = XMLContentSimpleWriter()

    def startElement(self, name, attrs):
        if self.__in_element:
            self.__content_writer.startElement(name, attrs)
        elif name == self.__element_name:
            self.__start_element_writer.startElement(name, attrs)
            self.__in_element = True

    def get_element_inner_text(self):
        return self.__content_writer.get_content()

    def get_element_outer_text(self):
        return self.__start_element_writer.get_content() + self.__content_writer.get_content() + self.__end_element_writer.get_content()

    def endElement(self, name, value):
        if self.__in_element:
            if name != self.__element_name:
                self.__content_writer.endElement(name, value)
            else:
                self.__end_element_writer.endElement(name, value)
                self.__in_element = False
                raise StopSaxProcessing()


#
# Class to extract an element value from XML
#
class XmlElementValueGetter(XmlWorker):
    def __init__(self, element_name):
        self.__element_name = element_name
        self.__element_value = None

    def get_element_value(self):
        return self.__element_value

    def endElement(self, name, value):
        if name == self.__element_name:
            self.__element_value = value
            raise StopSaxProcessing()


#
# Class to check that XML contains element from the specified element array
#
class XmlElementSelector(XmlWorker):
    def __init__(self, element_names):
        self.__element_names = element_names
        self.__element_found = False
        self.__element_name = None

    def get_element_found(self):
        return self.__element_found

    def get_element_name(self):
        return self.__element_name

    def startElement(self, name, attrs):
        if name in self.__element_names:
            self.__element_found = True
            self.__element_name = name
            raise StopSaxProcessing()


#
# Class to extract value of an element given by its name and attributes from XML
#
class XmlElementAttrValueGetter(XmlWorker):
    def __init__(self, element_name, element_attrs):
        self.__element_name = element_name
        self.__element_attrs = element_attrs
        self.__element_attributes = {}
        self.__in_element = False
        self.__element = None

    def get_element(self):
        return self.__element

    def get_element_attributes(self):
        return self.__element_attributes

    def startElement(self, name, attrs):
        if name == self.__element_name:
            matched = True
            for attr in self.__element_attrs.keys():
                if not attr in attrs.keys():
                    matched = False
                    break
                if self.__element_attrs[attr] != attrs[attr]:
                    matched = False
                    break
            if matched:
                self.__in_element = True
                self.__element_attributes = attrs.copy()

    def endElement(self, name, value):
        if name == self.__element_name and self.__in_element:
            self.__element = self.__element_name, self.__element_attributes, value
            raise StopSaxProcessing()


#
# Class to extract an element attribute from XML
#
class XmlElementAttrGetter(XmlWorker):
    def __init__(self, element_name, attribute_name):
        self.__element_name = element_name
        self.__attribute_name = attribute_name
        self.__attribute_value = None

    def get_attribute_value(self):
        return self.__attribute_value

    def startElement(self, name, attrs):
        if name == self.__element_name:
            if self.__attribute_name in attrs.keys():
                self.__attribute_value = attrs[self.__attribute_name]
                raise StopSaxProcessing()


#
# Class to set an element attribute for XML
#
class XmlElementAttrSetter(XmlWorker):
    def __init__(self, element_name, attribute_name, attribute_value):
        self.__element_name = element_name
        self.__attribute_name = attribute_name
        self.__attribute_value = attribute_value

    def startElement(self, name, attrs):
        if name == self.__element_name:
            attrs._attrs.setdefault(self.__attribute_name,self.__attribute_value)
            raise StopSaxProcessing()


#
# Xml Content Handler class, working with XmlWorkers list.
# Processes an input XML throw the list of XmlWorker. XmlWorker list order is important!
# Basically an XmlWriter (if any) should be the last XmlWorker
#
class XmlHandler(ContentHandler):
    def __init__ (self, xmlWorkers = []):
        ContentHandler.__init__(self)
        self.__xmlWorkers = xmlWorkers
        self.__elementValue = u''

    def startElement(self, name, attrs):
        self.__elementValue = u''
        keep_workers = []
        for index, worker in enumerate(self.__xmlWorkers):
            try:
                worker.startElement(name, attrs)
                keep_workers.append(worker)
            except StopSaxProcessing:
                pass
        
        self.__xmlWorkers = keep_workers
        
        if len(self.__xmlWorkers) == 0:
            raise StopSaxProcessing()

    def characters (self, ch):
        self.__elementValue = self.__elementValue + ch

    def endElement(self, name):
        keep_workers = []
        for index, worker in enumerate(self.__xmlWorkers):
            try:
                worker.endElement(name, self.__elementValue)
                keep_workers.append(worker)
            except StopSaxProcessing:
                pass
        
        self.__xmlWorkers = keep_workers
        self.__elementValue = u''        
        if len(self.__xmlWorkers) == 0:
            raise StopSaxProcessing()


class InfoXMLProcessor:
    def __init__(self, info_xml, infoXmlHandler):
        self.__info_xml = info_xml
        self.__infoXmlHandler = infoXmlHandler

    def process(self):
        parser = make_parser()
        parser.setContentHandler(self.__infoXmlHandler)
        try:
            parser.parse(open(self.__info_xml))
        except StopSaxProcessing:
            pass


class DumpDescription:
    def __init__(self, name, fullname, creation_date, comment, size):
        self.__name = name
        self.__fullname = fullname
        self.__creation_date = creation_date
        self.__comment = comment
        self.__size = size

    def get_name(self):
        return self.__name

    def get_fullname(self):
        return self.__fullname

    def get_creation_date(self):
        return self.__creation_date

    def get_comment(self):
        return self.__comment

    def get_size(self):
        return self.__size


class LocalDumpAccessService:
    def __init__(self,dump_storage_credentials):
        self.__dump_storage_credentials = dump_storage_credentials

    def getDumpDescription(self,fullname):
        head, tail = os.path.split(fullname)
        creation_date = os.path.getmtime(fullname)
        # extract comment from dump
        comment = ""
        size = os.path.getsize(fullname)
        dump_description = DumpDescription(tail, fullname, creation_date, comment, size)
        return dump_description

    def getDumpType(self, fullname):
        dump_type = ''
        element_selector = XmlElementSelector(['admin','server','reseller','client','domain'])
        xml_processor = InfoXMLProcessor( fullname, XmlHandler([element_selector]) )
        xml_processor.process()
        if element_selector.get_element_found():
            dump_type = element_selector.get_element_name()
            if dump_type == 'admin':
                dump_type = 'server'
            
        return dump_type


class FtpDumpAccessService:
    def __init__(self,dump_storage_credentials):
        self.__dump_storage_credentials = dump_storage_credentials

    def getDumpDescription(self,fullname):
        head, tail = os.path.split(fullname)
        # extract comment from dump
        comment = ""
        dump_description = DumpDescription(tail, fullname, time.strftime('%Y-%m-%d-%H%M%S',time.localtime()), comment, 1000)
        return dump_description


def main():
    
    # take an info.xml file as argv[1] and write to 'migration-dump' element attribute 'base' taking it from argv[2]
    if not len(argv) == 3:
        exit(1)

    info_xml_path = argv[1].decode('utf-8')
    base_value = argv[2].decode('utf-8')

    writer = XMLSimpleWriter()
    getter = XmlElementAttrGetter('migration-dump','dump-version')
    setter = XmlElementAttrSetter('migration-dump','base',base_value)

    xml_processor = InfoXMLProcessor( info_xml_path, XmlHandler([getter,setter,writer]) )
    xml_processor.process()
    if getter.get_attribute_value() != "9.0.0":
        print "FAILED: "
        print getter
    getter = XmlElementAttrGetter('migration-dump','dump-version')
    getter2 = XmlElementAttrGetter('migration-dump','base')

    content_path = '/tmp/pmm_dump_access_service'
    content = open(content_path,'wt')
    content.write(writer.get_content())
    content.close()
    xml_processor2 = InfoXMLProcessor( content_path, XmlHandler([getter,getter2]) )
    xml_processor2.process()
    if getter.get_attribute_value() != "9.0.0":
        print "FAILED: "
        print getter
    if getter2.get_attribute_value() != base_value:
        print "FAILED: "
        print getter2
    print "OK" 

if __name__ == '__main__':
    main()

