import requests
import time
import os
import sys
import json
import threading
from getpass import getpass
import schedule
import event as e
import configuration as c
import RPi.GPIO as GPIO

#SERVER_URL = "https://home-automation-289621.uc.r.appspot.com"
#SERVER_URL = "http://127.0.0.1:4747"
SERVER_URL = "http://192.168.11.117:4747"

pins = [2, 3, 4, 7, 8, 9, 10, 11, 14, 15, 17, 18, 22, 23, 24, 27]


def calculate_max_duration(time):
    hours = 23 - time.hour
    minutes = 60 - time.minute

    return hours * 60 + minutes


def turn_on(pin):
    print("Turn on " + str(pin))
    GPIO.output(pin, GPIO.HIGH)


def turn_off(pin):
    print("Turn off " + str(pin))
    GPIO.output(pin, GPIO.LOW)


def schedule_off(time, day, duration, pin):
    new_day = day
    end_time = e.Time(0, 0)

    if duration > calculate_max_duration(time):
        # Next day calculation
        new_day = day + 1
        off_duration = duration - calculate_max_duration(time)
        end_time.hour = off_duration // 60
        end_time.minute = off_duration % 60
    else:
        # Same day calculation
        end_time.hour = time.hour + \
            (duration // 60) + (time.minute + (duration % 60)) // 60
        end_time.minute = (time.minute + duration % 60) % 60

    if new_day > 7:
        new_day = 1

    if new_day == 1:
        schedule.every().monday.at(str(end_time)).do(turn_off, pin)
    elif new_day == 2:
        schedule.every().tuesday.at(str(end_time)).do(turn_off, pin)
    elif new_day == 3:
        schedule.every().wednesday.at(str(end_time)).do(turn_off, pin)
    elif new_day == 4:
        schedule.every().thursday.at(str(end_time)).do(turn_off, pin)
    elif new_day == 5:
        schedule.every().friday.at(str(end_time)).do(turn_off, pin)
    elif new_day == 6:
        schedule.every().saturday.at(str(end_time)).do(turn_off, pin)
    elif new_day == 7:
        schedule.every().sunday.at(str(end_time)).do(turn_off, pin)


def schedule_job(event):
    GPIO.setup(event.pin, GPIO.OUT)

    if len(event.days) == 0 or len(event.days) == 7:
        schedule.every().day.at(str(event.time)).do(turn_on, event.pin)
    else:
        if 1 in event.days:
            schedule.every().monday.at(str(event.time)).do(turn_on, event.pin)
            schedule_off(event.time, 1, event.duration, event.pin)
        if 2 in event.days:
            schedule.every().tuesday.at(str(event.time)).do(turn_on, event.pin)
            schedule_off(event.time, 2, event.duration, event.pin)
        if 3 in event.days:
            schedule.every().wednesday.at(str(event.time)).do(turn_on, event.pin)
            schedule_off(event.time, 3, event.duration, event.pin)
        if 4 in event.days:
            schedule.every().thursday.at(str(event.time)).do(turn_on, event.pin)
            schedule_off(event.time, 4, event.duration, event.pin)
        if 5 in event.days:
            schedule.every().friday.at(str(event.time)).do(turn_on, event.pin)
            schedule_off(event.time, 5, event.duration, event.pin)
        if 6 in event.days:
            schedule.every().saturday.at(str(event.time)).do(turn_on, event.pin)
            schedule_off(event.time, 6, event.duration, event.pin)
        if 7 in event.days:
            schedule.every().sunday.at(str(event.time)).do(turn_on, event.pin)
            schedule_off(event.time, 7, event.duration, event.pin)


def run_scheduling():
    while True:
        schedule.run_pending()
        time.sleep(1)


def initial_setup():
    username = input("Enter your username: ")
    password = getpass("Enter your password: ")

    pload = json.dumps({"username": username, "password": password})

    r = requests.post(SERVER_URL + "/login", data=pload,
                      headers={'Content-type': 'application/json'})
    r_dict = r.json()

    if not r_dict["valid"]:
        print("Invalid username/password")
        print("Run program again to try again")
        sys.exit()

    print("Successful login...")
    print("Saving configuration...")
    f = open("config.txt", "w")
    f.write(r_dict["user"])
    f.close()

    return r_dict["user"]


def get_user():
    f = open("config.txt", "r")
    user = f.readline()

    r = requests.get(SERVER_URL + "/users/" + user)

    if r.status_code == 200:
        print("Successful login...")
        return user
    else:
        print("Invalid user... Reinitializing configuration")
        return initial_setup()


def get_configuration(user):

    r = requests.get(SERVER_URL + "/configurations/by-user/" + user)

    if r.status_code != 200:
        print("Error retrieving configuration, check internet connection.")
        sys.exit()

    r_dict = r.json()

    return c.Configuration(
        r_dict["systemStatus"], r_dict["rainPercentage"], r_dict["defaultDuration"], r_dict["update"], r_dict["_id"])


def set_update_off(configuration):
    r = requests.put(
        SERVER_URL + "/configurations/set-off-update/" + configuration.id)

    if r.status_code >= 400:
        print(
            "Error updating configuration status... Possible reconfiguration on next cycle")
    else:
        print("Update set off.")


def update_schedules(user):
    r = requests.get(
        SERVER_URL + "/devices/by-user-with-events/" + user)

    devices = r.json()

    schedule.clear()
    GPIO.cleanup()

    for device in devices:
        for event in device["events"]:
            print(event)
            schedule_job(e.Event(
                device["pin"], event["days"], e.Time(event["time"]["hour"], event["time"]["minute"]), e.Repetition(event["repetition"]["times"], event["repetition"]["date"], event["repetition"]["current"]), event["duration"]))


def setup():
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    for pin in pins:
        GPIO.setup(pin, GPIO.OUT, initial=GPIO.LOW)

    GPIO.cleanup()


def main():
    setup()
    user = ""
    if not os.path.isfile("./config.txt"):
        print("No configuration found... Initializing configuration")
        user = initial_setup()
    else:
        print("Validating user...")
        user = get_user()

    print("Initializing routine...")

    # Initialize separate thread to run scheduling jobs
    thread = threading.Thread(None, run_scheduling, "Schedule")
    thread.start()

    print("Schedule running.")

    while True:
        configuration = get_configuration(user)
        if configuration.update:
            print("Updating schedule...")
            update_schedules(user)
            set_update_off(configuration)
        time.sleep(1)

    thread.join()


if __name__ == "__main__":
    main()

[(29, 'RPi.GPIO.output', 'GPIO.output', True, 'import RPi.GPIO as GPIO\n'), (34, 'RPi.GPIO.output', 'GPIO.output', True, 'import RPi.GPIO as GPIO\n'), (39, 'event.Time', 'e.Time', True, 'import event as e\n'), (73, 'RPi.GPIO.setup', 'GPIO.setup', True, 'import RPi.GPIO as GPIO\n'), (109, 'getpass.getpass', 'getpass', False, 'from getpass import getpass\n'), (111, 'json.dumps', 'json.dumps', False, 'import json\n'), (113, 'requests.post', 'requests.post', False, 'import requests\n'), (135, 'requests.get', 'requests.get', False, 'import requests\n'), (147, 'requests.get', 'requests.get', False, 'import requests\n'), (155, 'configuration.Configuration', 'c.Configuration', True, 'import configuration as c\n'), (160, 'requests.put', 'requests.put', False, 'import requests\n'), (171, 'requests.get', 'requests.get', False, 'import requests\n'), (176, 'schedule.clear', 'schedule.clear', False, 'import schedule\n'), (177, 'RPi.GPIO.cleanup', 'GPIO.cleanup', True, 'import RPi.GPIO as GPIO\n'), (187, 'RPi.GPIO.setmode', 'GPIO.setmode', True, 'import RPi.GPIO as GPIO\n'), (188, 'RPi.GPIO.setwarnings', 'GPIO.setwarnings', True, 'import RPi.GPIO as GPIO\n'), (192, 'RPi.GPIO.cleanup', 'GPIO.cleanup', True, 'import RPi.GPIO as GPIO\n'), (208, 'threading.Thread', 'threading.Thread', False, 'import threading\n'), (103, 'schedule.run_pending', 'schedule.run_pending', False, 'import schedule\n'), (104, 'time.sleep', 'time.sleep', False, 'import time\n'), (120, 'sys.exit', 'sys.exit', False, 'import sys\n'), (151, 'sys.exit', 'sys.exit', False, 'import sys\n'), (190, 'RPi.GPIO.setup', 'GPIO.setup', True, 'import RPi.GPIO as GPIO\n'), (198, 'os.path.isfile', 'os.path.isfile', False, 'import os\n'), (219, 'time.sleep', 'time.sleep', False, 'import time\n'), (183, 'event.Time', 'e.Time', True, 'import event as e\n'), (183, 'event.Repetition', 'e.Repetition', True, 'import event as e\n'), (57, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (76, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (59, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (79, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (82, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (85, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (88, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (91, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (94, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (97, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (61, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (63, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (65, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (67, 'schedule.every', 'schedule.every', False, 'import schedule\n'), (69, 'schedule.every', 'schedule.every', False, 'import schedule\n')]