# this is a slightly modified version of the src/web/faces.py to account for uwsgi import requirements

import logging, requests, os, weakref

from flask import Flask, jsonify, make_response, request, abort, redirect, send_file
from flask_request_id import RequestID
from web.emotion_gender_processor import EGProcessor
from flask import g
from peewee import Model, CharField, IntegerField, DateTimeField, PostgresqlDatabase, CompositeKey
from datetime import datetime
from dateutil.tz import tzlocal

dirname = os.path.normpath(os.path.join(os.getcwd(), 'results'))
if not os.path.exists(dirname):
    os.mkdir(dirname)

logging.basicConfig(filename = os.path.join(dirname, 'debug.log'), level = logging.DEBUG)

# PostgreSQL table classes:
database = PostgresqlDatabase('stop', user='stop', password='PT52lRecp4NBKQrZT9',
                           host='database')

# NOTE: most docker images run in UTC by default
def getCurrentTime(format = None):
	ts = datetime.now(tzlocal())
	if not format:
		return ts

	return ts.strftime(format)

class UnknownField(object):
    def __init__(self, *_, **__): pass

class BaseModel(Model):
    class Meta:
        database = database

class Gender_Stats(BaseModel):
    gender_text = CharField(null=True)
    rowid = IntegerField()
    t = CharField(column_name='t_id')
    t_ts = DateTimeField(default=getCurrentTime)

    class Meta:
        table_name = 'Gender_Stats'
        indexes = (
            (('t', 't_ts', 'rowid'), True),
        )
        primary_key = CompositeKey('rowid', 't', 't_ts')

# File removal after processing (match with legal documentation about storage)
class FileCleaner(object):
    def __init__(self):
        self.weak_references = dict()

    def cleanup(self, response, path):
        self.weak_references[weakref.ref(response, self._deleteTask)] = path

    def _deleteTask(self, wref):
        filepath = self.weak_references[wref]
        logging.debug('Deleting request result: {0}'.format(filepath))
        os.remove(filepath)

def initFlask():
    app = Flask(__name__)
    RequestID(app)
    return app

def getRequestID(request):
    return request.environ.get("FLASK_REQUEST_ID")

app = initFlask()
processor = EGProcessor(dirname = dirname)
cleaner = FileCleaner()

@app.before_request
def before_request():
    g.db = database
    g.db.connect()

@app.after_request
def after_request(response):
    g.db.close()
    return response

@app.route('/')
def index():
    return redirect("http://stop.capstone.utu.fi", code=302)

@app.route('/api/v<int:version>/classifyImage/', methods=['POST'])
@app.route('/api/v<int:version>/classifyImage/<type>', methods=['POST'])
def upload(version, type = 'mem'):
    try:
        if int(version) != 1:
            raise Exception('Invalid API version number.')

        if not processor.isValid():
            abort(503)

        stopCode = request.args.get('stop_code', default = '', type = str)
        detectEmotion = request.args.get('detect_emotion', default = 0, type = int)
        result = processor.processImage(request.files['image'].read(), result_fname = getRequestID(request), type = type, detect_emotion = detectEmotion != 0)

        # VH Insert result into database
        with database.atomic():
            i = 0
            ts = getCurrentTime() # only one value for current time per request
            for face in result:
                Gender_Stats.create(t = stopCode if stopCode else "unknown", t_ts = ts, rowid = i, gender_text = face["gender"])
                i += 1

        # by default avoid all disk I/O by using the result held in memory, explicit type argument will still roundtrip to disk
        # NOTE: the ordering in json results is not stable (python dictionaries are unordered)
        if type == 'mem':
            response = jsonify(result)
            response.headers.extend({'Cache-Control': 'no-cache', 'Expires': '0'})
            return response

        response = None
        resultFile = None

        if type == 'png':
            resultFile = os.path.join(dirname, getRequestID(request) + '.png')
            response = send_file(resultFile, mimetype='image/png')

        if type == 'json':
            resultFile = os.path.join(dirname, getRequestID(request) + '.json')
            response = send_file(resultFile, mimetype='application/json')

        if response == None:
            raise Exception('Unsupported type requested.')
        
        cleaner.cleanup(response, resultFile)
        # require clients to explicitly handle caching results, HTTP caches are unpredictable
        response.headers.extend({'Cache-Control': 'no-cache', 'Expires': '0'})
        
        return response
    except Exception as err:
        logging.error('An error has occurred whilst processing the file: "{0}", from: {1}'.format(err, stopCode))
        abort(400)

@app.route('/api/v<int:version>/classifyImage/', methods=['GET'])
@app.route('/api/v<int:version>/classifyImage/<type>', methods=['GET'])
def dowload(version, type = 'mem'):
    try:
        if int(version) != 1:
            raise Exception('Invalid API version number.')

        uri = request.args.get('uri', default = '', type = str)
        if uri == '':
            raise Exception('No URI for image provided.')

        if not processor.isValid():
            abort(503)

        stopCode = request.args.get('stop_code', default = '', type = str)
        detectEmotion = request.args.get('detect_emotion', default = 0, type = int)
        result = processor.processImage(requests.get(uri).content, result_fname = getRequestID(request), type = type, detect_emotion = detectEmotion != 0)

        # VH Insert result into database
        with database.atomic():
            i = 0
            ts = getCurrentTime() # only one value for current time per request
            for face in result:
                Gender_Stats.create(t = stopCode if stopCode else "unknown", t_ts = ts, rowid = i, gender_text = face["gender"])
                i += 1

        # by default avoid all disk I/O by using the result held in memory, explicit type argument will still roundtrip to disk
        # NOTE: the ordering in json results is not stable (python dictionaries are unordered)
        if type == 'mem':
            response = jsonify(result)
            response.headers.extend({'Cache-Control': 'no-cache', 'Expires': '0'})
            return response

        response = None
        resultFile = None

        if type == 'png':
            resultFile = os.path.join(dirname, getRequestID(request) + '.png')
            response = send_file(resultFile, mimetype='image/png')

        if type == 'json':
            resultFile = os.path.join(dirname, getRequestID(request) + '.json')
            response = send_file(resultFile, mimetype='application/json')

        if response == None:
            raise Exception('Unsupported type requested.')

        
        cleaner.cleanup(response, resultFile)
        # require clients to explicitly handle caching results, HTTP caches are unpredictable
        response.headers.extend({'Cache-Control': 'no-cache', 'Expires': '0'})
        
        
        return response

        
    except Exception as err:
        logging.error('An error has occurred whilst processing the file: "{0}", from: {1}'.format(err, stopCode))
        abort(400)

@app.errorhandler(400)
def bad_request(error):
    return make_response(jsonify({'error': 'We cannot process the file sent in the request.'}), 400, {'Cache-Control': 'no-cache', 'Expires': '0'})

@app.errorhandler(404)
def not_found(error):
    return make_response(jsonify({'error': 'Resource no found.'}), 404, {'Cache-Control': 'no-cache', 'Expires': '0'})

@app.errorhandler(503)
def server_error(error):
    logging.error('Internal server error: "{0}"'.format(error))
    return make_response(jsonify({'error': 'Service unavailable.'}), 503, {'Cache-Control': 'no-cache', 'Expires': '0'})
