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()

[((586, 613), 'RPi.GPIO.output', 'GPIO.output', (['pin', 'GPIO.HIGH'], {}), '(pin, GPIO.HIGH)\n', (597, 613), True, 'import RPi.GPIO as GPIO\n'), ((673, 699), 'RPi.GPIO.output', 'GPIO.output', (['pin', 'GPIO.LOW'], {}), '(pin, GPIO.LOW)\n', (684, 699), True, 'import RPi.GPIO as GPIO\n'), ((779, 791), 'event.Time', 'e.Time', (['(0)', '(0)'], {}), '(0, 0)\n', (785, 791), True, 'import event as e\n'), ((1973, 2004), 'RPi.GPIO.setup', 'GPIO.setup', (['event.pin', 'GPIO.OUT'], {}), '(event.pin, GPIO.OUT)\n', (1983, 2004), True, 'import RPi.GPIO as GPIO\n'), ((3544, 3576), 'getpass.getpass', 'getpass', (['"""Enter your password: """'], {}), "('Enter your password: ')\n", (3551, 3576), False, 'from getpass import getpass\n'), ((3590, 3646), 'json.dumps', 'json.dumps', (["{'username': username, 'password': password}"], {}), "({'username': username, 'password': password})\n", (3600, 3646), False, 'import json\n'), ((3656, 3754), 'requests.post', 'requests.post', (["(SERVER_URL + '/login')"], {'data': 'pload', 'headers': "{'Content-type': 'application/json'}"}), "(SERVER_URL + '/login', data=pload, headers={'Content-type':\n    'application/json'})\n", (3669, 3754), False, 'import requests\n'), ((4189, 4232), 'requests.get', 'requests.get', (["(SERVER_URL + '/users/' + user)"], {}), "(SERVER_URL + '/users/' + user)\n", (4201, 4232), False, 'import requests\n'), ((4463, 4523), 'requests.get', 'requests.get', (["(SERVER_URL + '/configurations/by-user/' + user)"], {}), "(SERVER_URL + '/configurations/by-user/' + user)\n", (4475, 4523), False, 'import requests\n'), ((4684, 4814), 'configuration.Configuration', 'c.Configuration', (["r_dict['systemStatus']", "r_dict['rainPercentage']", "r_dict['defaultDuration']", "r_dict['update']", "r_dict['_id']"], {}), "(r_dict['systemStatus'], r_dict['rainPercentage'], r_dict[\n    'defaultDuration'], r_dict['update'], r_dict['_id'])\n", (4699, 4814), True, 'import configuration as c\n'), ((4864, 4943), 'requests.put', 'requests.put', (["(SERVER_URL + '/configurations/set-off-update/' + configuration.id)"], {}), "(SERVER_URL + '/configurations/set-off-update/' + configuration.id)\n", (4876, 4943), False, 'import requests\n'), ((5172, 5237), 'requests.get', 'requests.get', (["(SERVER_URL + '/devices/by-user-with-events/' + user)"], {}), "(SERVER_URL + '/devices/by-user-with-events/' + user)\n", (5184, 5237), False, 'import requests\n'), ((5276, 5292), 'schedule.clear', 'schedule.clear', ([], {}), '()\n', (5290, 5292), False, 'import schedule\n'), ((5297, 5311), 'RPi.GPIO.cleanup', 'GPIO.cleanup', ([], {}), '()\n', (5309, 5311), True, 'import RPi.GPIO as GPIO\n'), ((5684, 5706), 'RPi.GPIO.setmode', 'GPIO.setmode', (['GPIO.BCM'], {}), '(GPIO.BCM)\n', (5696, 5706), True, 'import RPi.GPIO as GPIO\n'), ((5711, 5734), 'RPi.GPIO.setwarnings', 'GPIO.setwarnings', (['(False)'], {}), '(False)\n', (5727, 5734), True, 'import RPi.GPIO as GPIO\n'), ((5813, 5827), 'RPi.GPIO.cleanup', 'GPIO.cleanup', ([], {}), '()\n', (5825, 5827), True, 'import RPi.GPIO as GPIO\n'), ((6192, 6242), 'threading.Thread', 'threading.Thread', (['None', 'run_scheduling', '"""Schedule"""'], {}), "(None, run_scheduling, 'Schedule')\n", (6208, 6242), False, 'import threading\n'), ((3415, 3437), 'schedule.run_pending', 'schedule.run_pending', ([], {}), '()\n', (3435, 3437), False, 'import schedule\n'), ((3446, 3459), 'time.sleep', 'time.sleep', (['(1)'], {}), '(1)\n', (3456, 3459), False, 'import time\n'), ((3923, 3933), 'sys.exit', 'sys.exit', ([], {}), '()\n', (3931, 3933), False, 'import sys\n'), ((4638, 4648), 'sys.exit', 'sys.exit', ([], {}), '()\n', (4646, 4648), False, 'import sys\n'), ((5764, 5807), 'RPi.GPIO.setup', 'GPIO.setup', (['pin', 'GPIO.OUT'], {'initial': 'GPIO.LOW'}), '(pin, GPIO.OUT, initial=GPIO.LOW)\n', (5774, 5807), True, 'import RPi.GPIO as GPIO\n'), ((5879, 5909), 'os.path.isfile', 'os.path.isfile', (['"""./config.txt"""'], {}), "('./config.txt')\n", (5893, 5909), False, 'import os\n'), ((6519, 6532), 'time.sleep', 'time.sleep', (['(1)'], {}), '(1)\n', (6529, 6532), False, 'import time\n'), ((5484, 5538), 'event.Time', 'e.Time', (["event['time']['hour']", "event['time']['minute']"], {}), "(event['time']['hour'], event['time']['minute'])\n", (5490, 5538), True, 'import event as e\n'), ((5540, 5647), 'event.Repetition', 'e.Repetition', (["event['repetition']['times']", "event['repetition']['date']", "event['repetition']['current']"], {}), "(event['repetition']['times'], event['repetition']['date'],\n    event['repetition']['current'])\n", (5552, 5647), True, 'import event as e\n'), ((1328, 1344), 'schedule.every', 'schedule.every', ([], {}), '()\n', (1342, 1344), False, 'import schedule\n'), ((2067, 2083), 'schedule.every', 'schedule.every', ([], {}), '()\n', (2081, 2083), False, 'import schedule\n'), ((1419, 1435), 'schedule.every', 'schedule.every', ([], {}), '()\n', (1433, 1435), False, 'import schedule\n'), ((2181, 2197), 'schedule.every', 'schedule.every', ([], {}), '()\n', (2195, 2197), False, 'import schedule\n'), ((2355, 2371), 'schedule.every', 'schedule.every', ([], {}), '()\n', (2369, 2371), False, 'import schedule\n'), ((2530, 2546), 'schedule.every', 'schedule.every', ([], {}), '()\n', (2544, 2546), False, 'import schedule\n'), ((2707, 2723), 'schedule.every', 'schedule.every', ([], {}), '()\n', (2721, 2723), False, 'import schedule\n'), ((2883, 2899), 'schedule.every', 'schedule.every', ([], {}), '()\n', (2897, 2899), False, 'import schedule\n'), ((3057, 3073), 'schedule.every', 'schedule.every', ([], {}), '()\n', (3071, 3073), False, 'import schedule\n'), ((3233, 3249), 'schedule.every', 'schedule.every', ([], {}), '()\n', (3247, 3249), False, 'import schedule\n'), ((1511, 1527), 'schedule.every', 'schedule.every', ([], {}), '()\n', (1525, 1527), False, 'import schedule\n'), ((1605, 1621), 'schedule.every', 'schedule.every', ([], {}), '()\n', (1619, 1621), False, 'import schedule\n'), ((1698, 1714), 'schedule.every', 'schedule.every', ([], {}), '()\n', (1712, 1714), False, 'import schedule\n'), ((1789, 1805), 'schedule.every', 'schedule.every', ([], {}), '()\n', (1803, 1805), False, 'import schedule\n'), ((1882, 1898), 'schedule.every', 'schedule.every', ([], {}), '()\n', (1896, 1898), False, 'import schedule\n')]