#! /usr/bin/python

"""Provides PatrolScheduler class."""

import rospy
import os
import requests
import pycron
import json
from std_msgs.msg import String


class PatrolScheduler:
    """Implement the Patrol Scheduler node.

    The node reads schedule from database and publish
    the scheduled patrols at the correct time.
    """

    def __init__(self):
        """Register node and topics to ROS."""
        rospy.init_node("patrol_scheduler")

        self.patrol_publisher = rospy.Publisher(
            "scheduled_patrol",
            String,
            queue_size=10)

        self.robot_id = ""

        # Get robot name and server url from environment variables
        try:
            self.robot_name = os.environ["SECURBOT_ROBOT_NAME"]

            server_url = os.environ["SECURBOT_SERVER_URL"]
            api_path = os.environ["SECURBOT_API_PATH"]
            self.api_url = server_url + api_path
        except KeyError, e:
            rospy.logerr("Missing %s environement variable" % e.args[0])
            exit(-1)

    def retrieve_robot_id(self):
        """Get robot id matching this robot name from server."""
        # Get robot id
        request = requests.get("%s/robots" % self.api_url, timeout=10)
        request.raise_for_status()
        response = request.json()

        for robot in response:
            if robot["name"] == self.robot_name:
                self.robot_id = robot["_id"]
                break

        if self.robot_id == "":
            return []

        rospy.loginfo("Robot id is %s" % self.robot_id)
        return self.robot_id

    def retrieve_schedules(self):
        """Get the schedules from the server."""
        self.retrieve_robot_id()

        request = requests.get(
            "%s/robots/%s/schedules" % (self.api_url, self.robot_id),
            timeout=10)
        request.raise_for_status()
        return request.json()

    def retrieve_patrol_by_id(self, id):
        """Get a patrol from the server by its id."""
        request = requests.get(
            "%s/robots/%s/patrols/%s" % (self.api_url, self.robot_id, id),
            timeout=10)
        request.raise_for_status()
        response = request.json()

        patrol = dict()
        patrol["patrol"] = response["waypoints"]
        patrol["id"] = id

        return patrol

    def process_loop(self):
        """Send the patrol that should start this minute if there is one."""
        schedules = []

        rospy.loginfo("Processing scheduled patrols")

        # Retrieve schedules from server
        try:
            schedules = self.retrieve_schedules()
        except requests.exceptions.RequestException, e:
            rospy.logerr("Failed to retrieve schedules because %s", e.args[0])
            return

        # Print schedules for debug
        for schedule in schedules:
            rospy.loginfo(
                "Patrol %s scheduled at %s" %
                (schedule["patrol"], schedule["cron"])
            )

        # Get patrol ids that should run this minute
        patrol_ids = [
            (schedule["patrol"], schedule["repetitions"])
            for schedule in schedules
            if pycron.is_now(schedule["cron"]) and schedule["enabled"]
        ]

        # Multiple patrols. This is unfair standards for robot so we warn
        if len(patrol_ids) > 1:
            rospy.logwarn("Multiples patrols scheduled at the same time!")

        # Retrieve patrol matching each id
        patrols = []
        for id, rep in patrol_ids:
            rospy.loginfo("Patrol %s must run now. Retrieving..." % id)
            try:
                patrol = self.retrieve_patrol_by_id(id)
                patrol["repetitions"] = rep
                patrols.append(patrol)
            except requests.exceptions.RequestException, e:
                rospy.logwarn(
                    "Failed to retrieve patrol %s because %s",
                    e.args[0])
            rospy.loginfo("Patrol %s retrieved" % id)

        # Publish patrol to ROS
        for patrol in patrols:
            self.patrol_publisher.publish(json.dumps(patrol))

    def run(self):
        """Start node."""
        process_rate = rospy.Rate(1.0/60.0)

        while not rospy.is_shutdown():
            process_rate.sleep()
            self.process_loop()


if __name__ == "__main__":
    node = PatrolScheduler()
    node.run()
