import re
import os
import sha
import inspect
import tempfile
from trac.core import *
from trac.util import NaivePopen
from trac.web.href import Href
from trac.attachment import Attachment
from trac.web.api import IRequestHandler, Request
from trac.wiki.api import IWikiMacroProvider
from trac.mimeview.api import IHTMLPreviewRenderer

settings_name = 'OfficeView'
req_url_prefix = 'officeview'

mimetypes = (
    'application/msword',
    'application/vnd.ms-excel',
    'application/vnd.ms-powerpoint',
)

class OfficeView(Component):
    """
    Office File view plugin for trac
    """
       
    implements(IWikiMacroProvider, IRequestHandler, IHTMLPreviewRenderer)
    
    def __load_config(self):
        """
        get configuration of OfficeViewPlugin from trac.ini
        """
        # get OpenOffice.org path from trac.ini
        self.ooo_path = self.config.get(settings_name, 'OOoPath', '')
        self.ooo_python_path = os.path.join(self.ooo_path, 'python.bat')
        # TODO check OOo exists
        # TODO check Python in OOo exists
        
        # get temporary directory path from trac.ini
        self.temp_directory = self.config.get(
            settings_name, 'TempDirectory', '')
        if self.temp_directory == '':
            raise Exception('Configuration not found : TempDirectory')
        
        # get PyODConverter path from trac.ini
        self.pyodconverter_path = self.config.get(
            settings_name, 'PyODConverterPath', '')
        if self.pyodconverter_path == '':
            raise Exception('Configuration not found : PyODConverterPath')
        
        # TODO get PyODConverter command option from trac.ini
        
        self.default_width = self.config.get(settings_name, 'DefaultWidth', '800px')
        self.default_height = self.config.get(settings_name, 'DefaultHeight', '600px')

    def __rendering_file(self, base_path, input_file_path, sha_key):
        # file_base_name = os.path.basename(input_file_path)
        # output_file_name = file_base_name[:file_base_name.rfind('.')] + '.pdf'
        output_file_name = '.'.join([sha_key, 'pdf'])
        self.log.debug('out_file_name : %s' % output_file_name)
        output_file_path = os.path.join(self.temp_directory, output_file_name)
        self.log.debug('output_file_path : %s' % output_file_path)
        
        if os.path.exists(output_file_path) == False:
            cmdline = '\"%s\" \"%s\" \"%s\" \"%s\"' % (
                self.ooo_python_path, self.pyodconverter_path, input_file_path,
                output_file_path,)
            self.log.debug('command line : %s' % cmdline)
            np = NaivePopen(cmdline)
            if np.errorlevel or np.err:
                err = 'Running (%s) failed: %s, %s.' % (
                    cmdline, np.errorlevel, np.err)
                raise Exception, err
        
        # TODO get output file URL
        href = Href(base_path)
        output_file_url = href(req_url_prefix, output_file_name)
        self.log.debug('output_file_url : %s' % output_file_url)
        
        width = self.default_width
        height = self.default_height
        
        # TODO output HTML strings.
        return '''
<iframe src="%s" width="%s" height="%s"></iframe>
        ''' % (output_file_url, width, height, )        
        pass
    
    #
    # IWikiMacroProvider implements
    #
    
    def get_macros(self):
        """
        """
        yield "OfficeView"
    
    def get_macro_description(self, name):
        """
        name : macro name
        """
        return inspect.getdoc(self.__class__)
    
    def render_macro(self, req, name, content):
        """
        rendering macro
        
        req : request object
        name : macro name
        content : macro args(comma seperated)
        """
        args = content.split(',')
        self.log.debug('get args : %s' % args)
        filespec = args[0]
        self.log.debug('filespec : %s' % filespec)
        
        # get configuration from trac.ini
        self.__load_config()
        
        # TODO parsing request path
        parts = filespec.split(':')
        
        module = ''
        id = ''
        file = ''
        input_file_path = ''
        
        #
        # FIXME refactoring(extract method or module) start.
        #
        
        # case "wiki:<wiki page id>:<attachment file name>"
        if re.match('^wiki:[^:]*:[^:]*$', filespec) is not None:
            module = parts[0]
            id = parts[1]
            file = parts[2]
      
        # case "ticket:<ticket id>:<attachment file name>"
        elif re.match('^ticket:[0-9]*:[^:]*$', filespec) is not None:
            module = parts[0]
            id = parts[1]
            file = parts[2]
        
        # case "#<ticket id>:<attachment file name>"
        elif re.match('^\x23[0-9]*:[^:]*$', filespec) is not None:
            module = 'ticket'
            id = parts[0].replace('#', '')
            file = parts[1]
        
        # case "htdocs:<file name>"
        
        # TODO browser（files in repository）
        # リポジトリからファイルを取る方法は？ Contextオブジェクトを調べてみると何かあるかも？？
        # テンポラリ領域にファイルを置く(これをinput fileとする)
        
        # case "<wiki page id>:<attachment file name>"
        elif re.match('^[^:]*:[^:]*$', filespec) is not None:
            module = 'wiki'
            id = parts[0]
            file = parts[1]
        
        # case "<attachment file name>"
        
        # filespec was not match any pattern.
        else:
            raise Exception('No filespec given.')
        
        #
        # FIXME refactoring(extract method or module) end.
        #
        
        # get attachment file in wiki page or ticket
        attachment = Attachment(self.env, module, id, file)
        input_file_path = attachment.path
        self.log.debug('input_file_path : %s' % input_file_path)
        sha_key = sha.new(input_file_path).hexdigest()
        self.log.debug('sha key : %s' % sha_key)
        
        # TODO check extensions
            
        # TODO clean temporary directory.
        
        ret_doc = self.__rendering_file(req.base_url, input_file_path, sha_key)
        return ret_doc

    
    #
    # IRequestHandler implements
    #
    
    def match_request(self, req):
        """
        """
        return req.path_info.startswith('/' + req_url_prefix)
    
    def process_request(self, req):
        """
        """
        # get configuration from trac.ini
        self.__load_config()
        
        prefix = '/' + req_url_prefix
        pieces = [item for item in req.path_info.split(prefix) if len(item)]
        
        if len(pieces):
            pieces = [item for item in pieces[0].split('/') if len(item)]
            
            if len(pieces):
                name = pieces[0]
                pdf_path = os.path.join(self.temp_directory, name)
                return req.send_file(pdf_path, mimetype='application/pdf')
            
        return

    #
    # IHTMLPreviewRenderer implements
    #    
    def get_quality_ratio(self, type):
        if type in mimetypes:
            return 8
        return 0
    
    def render(self, req, type, content, filename = None, rev = None):
        self.__load_config()
        
        self.log.debug('request path info : %s' % req.path_info)
        self.log.debug('file name : %s' % filename)
        self.log.debug('revision : %s' % rev)
        
        ext = filename[filename.rfind('.'):]
        input_file_path = tempfile.mktemp(ext)
        
        tmpfile = open(str(input_file_path), 'wb')
        tmpfile.write(content)  # if version of trac is 0.10 or later then content.read()
        
        tmpfile.close()
        
        self.log.debug('sha key args : %s' % '@'.join([req.path_info, str(rev)]))
        sha_key = sha.new( '@'.join([req.path_info, str(rev)])).hexdigest()
        self.log.debug('sha key : %s' % sha_key)
        
        ret_doc = self.__rendering_file(req.base_url, input_file_path, sha_key)
        if os.path.isfile(input_file_path):
            os.remove(input_file_path)
        return ret_doc
