# -*- coding: utf-8 -*-
"""
  Copyright 2012-2014 Grégory Soutadé

  This file is part of Dynastie.

  Dynastie 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, either version 3 of the License, or
  (at your option) any later version.

  Dynastie 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 Dynastie.  If not, see <http://www.gnu.org/licenses/>.
"""
import os
import hashlib
import gzip
import math
import codecs
from xml.dom import *
from xml.dom.minidom import parse
from xml.parsers.expat import *

class StrictUTF8Writer(codecs.StreamWriter):
    '''A StreamWriter for utf8'''
    encode = codecs.utf_8_encode
    value = ''

    def __init__(self):
        self.value = u''

    def write(self, object):
        object = object.replace('&lt;', '<')
        object = object.replace('&gt;', '>')
        object = object.replace('&quot;', '"')
        object = object.replace('&apos;', "'")
        object = object.replace('&amp;', '&')

        if not type(object) == unicode:
            self.value = self.value + unicode(object, 'utf-8')
        else:
            self.value = self.value + object
        return self.value

    def reset(self):
        self.value = u''

    def getvalue(self):
        return self.value

class DynastieGenerator:

    URI = "http://indefero.soutade.fr/p/dynastie"

    def __init__(self, request, hash_posts={}, hash_posts_content={}):
        self.report = ''
        self.somethingWrote = False
        self.request = request
        self.hash_posts = hash_posts
        self.hash_posts_content = hash_posts_content
        self.user = request and request.user or None

    def addReport(self, string, color=''):
        if string in self.report: return
        if color:
            self.report = self.report + '<span style="color:' + color + '">'
        self.report = self.report + '<b>' + self.__class__.__name__ + '</b> : '
        self.report = self.report + string
        if color:
            self.report = self.report + '</span>'
        self.report = self.report + '<br/>\n'

    def addWarning(self, string):
        self.addReport(string, 'yellow')

    def addError(self, string):
        self.addReport(string, 'red')

    # Virtual
    def generate(self, blog, src, output):
        return

    def computeNbPages(self, nb_post, nb_post_per_page):
        res = math.ceil((nb_post*1.0)/(nb_post_per_page*1.0))
        return int(res)

    def writeIfNotTheSame(self, filename, node):
        from dynastie.models import FileOutputCache

        writer = StrictUTF8Writer()
        node.writexml(writer)
        content = writer.getvalue().encode('utf-8')

        dst_md5 = hashlib.md5()
        dst_md5.update(content)

        cache_objs = FileOutputCache.objects.filter(name=filename)
        if cache_objs.count() == 0:
            cache_obj = None
        else:
            cache_obj = cache_objs[0]

        if cache_obj or os.path.exists(filename):
            if not cache_obj:
                src_md5 = hashlib.md5()
                f = open(filename,'rb') 
                src_md5.update(f.read())
                f.close()
                src_md5 = src_md5.hexdigest()
            else:
                src_md5 = cache_obj.hash

            if src_md5 == dst_md5.hexdigest():
                if cache_obj is None:
                    cache_obj = FileOutputCache(name=filename, hash=src_md5)
                    cache_obj.save()
                filename = filename + '.gz'
                if not os.path.exists(filename):
                    f = gzip.open(filename, 'wb')
                    f.write(content)
                    f.close()
                return
            if os.path.exists(filename):
                os.unlink(filename)

        if cache_obj is None:
            cache_obj = FileOutputCache(name=filename, hash=dst_md5.hexdigest())
        else:
            cache_obj.hash = dst_md5.hexdigest()

        self.addReport('Write (and compress) ' + filename)
        f = open(filename,'wb') 
        f.write(content)
        f.close()

        filename = filename + '.gz'
        #self.addReport('Compressing it ' + filename)
        f = gzip.open(filename, 'wb')
        f.write(content)
        f.close()
        
        cache_obj.save()

        self.somethingWrote = True

    def createLinkElem(self, dom, path, title):
        link_elem = dom.createElement('a')
        link_elem.setAttribute('href', path)
        text_elem = dom.createTextNode(title)
        link_elem.appendChild(text_elem)

        return link_elem

    def cloneSubtree(self, div, subtree):
        for node in subtree.childNodes:
            div.appendChild(node.cloneNode(True))

    def createElement(self, dom, name='', content='', subtree=None):
        div = dom.createElement('div')
        if name:
            div.setAttribute('class', name)
        if content:
            div.appendChild(dom.createTextNode(content))
        if subtree:
            self.cloneSubtree(div, subtree)

        return div

    def createMeta(self, dom, name='', content=''):
        div = dom.createElement('meta')
        if name:
            div.setAttribute('name', name)
        if content:
            div.setAttribute('content', content)

        return div

    # Recursively transform <dyn:XXX> elements with the ones in values
    def simpleTransform(self, values, dom, elem, root):
        for node in root.childNodes:
            if node.prefix == 'dyn':
                if node.localName in values:
                    content = values[node.localName]
                    if isinstance(content, basestring):
                        new_elem = self.createElement(dom, node.localName, content)
                    else:
                        new_elem = self.createElement(dom, node.localName)
                        new_elem.appendChild(content)
                else:
                    new_elem = node.cloneNode(False)
                    self.simpleTransform(values, dom, new_elem, node)
            else:
                new_elem = node.cloneNode(False)
                self.simpleTransform(values, dom, new_elem, node)
            elem.appendChild(new_elem)

    def replaceByText(self, dom, root, node, content):
        new_node = dom.createTextNode(content)
        root.replaceChild(new_node, node)
    
    def _parse(self, hooks, posts, dom, root):
        for node in root.childNodes:
            if node.prefix == 'dyn' and node.localName in hooks:
                node = hooks[node.localName](posts, dom, root, node)
            if node and node.hasChildNodes():
                self._parse(hooks, posts, dom, node)

    def parse(self, src, hooks, posts, dom, root):
        bases = dom.getElementsByTagNameNS(self.URI, 'base')
        
        if not bases:
            self._parse(hooks, posts, dom, root)
            return root

        if len(bases) != 1:
            self.addError('More than one base defined')
            return root

        base = bases[0]

        if not base.hasAttribute('file'):
            self.addError('No \'file\' attribute defined')
            return root
        filename = base.getAttribute('file')

        if not os.path.exists(src + '/' + filename):
            self.addError('Base ' + filename + ' doesn\'t exists')
            return root

        target_blocks = base.getElementsByTagNameNS(self.URI, 'block')
        if not target_blocks:
            self.addError('No \'block\' defined in ' + src + '/' + filename)
            return root

        for target_block in target_blocks:
            if not target_block.hasAttribute('name'):
                self.addError('Every block must have a name in ' + src + '/' + filename)
                return root
                
        dom2 = root
        try:
            dom2 = parse(src + '/' + filename)
        except ExpatError, e:
            self.addError('Error parsing ' + src + '/' + filename)
            return root
        
        blocks = dom2.getElementsByTagNameNS(self.URI, 'block')
        block_found = False
        for block in blocks:
            if not block.hasAttribute('name'):
                self.addError('block has no attribute \'name\' in ' + src + '/' + filename)
                return root
            blockname = block.getAttribute('name')
            for target_block in target_blocks:
                
                if blockname != target_block.getAttribute('name'):
                    continue

                for child in target_block.childNodes:
                    block.parentNode.appendChild(child.cloneNode(True))
                block_found = True
                break
            block.parentNode.removeChild(block)

        if not block_found:
            self.addError('Any block found in ' + src + '/' + filename)
            return root

        root = dom2.firstChild

        self.parse(src, hooks, posts, dom2, root)

        return root