import json
from flask import Flask, render_template, request, g, request_tearing_down
import psycopg2
import psycopg2.extras
import time
from datetime import datetime, timedelta
import sys
import dateutil
import dateutil.tz

### Flask app object ###

app = Flask(__name__)
app.config.from_pyfile('config.py')

### Database connection ###

db_connection = None
db = None

# XXX: This is safe only because we never write to the database. Otherwise, we would
# have to handle transactions and roll them back if an exception occurs during
# processing of the request.

def db_connect():
    global db_connection, db
    db_connection = psycopg2.connect(
                host = 'localhost',
                user = app.config['DB_USER'],
                password = app.config['DB_PASSWD'],
                dbname = app.config['DB_NAME'],
            )
    db = db_connection.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor)

def db_query(query, args=()):
    if db is None:
        db_connect()
    try:
        db.execute(query, args)
    except psycopg2.DatabaseError:
        # Reconnect if the connection died (timeout, server restart etc.)
        db_connect()
        db.execute(query, args)

def db_reset_signal(sender, **extra):
    # At the end of every request, we have to close the implicitly opened
    # transaction. Otherwise we end up with serving stale data.
    if db_connection is not None:
        try:
            db_connection.rollback()
        except:
            pass

request_tearing_down.connect(db_reset_signal, app)

### Schedule ###

def get_date():
    """Return a datetime object corresponding to the start of the given date."""
    try:
        d = request.args.get('date', "")
        dt = datetime.strptime(d, "%Y-%m-%d")
    except ValueError:
        dt = datetime.today()
    tz = dateutil.tz.tzlocal()
    return dt.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=tz)

room_list = {
    'i': [
        ('Z1', 'zoom-1@d3s.mff.cuni.cz'),
        ('Z2', 'zoom-2@d3s.mff.cuni.cz'),
        ('Z3', 'zoom-3@d3s.mff.cuni.cz'),
        ('Z4', 'zoom-4@d3s.mff.cuni.cz'),
        ('Z5', 'zoom-5@d3s.mff.cuni.cz'),
        ('Z6', 'zoom-6@d3s.mff.cuni.cz'),
        ('Z7', 'zoom-7@d3s.mff.cuni.cz'),
        ('Z8', 'zoom-8@d3s.mff.cuni.cz'),
        ('Z9', 'zoom-9@d3s.mff.cuni.cz'),
        ('Z10', 'zoom-10@d3s.mff.cuni.cz'),
    ],
    'm': [
        ('ZM1', 'zoom-m-1@d3s.mff.cuni.cz'),
        ('ZM2', 'zoom-m-2@d3s.mff.cuni.cz'),
        ('ZM3', 'zoom-m-3@d3s.mff.cuni.cz'),
        ('ZM4', 'zoom-m-4@d3s.mff.cuni.cz'),
        ('ZM7', 'zoom-m-7@d3s.mff.cuni.cz'),
        ('ZM8', 'zoom-m-8@d3s.mff.cuni.cz'),
    ],
}

@app.route('/')
def main_page():
    dt = get_date()
    g.date = dt.strftime("%Y-%m-%d")
    g.dow = dt.strftime("%A")

    hours_arg = request.args.get("hours", "")
    if hours_arg in ["0", "1", "2"]:
        g.hours = int(hours_arg)
    else:
        g.hours = 0

    if g.hours == 0:
        slot_size = 100*60
        first_slot = 9*3600
        num_slots = 10
    elif g.hours == 1:
        slot_size = 3600
        first_slot = 8*3600
        num_slots = 16
    else:
        slot_size = 3600
        first_slot = 0
        num_slots = 24

    g.rooms = request.args.get("rooms", "")
    if g.rooms not in room_list:
        g.rooms = "i"
    rooms = room_list[g.rooms]

    num_rooms = len(rooms)
    email_to_room_index = { rooms[i][1]: i for i in range(num_rooms) }
    room_box_width = 100
    room_hour_height = 50
    room_slot_height = room_hour_height * slot_size // 3600
    row_offset = 70

    g.total_width = room_box_width * num_rooms
    g.total_height = num_slots * room_slot_height
    g.room_boxes = [{
            "x": row_offset + i * room_box_width + 1,
            "w": room_box_width - 1,
            "h": g.total_height - 1,
            "name": rooms[i][0],
        } for i in range(num_rooms)]

    g.slot_boxes = [{
            "x": row_offset + 1,
            "y": i * room_slot_height + 1,
            "w": num_rooms * room_box_width,
            "h": room_slot_height - 1,
        } for i in range(num_slots)]

    g.slot_labels = [{
            "x": 0,
            "y": i * room_slot_height,
            "w": row_offset - 2,
            "h": room_slot_height - 1,
            "label": (dt + timedelta(seconds = first_slot + i*slot_size)).strftime("%H:%M"),
        } for i in range(num_slots)]

    # XXX: No meeting is ever longer than 24 hours
    db_query("""
            SELECT m.meeting_id, m.topic, s.start_time, s.duration, u.email, u.full_name
            FROM zoom_meetings m
            JOIN zoom_users u ON u.uid = m.host_uid
            JOIN zoom_schedule s ON s.mid = m.mid
            WHERE s.start_time >= DATE %s - INTERVAL '1 day'
              AND s.start_time <  DATE %s + INTERVAL '1 day'
            ORDER BY u.email, s.start_time
        """,
        (dt, dt))

    g.meetings = []
    prev_room_i = -1
    prev_end_t = None
    for r in db.fetchall():
        i = email_to_room_index.get(r.email, -1)
        if i < 0:
            continue
        # Datetime in the DB is in UTC, but psycopg2 interprets it as local time
        start_dt = r.start_time.replace(tzinfo=dateutil.tz.tz.tzutc())
        start_t = int(start_dt.timestamp())
        end_t = start_t + r.duration*60
        rel_start = start_t - dt.timestamp() - first_slot
        rel_end = rel_start + r.duration*60
        start = max(0, int(rel_start))
        end = min(num_slots * slot_size, int(rel_end))
        app.logger.debug("Meeting: %s start_t=%s start=%s end=%s room_i=%s", r, start_t, start, end, i)
        if start < end:
            coll = (i == prev_room_i and start_t < prev_end_t)
            g.meetings.append({
                "x": row_offset + i * room_box_width + 4 + 10*int(coll),
                "y": int(start / 3600. * room_hour_height),
                "w": room_box_width - 7 - 10*int(coll),
                "h": int((end - start) / 3600 * room_hour_height),
                "start": time.strftime("%H:%M", time.localtime(start_t)),
                "end": time.strftime("%H:%M", time.localtime(end_t)),
                "topic": r.topic,
                "coll": coll,
            })
            prev_room_i = i
            prev_end_t = end_t

    return render_template('main.html')