#!/usr/bin/python3

import mysql.connector
from mysql.connector import Error
from datetime import datetime
import time
import calendar
from dateutil.relativedelta import relativedelta
import os

import subprocess
from moviepy.editor import VideoFileClip

import cv2
from http.server import BaseHTTPRequestHandler, HTTPServer
import argparse
from PIL import Image, ImageFont, ImageDraw
import socket as Socket

active_data = []
# Create TV dictionary
tv_dict = {
    "328": "/dev/video1",
    "334": "/dev/video2",
    "339": "/dev/video3",
    "341": "/dev/video4",
    "342": "/dev/video5",
    "GO": "/dev/video6",
    "Kisdékáni": "/dev/video0",
}
# Create a TV dictionary for occupancy
tv_occup = {
    "328": [],
    "334": [],
    "339": [],
    "341": [],
    "342": [],
    "GO": [],
    "Kisdékáni": [],
}

img_types = ['.tiff', '.pjp', '.jfif', '.gif', '.svg', '.bmp', '.png', '.jpeg', '.svgz', '.jpg', '.webp', '.ico',
             '.xmb', '.dib', '.tif', '.pjpeg', '.avif']
video_types = ['.ogm', '.wmv', '.mpg', '.webm', '.ogv', '.mov', '.asx', '.mpeg', '.mp4', '.m4v', '.avi']

# DB FUNCTIONS:


def save_periodicity(cursor):
    """Get and split periodicity information & Save periodicity data"""

    cursor.execute("SELECT fajl_id, periodikussag from adatok ORDER BY mettol ASC")
    data_list = cursor.fetchall()
    split_data = dict()
    for data in data_list:
        if data[1] != "":
            # Split periodicity information
            tmp_data = data[1].split(':')

            # Save&Update correct weekdays
            if tmp_data[4] != "" or tmp_data[4] != "*":
                mon = tmp_data[4].replace("1", "Monday")
                tue = mon.replace("2", "Tuesday")
                wed = tue.replace("3", "Wednesday")
                thu = wed.replace("4", "Thursday")
                fri = thu.replace("5", "Friday")
                sat = fri.replace("6", "Saturday")
                sun = sat.replace("7", "Sunday")
                tmp_data[4] = sun
            split_data[data[0]] = tmp_data
        else:
            split_data[data[0]] = data[1]

    # Save periodicity data
    cursor.execute(
        "SELECT adatok.fajl_id, adatok.mettol, adatok.meddig, tv_adat.tv_id FROM adatok INNER JOIN  tv_adat ON adatok.fajl_id=tv_adat.fajl_id WHERE adatok.periodikussag!='' ORDER BY adatok.mettol ASC")
    periodic_shared_data = cursor.fetchall()

    return split_data, periodic_shared_data


def save_priority(cursor):
    """Get and return with priority information"""

    cursor.execute(
        "select adatok.fajl_id, adatok.prioritas, jogosultsagok.max_prioritas FROM (((adatok INNER JOIN felhasznaloi_fajlok ON "
        "adatok.fajl_id=felhasznaloi_fajlok.adat_id) INNER JOIN felhasznalok ON "
        "felhasznaloi_fajlok.user_shib=felhasznalok.shibboleth_id) INNER JOIN csoportok ON "
        "felhasznalok.csoport_id=csoportok.csoport_id) INNER JOIN jogosultsagok ON "
        "csoportok.jogosultsagok=jogosultsagok.id")
    return cursor.fetchall()


def find_element(data, prior_info):
    """ Find input info for input data """
    for info in prior_info:
        if data[0] == info[0]:
            return info
    return None


def check_periodic_info(now, data, periodic_shared_data, period_info, curr_shared_data):
    """ Check the start date of periodic data """

    if now.strftime("%Y-%m-%d %H:%M") >= data[1].strftime("%Y-%m-%d %H:%M") and periodic_shared_data.count(data) != 0:
        data_period = period_info[data[0]]
        if check_time(now.month, data_period[2], 2):  # Check month
            if check_time(now.day, data_period[3], 3):  # Check day
                if check_time(now.weekday(), data_period[4], 4):  # Check day of week
                    if check_time(now.hour, data_period[0], 0):  # Check hour
                        if check_time(now.minute, data_period[1], 1):  # Check minute
                            if len(curr_shared_data) == 0 or curr_shared_data.count(data) == 0:
                                return True
    return False


