#!/usr/bin/python

import os, glob, re, time, json
from flask import Flask, request, redirect, url_for, make_response, render_template
from flask import send_from_directory, g
from werkzeug import secure_filename
from werkzeug.routing import BaseConverter
from threading import Lock

app = Flask(__name__)
base = os.path.dirname(os.path.realpath(__file__))
lock = Lock()

class RegexConverter(BaseConverter):
  def __init__(self, url_map, *items):
    super(RegexConverter, self).__init__(url_map)
    self.regex = items[0]

app.url_map.converters['regex'] = RegexConverter

@app.before_request
def before_request():
  g.start = time.time()

@app.after_request
def add_header(response):
  diff = time.time() - g.start
  response.headers['Cache-Control'] = "max-age=0, no-cache, no-store, must-revalidate"
  response.headers['Expires'] = "Thu, 29 Nov 1984 19:40:00"
  response.headers['Pragma'] = "no-cache"
  response.headers['Run-in'] = os.path.dirname(os.path.abspath(__file__))
  response.headers['CWD'] = os.path.dirname(os.getcwd())
  response.headers['X-LOST'] = '4 8 15 16 23 42'
  response.headers['X-Runtime'] = "%1.1f ms" % (diff*1000)
  return response

def conf():
  c = dict()
  c["documents.sqlite3"] = os.path.join(base, "static/database/documents.sqlite3")

  return c

@app.route("/<regex('(templates)'):head>/<path:resource>")
def get_template(head, resource):
  return render_template("%s.html" % (resource))

@app.route("/<regex('(img|css|js)'):head>/<resource>")
def get_static(head, resource):
  return redirect(url_for('static', filename=head + '/' + resource))

@app.route("/<regex('(font|fonts)'):head>/<resource>")
def get_images(head, resource):
  return redirect(url_for('static', filename='fonts/'+resource))

@app.route("/pubkey/")
def get_public_key():
  return send_from_directory(os.path.join(base, 'static'), filename='pubkey/key.pub')

@app.route("/app/<regex('overview|registration|dates|report|statistics|notes|feedback|acknowledgment'):target>")
def registration(target=None):
  import uuid
  my_uuid = str(uuid.uuid4()).upper()
  resp = make_response(render_template('registration.html', uuid=my_uuid))
  resp.set_cookie('request', my_uuid)

  return resp

#@app.route("/<regex('([A-Z0-9]{4,11})'):hash>/")
#@app.route("/<regex('([A-Z0-9]{4,11})'):hash>/<regex('overview|registration|dates|report|statistics|notes|feedback|acknowledgment'):target>")
#def root(hash, target=None):
#  import uuid
#  my_uuid = str(uuid.uuid4()).upper()
#  resp = make_response(render_template('root.html', uuid=my_uuid))
#  resp.set_cookie('request', my_uuid)
#
#  return resp

