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', (['pin', 'GPIO.HIGH'], {}), True, 'import RPi.GPIO as GPIO\n'), (34, 'RPi.GPIO.output', 'GPIO.output', (['pin', 'GPIO.LOW'], {}), True, 'import RPi.GPIO as GPIO\n'), (39, 'event.Time', 'e.Time', (['(0)', '(0)'], {}), True, 'import event as e\n'), (73, 'RPi.GPIO.setup', 'GPIO.setup', (['event.pin', 'GPIO.OUT'], {}), True, 'import RPi.GPIO as GPIO\n'), (109, 'getpass.getpass', 'getpass', (['"""Enter your password: """'], {}), False, 'from getpass import getpass\n'), (111, 'json.dumps', 'json.dumps', (["{'username': username, 'password': password}"], {}), False, 'import json\n'), (113, 'requests.post', 'requests.post', (["(SERVER_URL + '/login')"], {'data': 'pload', 'headers': "{'Content-type': 'application/json'}"}), False, 'import requests\n'), (135, 'requests.get', 'requests.get', (["(SERVER_URL + '/users/' + user)"], {}), False, 'import requests\n'), (147, 'requests.get', 'requests.get', (["(SERVER_URL + '/configurations/by-user/' + user)"], {}), False, 'import requests\n'), (155, 'configuration.Configuration', 'c.Configuration', (["r_dict['systemStatus']", "r_dict['rainPercentage']", "r_dict['defaultDuration']", "r_dict['update']", "r_dict['_id']"], {}), True, 'import configuration as c\n'), (160, 'requests.put', 'requests.put', (["(SERVER_URL + '/configurations/set-off-update/' + configuration.id)"], {}), False, 'import requests\n'), (171, 'requests.get', 'requests.get', (["(SERVER_URL + '/devices/by-user-with-events/' + user)"], {}), 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', (['GPIO.BCM'], {}), True, 'import RPi.GPIO as GPIO\n'), (188, 'RPi.GPIO.setwarnings', 'GPIO.setwarnings', (['(False)'], {}), 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', (['None', 'run_scheduling', '"""Schedule"""'], {}), False, 'import threading\n'), (103, 'schedule.run_pending', 'schedule.run_pending', ([], {}), False, 'import schedule\n'), (104, 'time.sleep', 'time.sleep', (['(1)'], {}), 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', (['pin', 'GPIO.OUT'], {'initial': 'GPIO.LOW'}), True, 'import RPi.GPIO as GPIO\n'), (198, 'os.path.isfile', 'os.path.isfile', (['"""./config.txt"""'], {}), False, 'import os\n'), (219, 'time.sleep', 'time.sleep', (['(1)'], {}), False, 'import time\n'), (183, 'event.Time', 'e.Time', (["event['time']['hour']", "event['time']['minute']"], {}), True, 'import event as e\n'), (183, 'event.Repetition', 'e.Repetition', (["event['repetition']['times']", "event['repetition']['date']", "event['repetition']['current']"], {}), 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')]