def check_periodic_end_time(now, data, periodic_shared_data, period_info, curr_shared_data):
    """ Check the end date of periodic data """

    if now.strftime("%Y-%m-%d %H:%M") >= data[2].strftime("%Y-%m-%d %H:%M"):
        return True

    # Calculate difference => end-start
    diff = str(data[2] - data[1])
    # Split data I.
    split_diff = diff.split(", ")
    # Split data II.
    time_split = split_diff[-1].split(":")  # time_split FORMAT -> [hour, minute, second]

    data_period = period_info[data[0]]

    if periodic_shared_data.count(data) != 0:
        if check_time(now.month, data_period[2], 2):  # Check month
            if check_time(now.day, data_period[3], 3):  # Check day
                if check_time(now.weekday(), data_period[4], 4):  # Check day of week
                    # Calculate right end time
                    updated_time = now + relativedelta(hours=int(time_split[0]), minutes=int(time_split[1]))
                    if check_time(updated_time.hour, data_period[0], 0):  # Check hour
                        if check_time(updated_time.minute, data_period[1], 1):  # Check minute
                            return True
    return False


def check_time(now, data, category):
    """ Check now is in data, return with a bool value """

    data_split = data.split(',')
    for tmp in data_split:
        if tmp == now or tmp == "*":
            return True
        else:
            slash_pos = tmp.find('/')
            if slash_pos != -1:
                if tmp[0] == "*":
                    period = tmp.split('/')[1]
                    if category == 0:  # Hour
                        i = int(period)
                        while i <= 24:
                            if now % i == 0:
                                return True
                            i = i + int(period)
                        break
                    if category == 1:  # Minute
                        i = int(period)
                        while i <= 60:
                            if now % i == 0:
                                return True
                            i = i + int(period)
                        break
                    if category == 2:  # Month
                        i = int(period)
                        while i <= 12:
                            if now % i == 0:
                                return True
                            i = i + int(period)
                        break
                    if category == 3:  # Day
                        pairs = ["4", "6", "9", "11"]
                        feb = ""
                        if datetime.today().year % 4 == 0:
                            feb = "29"
                        else:
                            feb = "28"
                        odd = ["1", "3", "5", "7", "8", "10", "12"]
                        i = int(period)
                        this_month = datetime.today().month
                        if this_month in pairs:
                            while i <= 30:
                                if now % i == 0:
                                    return True
                                i = i + int(period)
                            break
                        elif this_month in odd:
                            while i <= 31:
                                if now % i == 0:
                                    return True
                                i = i + int(period)
                            break
                        elif this_month == "2":  # February
                            while i <= int(feb):
                                if now % i == 0:
                                    return True
                                i = i + int(period)
                            break
                    if category == 4:
                        days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
                        i = int(period)
                        while i <= 7:
                            if now == days[i - 1]:
                                return True
                            i = i + int(period)
                        break
            # Check intervals
            interval_pos = tmp.find('-')
            if interval_pos != -1:  # If tmp is an interval
                # Split tmp
                split_tmp = tmp.split('-')
                if split_tmp[0] <= now <= split_tmp[1]:
                    return True
            else:  # If tmp just a simple number
                if now == tmp:
                    return True
    return False


def find_data(tmp, curr_shared_data):

    for data in curr_shared_data:
        if data[0] == tmp:
            return data
    return None


