import os, time, signal, subprocess, tempfile, shutil
import socket
import xmlrpclib
from zipfile import ZipFile
from StringIO import StringIO

BLUEDCS_URL = 'http://localhost:8888'

class ConversionError(Exception):
    pass

class TimeOutError(Exception):
    pass

def convertTo(data, filename, format, extension):
    orig_filename, orig_ext = os.path.splitext(filename)
    output_filename = orig_filename + extension
    # special case handling for pdf to text
    if format == 'TXT' and orig_ext == '.pdf':
        return pdfToText(data, output_filename), output_filename
    s = data.encode('base64')
    server = xmlrpclib.Server(BLUEDCS_URL)
    try:
        r = server.convert(s, format, filename, output_filename, 1)
    except socket.error:
        raise ConversionError("Could not connect to conversion server.")
    except xmlrpclib.Fault:
        raise ConversionError("Conversion system could not handle format.")
    z = ZipFile(StringIO(r.decode('base64')))
    return z.read(output_filename), output_filename

def convertToPDF(data, filename):
    return convertTo(data, filename, 'PDF', '.pdf')
   
def convertToTxt(data, filename):
    data, filename = convertTo(data, filename, 'TXT', '.txt')
    # XXX if OpenOffice conversion services were ever to run on Windows,
    # this would screw up the line endings, but otherwise this is hopefully
    # reliable
    data = data.replace('\n', '\r\n')
    return data, filename

def pdfToText(data, filename):
    dirpath = tempfile.mkdtemp()
    try:
        f = open(os.path.join(dirpath, 'file.pdf'), 'wb')
        f.write(data)
        f.close()
        try:
            result = callWithTimeout(
                ['pdftotext', '-layout',
                 'file.pdf', filename], cwd=dirpath)
        except TimeOutError:
            raise ConversionError("Conversion of PDF to text conversion hung.")
        if result != 0:
            raise ConversionError("Could not handle PDF to text conversion.")
        f = open(os.path.join(dirpath, filename), 'rb')
        result = f.read()
        f.close()
    finally:
        shutil.rmtree(dirpath)
    return result

def callWithTimeout(*args, **kw):
    timeout = kw.pop('timeout', 30)
    granularity = kw.pop('granularity', 0.5)
    p = subprocess.Popen(*args, **kw) 
    count = 0
    status = None
    while count < timeout and status is None:
        time.sleep(granularity)
        count += granularity
        status = p.poll()

    if status is None:
        print "status is none:", status
        try:
            os.kill(p.pid, signal.SIGKILL)
            status = p.wait()
            raise TimeOutError
        except OSError:
            # process already ended by itself
            pass
    return p.returncode