@app.route("/candidate/<string:uuid>/<int:process>/", methods=['POST'])
@app.route("/candidate/<string:uuid>/<int:kind>/<int:process>/", methods=['POST'])
@app.route("/candidate/<string:uuid>/<int:process>/", methods=['GET'])
def post_registration(uuid, kind=1, process=0, version=0):
  import subprocess, shlex
  from os import system

  def ifnull(key, el, default):
    return default if key not in el else el[key]

  def to_xml(machine):
    import json
    import xml.etree.ElementTree as ET

    def as_xml(machine):
      m = ET.Element('machine')
      m.set('uuid', machine['uuid'])
      m.set('serialNumber', machine['serialNumber'])
      m.set('valid', str(machine['valid']))
      m.set('builtOn', machine['builtOn'])
      m.set('partnerCode', str(ifnull('partnerCode', machine, '')))
      m.set('modelCode', str(machine['modelCode']))
      m.set('intlCode', str(ifnull('intlCode', machine, '')))
      m.set('model', ifnull('model', machine, ''))
      m.set('color', ifnull('color', machine, ''))
      m.set('intlCodeLong', ifnull('intlCodeLong', machine, ''))
      m.set('description', ifnull('description', machine, ''))
      m.set('inputDate', machine['inputDate'])
      m.set('origin', machine['origin'])
      m.set('extref', ifnull('extref', machine, ''))
      m.set('kind', str(ifnull('kind', machine, 1)))
      m.set('shipment', ifnull('shipment', machine, ''))
      m.set('warranty', str(machine['warranty']))
      m.set('purchasedOn', ifnull('purchasedOn', machine, ''))
      m.set('document', ifnull('document', machine, ''))

      a = ET.Element('address')
      a.set('name', machine["address"]["name"])
      a.set('zip', machine["address"]["zip"])
      a.set('city', machine["address"]["city"])
      a.set('language', ifnull('language', machine["address"], 'de'))
      a.text = machine["address"]["street"]
      m.append(a)

      c = ET.Element('transporter')
      c.set('id', machine["transporter"]["id"])
      c.set('name', machine["transporter"]["name"])
      m.append(c)
      return m

    root = ET.Element('machines')
    m = as_xml(machine)
    root.append(m)

    return ET.ElementTree(root)

  import json
  from flask import abort
  payload = json.loads(request.data)
  uuid = payload["uuid"]
  serial_number = payload["serialNumber"]
  print uuid, request.data

  subdir = ['registrations'] + time.strftime("%Y,%m").split(',')
  directory = os.path.join(base, *subdir)
  if not os.path.exists(directory):
    os.makedirs(directory)

  filename = "%s.json" % (serial_number)

  with open(os.path.join(directory, filename), "wb") as outfile:
    json.dump(payload, outfile)

  document = ifnull('document', payload, None)
  if document is not None:
    from rasterize import get_document_by_name
    try:
      database = conf()["documents.sqlite3"]
      (_, filename, _, blob) = get_document_by_name(database, document)
      with open(os.path.join(directory, filename), "wb") as outfile:
        outfile.write(blob)
    except Exception, e:
      print "Failed to write document to database: "+ str(e)
    finally:
      pass

  try:
    xml = to_xml(payload)
    filename = "%s.xml" % (serial_number)
    with open(os.path.join(directory, filename), "wb") as outfile:
      xml.write(outfile, encoding="UTF-8", xml_declaration=True)

    print os.path.join(directory, filename)
    shell = """%s -i -w %s %s""" % (os.path.join(base, 'sql/xml2sql.sh'), "-p" if process==1 else "", os.path.join(directory, filename))
    system(shell)

    return "Ahoy, capt'n"
#  except:
#    return abort(500)
  finally:
    pass

@app.route("/serial/<regex('([A-Za-z0-9]{19,19})'):serial>/<int:mode>/", methods=['GET'])
@app.route("/serial/<regex('([A-Za-z0-9]{19,19})'):serial>/<int:mode>/<int:pi>/", methods=['GET'])
def serial_number_query(serial, mode, pi=None):
  from subprocess import call, check_output
  from shlex import split
  from flask import make_response
  from os import listdir, path, system

  source = "%s.%s" % (serial, "xml" if mode==0 else "json")

  shell = """mysql -u F5VksfQsYW -h 192.168.10.10 -pmWaq9FiGC6\
  --default-character-set=utf8\
  --skip-column-names\
  --raw\
  --execute="CALL serial_number_query('%s', 'FRE', %d);"\
  dfnesa > /tmp/%s
  """ % (serial, mode, source)
  system(shell)

  if mode==0 and pi in [0, 1]:
    ver = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n'
    pis = ['<?xml-stylesheet type="text/xsl" href="/static/xslt/repair_number.xslt"?>\n',
           '<?xml-stylesheet type="text/xsl" href="/static/xslt/repair_label.xslt"?>\n']
    xml = open("/tmp/%s" % source).readlines()
    open("/tmp/%s" % source, 'w').writelines([ver] + [pis[pi]] + xml[1:])

  return send_from_directory('/tmp', filename=source, as_attachment=False)