def check_tv_with_prior(curr_shared_data, prior_info):
    """Check and set the tv occupancy"""

    global active_data
    global tv_occup

    #  Check TV occupancy
    for tv in tv_occup:
        # Find the largest priority
        max_prior = 0
        if len(tv_occup[tv]) != 0:
            for tmp in tv_occup[tv]:
                data = find_data(tmp, curr_shared_data)
                if data:
                    info = find_element(data, prior_info)
                    if info[1] == "1":
                        if max_prior < info[2]:
                            max_prior = int(info[2])
            for data in curr_shared_data:
                if data[3] == tv:
                    info = find_element(data, prior_info)
                    if info[1] == "1":
                        if max_prior < int(info[2]):
                            tv_occup[tv] = []
                            tv_occup[tv].append(data[0])
                            max_prior = int(info[2])
                        if max_prior == int(info[2]):
                            if data[0] not in tv_occup[tv]:
                                tv_occup[tv].append(data[0])
                    else:
                        if max_prior == 0:
                            if data[0] not in tv_occup[tv]:
                                tv_occup[tv].append(data[0])
        else:
            for data in curr_shared_data:
                if data[3] == tv:
                    info = find_element(data, prior_info)
                    if info[1] == "1":
                        max_prior = int(info[2])
                    tv_occup[tv].append(data[0])

    # Save active data:
    for data in curr_shared_data:
        for tv in tv_occup:
            if data[0] in tv_occup[tv]:
                if len(active_data) == 0 or data not in active_data:
                    active_data.append(data)


def set_free_tv(period_info, curr_shared_data, periodic_shared_data):
    """ Check the active shared data and when done, set free that tv """

    now = datetime.today()
    global active_data
    global tv_occup

    for tv in tv_occup:
        if len(tv_occup[tv]) != 0:
            for data in active_data:
                if data[0] in tv_occup[tv]:
                    if period_info[data[0]] == "":
                        if now.strftime("%Y-%m-%d %H:%M") >= data[2].strftime("%Y-%m-%d %H:%M"):
                            tv_occup[tv].remove(data[0])
                            if data in active_data:
                                active_data.remove(data)
                            if data in curr_shared_data:
                                curr_shared_data.remove(data)
                            break
                    elif period_info[data[0]] != "":
                        if now.strftime("%Y-%m-%d %H:%M") >= data[2].strftime("%Y-%m-%d %H:%M"):
                            tv_occup[tv].remove(data[0])
                            if data in active_data:
                                active_data.remove(data)
                            if data in curr_shared_data:
                                curr_shared_data.remove(data)
                            break
                        elif check_periodic_end_time(now, data, periodic_shared_data, period_info, curr_shared_data):
                            tv_occup[tv].remove(data[0])
                            if data in active_data:
                                active_data.remove(data)
                            break


def save_data(cursor, curr_shared_data, end_shared_data, periodic_shared_data, period_info):
    """ Save data based on sharing times and periodicity to the corresponding array """

    # Save actual datetime
    today = datetime.today()
    now = today.strftime("%Y-%m-%d %H:%M")
    cursor.execute("SELECT adatok.fajl_id, adatok.mettol, adatok.meddig, tv_adat.tv_id FROM adatok INNER JOIN  tv_adat ON adatok.fajl_id=tv_adat.fajl_id ORDER BY adatok.mettol ASC")
    datalist = cursor.fetchall()

    # Save the current sharing data to 'curr_shared_data' array
    for data in datalist:
        if now >= data[2].strftime("%Y-%m-%d %H:%M"):
            if len(end_shared_data) == 0 or end_shared_data.count(data) == 0:
                end_shared_data.append(data)

        elif periodic_shared_data.count(data) == 0:
            if now >= data[1].strftime("%Y-%m-%d %H:%M"):
                if len(curr_shared_data) == 0 or curr_shared_data.count(data) == 0:
                    curr_shared_data.append(data)

        elif periodic_shared_data.count(data) != 0:
            if check_periodic_info(today, data, periodic_shared_data, period_info, curr_shared_data):
                curr_shared_data.append(data)

    # Check current shared data validation
    for curr_data in curr_shared_data:
        if now >= curr_data[2].strftime("%Y-%m-%d %H:%M"):
            if len(end_shared_data) == 0 or end_shared_data.count(curr_data) == 0:
                end_shared_data.append(curr_data)


def check_data(cursor, curr_shared_data, end_shared_data, periodic_shared_data, period_info, prior_info):
    """Check and get the correct informations for sharing"""

    # Get periodicity info
    period_info, periodic_shared_data = save_periodicity(cursor)

    # Get priority info
    prior_info = save_priority(cursor)

    # Save data to correct array
    save_data(cursor, curr_shared_data, end_shared_data, periodic_shared_data, period_info)

    # Check active shared data
    set_free_tv(period_info, curr_shared_data, periodic_shared_data)

    # Check TV occupancy and priority
    check_tv_with_prior(curr_shared_data, prior_info)


