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, 4, 29, 31), 'RPi.GPIO.output', 'GPIO.output', ({(29, 16, 29, 19): 'pin', (29, 21, 29, 30): 'GPIO.HIGH'}, {}), '(pin, GPIO.HIGH)', True, 'import RPi.GPIO as GPIO\n'), ((34, 4, 34, 30), 'RPi.GPIO.output', 'GPIO.output', ({(34, 16, 34, 19): 'pin', (34, 21, 34, 29): 'GPIO.LOW'}, {}), '(pin, GPIO.LOW)', True, 'import RPi.GPIO as GPIO\n'), ((39, 15, 39, 27), 'event.Time', 'e.Time', ({(39, 22, 39, 23): '0', (39, 25, 39, 26): '0'}, {}), '(0, 0)', True, 'import event as e\n'), ((73, 4, 73, 35), 'RPi.GPIO.setup', 'GPIO.setup', ({(73, 15, 73, 24): 'event.pin', (73, 26, 73, 34): 'GPIO.OUT'}, {}), '(event.pin, GPIO.OUT)', True, 'import RPi.GPIO as GPIO\n'), ((109, 15, 109, 47), 'getpass.getpass', 'getpass', ({(109, 23, 109, 46): '"""Enter your password: """'}, {}), "('Enter your password: ')", False, 'from getpass import getpass\n'), ((111, 12, 111, 68), 'json.dumps', 'json.dumps', ({(111, 23, 111, 67): "{'username': username, 'password': password}"}, {}), "({'username': username, 'password': password})", False, 'import json\n'), ((113, 8, 114, 67), 'requests.post', 'requests.post', (), '', False, 'import requests\n'), ((135, 8, 135, 51), 'requests.get', 'requests.get', ({(135, 21, 135, 50): "SERVER_URL + '/users/' + user"}, {}), "(SERVER_URL + '/users/' + user)", False, 'import requests\n'), ((147, 8, 147, 68), 'requests.get', 'requests.get', ({(147, 21, 147, 67): "SERVER_URL + '/configurations/by-user/' + user"}, {}), "(SERVER_URL + '/configurations/by-user/' + user)", False, 'import requests\n'), ((155, 11, 156, 117), 'configuration.Configuration', 'c.Configuration', ({(156, 8, 156, 30): "r_dict['systemStatus']", (156, 32, 156, 56): "r_dict['rainPercentage']", (156, 58, 156, 83): "r_dict['defaultDuration']", (156, 85, 156, 101): "r_dict['update']", (156, 103, 156, 116): "r_dict['_id']"}, {}), "(r_dict['systemStatus'], r_dict['rainPercentage'], r_dict[\n    'defaultDuration'], r_dict['update'], r_dict['_id'])", True, 'import configuration as c\n'), ((160, 8, 161, 74), 'requests.put', 'requests.put', ({(161, 8, 161, 73): "SERVER_URL + '/configurations/set-off-update/' + configuration.id"}, {}), "(SERVER_URL + '/configurations/set-off-update/' + configuration.id)", False, 'import requests\n'), ((171, 8, 172, 60), 'requests.get', 'requests.get', ({(172, 8, 172, 59): "SERVER_URL + '/devices/by-user-with-events/' + user"}, {}), "(SERVER_URL + '/devices/by-user-with-events/' + user)", False, 'import requests\n'), ((176, 4, 176, 20), 'schedule.clear', 'schedule.clear', ({}, {}), '()', False, 'import schedule\n'), ((177, 4, 177, 18), 'RPi.GPIO.cleanup', 'GPIO.cleanup', ({}, {}), '()', True, 'import RPi.GPIO as GPIO\n'), ((187, 4, 187, 26), 'RPi.GPIO.setmode', 'GPIO.setmode', ({(187, 17, 187, 25): 'GPIO.BCM'}, {}), '(GPIO.BCM)', True, 'import RPi.GPIO as GPIO\n'), ((188, 4, 188, 27), 'RPi.GPIO.setwarnings', 'GPIO.setwarnings', ({(188, 21, 188, 26): '(False)'}, {}), '(False)', True, 'import RPi.GPIO as GPIO\n'), ((192, 4, 192, 18), 'RPi.GPIO.cleanup', 'GPIO.cleanup', ({}, {}), '()', True, 'import RPi.GPIO as GPIO\n'), ((208, 13, 208, 63), 'threading.Thread', 'threading.Thread', ({(208, 30, 208, 34): 'None', (208, 36, 208, 50): 'run_scheduling', (208, 52, 208, 62): '"""Schedule"""'}, {}), "(None, run_scheduling, 'Schedule')", False, 'import threading\n'), ((103, 8, 103, 30), 'schedule.run_pending', 'schedule.run_pending', ({}, {}), '()', False, 'import schedule\n'), ((104, 8, 104, 21), 'time.sleep', 'time.sleep', ({(104, 19, 104, 20): '(1)'}, {}), '(1)', False, 'import time\n'), ((120, 8, 120, 18), 'sys.exit', 'sys.exit', ({}, {}), '()', False, 'import sys\n'), ((151, 8, 151, 18), 'sys.exit', 'sys.exit', ({}, {}), '()', False, 'import sys\n'), ((190, 8, 190, 51), 'RPi.GPIO.setup', 'GPIO.setup', (), '', True, 'import RPi.GPIO as GPIO\n'), ((198, 11, 198, 41), 'os.path.isfile', 'os.path.isfile', ({(198, 26, 198, 40): '"""./config.txt"""'}, {}), "('./config.txt')", False, 'import os\n'), ((219, 8, 219, 21), 'time.sleep', 'time.sleep', ({(219, 19, 219, 20): '(1)'}, {}), '(1)', False, 'import time\n'), ((183, 46, 183, 100), 'event.Time', 'e.Time', ({(183, 53, 183, 74): "event['time']['hour']", (183, 76, 183, 99): "event['time']['minute']"}, {}), "(event['time']['hour'], event['time']['minute'])", True, 'import event as e\n'), ((183, 102, 183, 205), 'event.Repetition', 'e.Repetition', ({(183, 115, 183, 143): "event['repetition']['times']", (183, 145, 183, 172): "event['repetition']['date']", (183, 174, 183, 204): "event['repetition']['current']"}, {}), "(event['repetition']['times'], event['repetition']['date'],\n    event['repetition']['current'])", True, 'import event as e\n'), ((57, 8, 57, 24), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((76, 8, 76, 24), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((59, 8, 59, 24), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((79, 12, 79, 28), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((82, 12, 82, 28), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((85, 12, 85, 28), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((88, 12, 88, 28), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((91, 12, 91, 28), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((94, 12, 94, 28), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((97, 12, 97, 28), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((61, 8, 61, 24), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((63, 8, 63, 24), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((65, 8, 65, 24), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((67, 8, 67, 24), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n'), ((69, 8, 69, 24), 'schedule.every', 'schedule.every', ({}, {}), '()', False, 'import schedule\n')]