def make_pdf(serial):
  from os import path, system

  with lock:
    try:
      xslt1 = path.join(path.dirname(path.realpath(__file__)), 'static/xslt/repair_number.xslt')
      xslt2 = path.join(path.dirname(path.realpath(__file__)), 'static/xslt/repair_label.xslt')

      shell = """mysql -u F5VksfQsYW -h 192.168.10.10 -pmWaq9FiGC6\
      --default-character-set=utf8\
      --skip-column-names\
      --raw\
      --execute="CALL serial_number_query('%s', 'FRE', 0);"\
      dfnesa > /tmp/%s.xml
      """ % (serial, serial)
      system(shell)

#      wkhtml = '/usr/local/bin/./wkhtmltopdf.sh'
      wkhtml = 'wkhtmltopdf'

      # xslt1
      shell = 'xsltproc -o /tmp/%s_1.html %s /tmp/%s.xml' % (serial, xslt1, serial)
      system(shell)

      shell = '%s -q --title NESA --page-width 62mm --page-height 25mm -L 0 -R 0 -T 0 -B 0 --print-media-type /tmp/%s_1.html /tmp/%s_1.pdf' % (wkhtml, serial, serial)
      system(shell)

      # xslt2
      shell = 'xsltproc -o /tmp/%s_2.html %s /tmp/%s.xml' % (serial, xslt2, serial)
      system(shell)

      shell = '%s -q --title NESA --page-width 62mm --page-height 150mm -L 0 -R 0 -T 0 -B 0 --print-media-type /tmp/%s_2.html /tmp/%s_2.pdf' % (wkhtml, serial, serial)
      system(shell)

      # merge
      shell = 'gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -dPDFSETTINGS=/prepress -sOutputFile=/tmp/%s.pdf /tmp/%s_1.pdf /tmp/%s_2.pdf' % (serial, serial, serial)
      system(shell)

      return True
    except:
      return False

@app.route("/pdf/<regex('([A-Za-z0-9]{19,19})'):serial>/<int:mode>/", methods=['GET'])
@app.route("/pdf/<regex('([A-Za-z0-9]{19,19})'):serial>/<int:mode>/<int:pi>/", methods=['GET'])
def pdf(serial, mode=0, pi=None):
  from os import path, system

  if not make_pdf(serial):
    return abort(505)

  if mode==0:
    return send_from_directory('/tmp', filename=("%s.pdf" % serial), as_attachment=False)
  elif pi in [0, 1]:
    if pi==0:
      print "Printing number label"
#      shell = 'lpr -P QL720NWLOG1 -#2 -o media=om_br-l0-b3-e0199-b6-cf_61.91x24.96mm /tmp/%s_1.pdf' % (serial)
#      shell = 'lpr -P QL820NWB -#2 -o media=om_br-l0-b3-e019-fe903_61.91x24.96mm /tmp/%s_1.pdf' % (serial)
      shell = 'lpr -P QL820NWB -#2 -o media=custom_61.98x28.87mm_61.98x28.87mm /tmp/%s_1.pdf' % (serial)
    elif pi==1:
      print "Printing repair label"
      shell = 'lpr -P QL720NWLOG2XL -o media=om_br-l0-c3-e09609526_61.91x149.77mm /tmp/%s_2.pdf' % (serial)
    system(shell)

    return "Ahoy, capt'n"

  return "Arrrr"

@app.route("/label/<regex('([A-Za-z0-9]{19,19})'):serial>/<int:pi>/<int:resolution>/", methods=['GET'])
def label(serial, resolution=600, pi=None):
  from os import path, system

  if not make_pdf(serial):
    return abort(505)

  if pi in [0, 1]:
    if pi==0:
      print "Number label"
      shell = 'gs -dQUIET -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pngmono -r%d -dFirstPage=1 -dLastPage=1 -sOutputFile=/tmp/%s_1.png /tmp/%s.pdf' % (resolution, serial, serial)
      system(shell)
      return send_from_directory('/tmp', filename=("%s_1.png" % serial), as_attachment=False)
    elif pi==1:
      print "Repair label"
      shell = 'gs -dQUIET -dSAFER -dBATCH -dNOPAUSE -sDEVICE=pngmono -r%d -dFirstPage=2 -dLastPage=2 -sOutputFile=/tmp/%s_2.png /tmp/%s.pdf' % (resolution, serial, serial)
      system(shell)
      return send_from_directory('/tmp', filename=("%s_2.png" % serial), as_attachment=False)

  return "Arrrr"

