#!flask/bin/python
import subprocess
import sqlite3
import secrets
from flask import Flask, abort, request, jsonify

START_PORT = 8128
GAME_EXEC = '/opt/godot/Godot_v3.1.1-stable_linux_server.64'
GAME_EXEC_ARG_PACK = '--main-pack'
GAME_EXEC_ARG_PACK_PATH = '/opt/godot/Muffrace Pre Alpha.pck'
GAME_EXEC_ARG_PORT = '--port={0}'
GAME_EXEC_ARG_ID = '--server-id={0}'
GAME_EXEC_ARG_SECRET = '--secret={0}'
GAME_EXEC_ARG_BOTS = '--bots={0}'
games = {}

try:
    with sqlite3.connect("database.db") as con:
        cur = con.cursor()
        cur.execute(
            'CREATE TABLE IF NOT EXISTS games (id INTEGER PRIMARY KEY, name TEXT, secret TEXT, ip TEXT, running INTEGER, port INTEGER, player_count INTEGER, bots INTEGER)')
        con.commit()
except:
    con.rollback()
finally:
    con.close()

app = Flask(__name__)


@app.route('/client/game/create', methods=['POST'])
def create_game():
    # TODO: block multiple request from same user
    if not request.json:
        abort(400)
    if not 'name' in request.json:
        abort(400)

    bots = False
    name = request.json.get('name')
    if 'bots' in request.json:
        bots = request.json.get('bots')

    try:
        with sqlite3.connect("database.db") as con:
            # check free port
            port = START_PORT
            cur = con.cursor()
            cur.execute("SELECT * FROM games WHERE port=?", (port,))
            rows = cur.fetchall()
            while rows:
                port += 1
                cur.execute("SELECT * FROM games WHERE port=?", (port,))
                rows = cur.fetchall()

            # check duplicate name
            base_name = name
            base_name_count = 1
            cur.execute("SELECT * FROM games WHERE name=?", (name,))
            rows = cur.fetchall()
            while rows:
                base_name_count += 1
                name = base_name + "_" + str(base_name_count)
                cur.execute("SELECT * FROM games WHERE name=?", (name,))
                rows = cur.fetchall()

            # gen secret
            secret = secrets.token_hex(32)
            cur.execute("INSERT INTO games (name,secret,ip,port,bots,running) VALUES (?,?,?,?,?,0)",
                        (name, secret, request.remote_addr, port, bots))
            con.commit()
            cur.execute("SELECT id FROM games WHERE secret=?",
                        (secret,))
            result = cur.fetchone()
            if result[0]:
                games[result[0]] = subprocess.Popen([GAME_EXEC, GAME_EXEC_ARG_PACK, GAME_EXEC_ARG_PACK_PATH, GAME_EXEC_ARG_ID.format(int(result[0])), GAME_EXEC_ARG_PORT.format(
                    port), GAME_EXEC_ARG_SECRET.format(secret), GAME_EXEC_ARG_BOTS.format(int(bots))])
    except:
        con.rollback()
        abort(500)
    finally:
        con.close()
        return jsonify({'name': name, 'port': port})


@app.route('/client/games', methods=['GET'])
def get_games():
    try:
        with sqlite3.connect("database.db") as con:
            cur = con.cursor()
            query = "SELECT * FROM games ORDER BY running"
            if 'open' in request.args:
                query = "SELECT * FROM games WHERE running=0"
            cur.execute(query)
            rows = cur.fetchall()
    except:
        con.rollback()
    finally:
        con.close()
        result = []
        for row in rows:
            result.append(
                {'name': row[1], 'player_count': row[6], 'running': (row[4] == 1), 'port': row[5], 'bots': row[7]})
        return jsonify(result)


@app.route('/game', methods=['PUT'])
def update_game():
    if not request.json:
        abort(400)
    if not 'secret' in request.json or not 'player_count' in request.json or not 'running' in request.json:
        abort(400)

    secret = request.json.get('secret')
    player_count = request.json.get('player_count')
    running = request.json.get('running')
    try:
        with sqlite3.connect("database.db") as con:
            cur = con.cursor()
            cur.execute("SELECT id FROM games WHERE secret=?",
                        (secret,))
            result = cur.fetchone()
            if result[0]:
                game_id = result[0]
                cur.execute("UPDATE games SET player_count=?,running=? WHERE id=?",
                            (player_count, running,  game_id,))
                con.commit()
            else:
                abort(401)
    except:
        con.rollback()
        return jsonify(False)
    finally:
        con.close()
        return jsonify(True)


@app.route('/game', methods=['DELETE'])
def close_game():
    if not request.json:
        abort(400)
    if not 'secret' in request.json:
        abort(400)

    secret = request.json.get('secret')
    try:
        with sqlite3.connect("database.db") as con:
            cur = con.cursor()
            cur.execute("SELECT id FROM games WHERE secret=?",
                        (secret,))
            result = cur.fetchone()
            if result[0]:
                game_id = result[0]
                cur.execute("DELETE FROM games WHERE id=?", (game_id,))
                con.commit()
                if games[game_id]:
                    games[game_id].terminate()
                    games[game_id].communicate()
                    del games[game_id]
            else:
                abort(401)
    except:
        con.rollback()
        return jsonify(False)
    finally:
        con.close()
        return jsonify(True)


if __name__ == '__main__':
    app.run(debug=True)