def main():
    """  Connect to MySQL database """
    conn = None
    try:
        conn = mysql.connector.connect(host='localhost', user='*****', password='*****', database='tv')
        if conn.is_connected():
            print('Connected to MySQL database')

        cursor = conn.cursor()
        stream(cursor)

    except Error as e:
        print(e)

    finally:
        if conn is not None and conn.is_connected():
            cursor.close()
            conn.close()

# STREAM FUNCTIONS & CLASS


def save_video(tmp):
    saved_pictures = []
    video = cv2.VideoCapture(tmp)
    tmp_name, ext = os.path.splitext(tmp)
    name = tmp_name + '_pictures'

    path_and_name = '../uploads/' + name + '/frame'

    try:
        if not os.path.exists(name):
            os.makedirs(name)

    except OSError:
        print('Error: Creating directory of data')

    curr_frame = 0
    while True:
        ret, frame = video.read()
        if ret:
            name = path_and_name + str(curr_frame) + '.jpg'
            print('Creating...' + name)

            cv2.imwrite(name, frame)
            saved_pictures.append(name)

            curr_frame += 1
        else:
            break
    video.release()
    cv2.destroyAllWindows()

    return saved_pictures


def get_active_datalist(tv):
    global active_data
    datalist = []
    for data in active_data:
        if data[3] == tv:
            datalist.append(data)
    return datalist


def stream(cursor):
    global tv_occup
    global tv_dict
    # Array of data to share now
    curr_shared_data = []
    # Array of shared data
    end_shared_data = []
    # An array of data to be periodically shared
    periodic_shared_data = []
    # Array of periodicity information
    period_info = dict()
    # Array of priority information
    prior_info = []
    i = 1

    while i == 1:
        i = 2
        # CALL check_data FUNCTION
        check_data(cursor, curr_shared_data, end_shared_data, periodic_shared_data, period_info, prior_info)
        file_name = './veled_tervezzuk.jpg'
        path = '../uploads/'

        for tv in tv_occup:
            # Check the TV data
            datalist = get_active_datalist(tv)
            act_video = tv_dict[tv]
            if not datalist:
                os.system("./mpeg_streamer.sh " + file_name + " 0 " + act_video + " &")
                time.sleep(5)
            else:
                for tmp in datalist:
                    name = "/" + tmp[0]
                    tmp_name, ext = os.path.splitext(name)
                    full_name = path + tmp[0]

                    if ext == ".txt" or ext in img_types:
                        if ext == '.txt':
                            with open(full_name, "r", encoding="utf-8") as f:
                                data = f.read().rstrip("\n")
                            f.close()
                            tmp_img = Image.new('RGB', (854, 480), color=(93, 188, 210))
                            d = ImageDraw.Draw(tmp_img)
                            font = ImageFont.truetype("arial.ttf", 30, encoding="utf-8")

                            d.text((20, 20), data, font=font)
                            img_name = tmp_name[1:] + '.jpg'
                            tmp_img.save(img_name)
                            full_name = path + tmp[0]
                            ext = '.jpg'

                        # Call bash script
                        os.system("./mpeg_streamer.sh "+full_name+" 0 "+act_video+" &")
                        time.sleep(5)
                    elif ext in video_types:

                        # Calculate video duration
                        clip = VideoFileClip(full_name)
                        duration = clip.duration
                        # Call bash script
                        os.system("./mpeg_streamer.sh " + full_name + " 1 " + act_video + str(duration) + " &")
                        time.sleep(duration)

                        # saved_pictures = save_video(tmp)
                        # if len(saved_pictures) != 0:
                        #     for image in saved_pictures:
                        #         img = cv2.imread(image)
                                # os.system("./mpeg_streamer.sh "+img+" 0 "+act_video+" &")
                    else:
                        os.system("./mpeg_streamer.sh " + file_name + " 0 " + act_video + " &")
                        time.sleep(5)


if __name__ == '__main__':
    main()