@app.route("/database/")
def json_database():
  from subprocess import call, check_output
  from shlex import split
  from flask import abort, make_response
  from os import path, system

  return send_from_directory(os.path.join(base, 'static'), filename='database/database.json')

@app.route("/fetch/", methods=['GET'])
@app.route("/fetch/<string:file>", methods=['GET'])
def rasterize(file="pdf$"):
  from uuid import uuid4
  from json import dumps
  from rasterize import fetch, document_by_name
  try:
    uuid = str(uuid4())

    cc = 'mosquitto_pub -h apps.axiomes.ch -t "{0}" -m "{1}"'.format("documents/fetch", uuid)
    os.system(cc)
    database = conf()["documents.sqlite3"]
#    r = fetch(database, 'repairs.nicolet-engineering.com', 'nespick', 'Tzgx32U8k4s',
    r = fetch(database, '192.168.200.7', 'volkmar', 'MDa3ZyNhULYyA',
      'documents', archive='archives', pattern=file, limit=1)
    out = map(lambda (x, y, z): dict(zip(("name", "pages"), (x, y))), r)
#  except:
#    return abort(500)
#  else:
    cc = 'mosquitto_pub -h apps.axiomes.ch -t "{0}" -m "{1}"'.format(
      time.strftime("documents/%Y/%m/%d"), ",".join(map(lambda (x, y, z): x, r)))
    os.system(cc)
    return dumps(out)
  finally:
    pass

@app.route("/document/", methods=['GET'])
@app.route("/document/<int:document_id>/", methods=['GET'])
@app.route("/document/<int:document_id>/<int:index>", methods=['GET'])
def get_document(document_id=None, index=None):
  from flask import send_file, jsonify, abort
  from rasterize import get_document_by_id, get_last_document, get_preview
  import io

  database = conf()["documents.sqlite3"]
  if document_id is not None:
    d = get_document_by_id(database, document_id)
  else:
    d = get_last_document(database)

  if d is None:
    return abort(404)

  if index is None:
    return jsonify(id=d[0], name=d[1], pages=d[2])
  elif index==0:
    return send_file(io.BytesIO(d[3]), mimetype="application/pdf", as_attachment=False)
  else:
    p = get_preview(database, document_id, index)
    if p is None:
      return abort(404)
    return send_file(io.BytesIO(p[2]), mimetype="image/%s" % p[1], as_attachment=False)

@app.route("/documents/", methods=['GET'])
@app.route("/documents/<int:count>/", methods=['GET'])
def get_last_documents(count=10):
  from rasterize import get_last_documents

  database = conf()["documents.sqlite3"]
  r = get_last_documents(database, count)
  resp = make_response(json.dumps(map(lambda (x, y, z, u, v): dict(zip(("id", "created_on", "name", "pages"), (x, y, z, u))), r)))
  resp.mimetype = "application/json"
  return resp

########################################################################

@app.route("/timestamp")
def timestamp():
  return time.strftime("%Y-%m-%d %H:%M:%S%z")


###
# Proxy wrapper class to allow Flask to run behind a reverse
# proxy (such as Apache)
#
class ReverseProxied(object):
  def __init__(self, app):
    self.app = app

  def __call__(self, environ, start_response):
    script_name = environ.get('HTTP_X_SCRIPT_NAME', '')
    if script_name:
      environ['SCRIPT_NAME'] = script_name
      path_info = environ['PATH_INFO']
      if path_info.startswith(script_name):
        environ['PATH_INFO'] = path_info[len(script_name):]

    scheme = environ.get('HTTP_X_SCHEME', '')
    if scheme:
      environ['wsgi.url_scheme'] = scheme
    return self.app(environ, start_response)

app.wsgi_app = ReverseProxied(app.wsgi_app)

if __name__ == "__main__":
  app.run(host='0.0.0.0', port=7000, threaded=True, debug=True)
