#!/usr/bin/env python
"""
Copyright (c) 2017 Daniel Tobias

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""

import rospy
import tf
from geometry_msgs.msg import Twist, TwistStamped
from geometry_msgs.msg import Quaternion
from sensor_msgs.msg import Imu
from std_msgs.msg import String
from sensor_msgs.msg import Image
from sensor_msgs.msg import Joy
##import joinstates
from Xbox360 import XBox360
import sys, os
import time
from threading import Lock


"""
This node is a python implementation of the jetsoncar_joy.cpp with some added features and functionality  
This node takes in joystick, lidar twist, and neural twist and toggles the control between these inputs for
a final cmd_vel twist that get sent to the Teensy for steering and throttle

Note:
1. Y button trim left
2. B button trim right
3. X button is cruise control, it will read the current throttle value from the right trigger and repeat it until X is pressed again.
4. right trigger must be pressed to once at start to initialize all other commands

WIP
Add jointstate for URDF

"""

class twistControl(object):

    def __init__(self):
        self.throttleInitialized = False
        self.joy_timeout = 2.0
        self.lid_timeout = 0.30
        self.cnn_timeout = 0.1
        self.joy_time = time.time()
        self.lid_time = time.time()
        self.cnn_time = time.time()
        self.controller = XBox360()
        self.joy_cmd = TwistStamped()
        self.lid_cmd = TwistStamped()
        self.cnn_cmd = TwistStamped()
        self.cruiseControl = False
        self.cruiseThrottle = 0.5
        self.steeringAngle = 0.5
        self.throttle = 0.5
        self.trim = 0.0
        ##self.throttleLock = Lock()
        print "cmd_control"
       
        rospy.Subscriber("/imu", Imu, self.imuCB, queue_size=5)
        rospy.Subscriber("/lidar_twist", TwistStamped, self.lidarTwistCB, queue_size=5)
        rospy.Subscriber("/neural_twist", TwistStamped, self.neuralTwistCB, queue_size=5)
        rospy.Subscriber("/joy", Joy, self.joyCB, queue_size=5)
        self.vel_pub = rospy.Publisher("/cmd_vel", TwistStamped, queue_size = 1) 
        #self.state_pub = rospy.Publisher("/joint_states", JointState, queue_size=1)
        self.sound = rospy.Publisher("/sound_server/speech_synth", String, queue_size=1)        
        rospy.init_node ('cmd_control',anonymous=True)
        rate = rospy.Rate(66)
        while not rospy.is_shutdown():
            self.cmdRouter()
            rate.sleep()
            

    def imuCB(self, imu):
        try: 
            quaternion = (imu.orientation.x, imu.orientation.y, imu.orientation.z, imu.orientation.w)
            euler = tf.transformations.euler_from_quaternion(quaternion)
            roll = euler[0]
            pitch = euler[1]
            yaw = euler[2]
        except Exception as e:
            print(e)
            ##TODO update jointstate

    def lidarTwistCB(self, lidarTwist):
        try: 
            self.lid_cmd.twist = lidarTwist.twist
            if self.cruiseControl == True:
                
                if lidarTwist.twist.linear.x == 0.5:
                    self.lid_cmd.twist.linear.x = 0.5
                else:
                    self.lid_cmd.twist.linear.x = self.cruiseThrottle
            else:
                self.lid_cmd.twist.linear.x = self.throttle
            self.lid_time = time.time()
        except Exception as b:
            print(b)


    def neuralTwistCB(self, neuralTwist):
        try:
            self.cnn_cmd.twist = neuralTwist.twist
            
            if self.cruiseControl == True:
                self.cnn_cmd.twist.linear.x = self.cruiseThrottle
            else:
                self.cnn_cmd.twist.linear.x = self.throttle
            self.cnn_time = time.time()
        except Exception as a:
            print(a)
        

    def joyCB(self, joy):
        try:
            self.joystick = joy
            self.controller.update(joy)
            events = self.controller.buttonEvents()
            if 'X_pressed' in events:
                self.cruiseControl = not self.cruiseControl ##toggle cruise control
                self.cruiseThrottle = self.throttle   
            if self.joystick.axes[5] != 0.0 and not self.throttleInitialized:
                self.throttleInitialized = True  
            if self.throttleInitialized:
                self.steeringAngle = self.trim + (1-(((self.joystick.axes[0])/2.0)+0.5))  ## normed steering
                self.joy_cmd.twist.angular.z = self.steeringAngle
                self.throttle = (((1.0-((0.5*self.joystick.axes[5])+0.5))/2.0)+ 0.5)      ## normed throttle
                self.joy_cmd.twist.linear.x = self.throttle         
                if 'Y_pressed' in events:
                    self.trim = self.trim - 0.0025                ##trim left
                if 'B_pressed' in events:
                    self.trim = self.trim + 0.0025                ##trim right
                if 'X_pressed' in events:
                    self.cruiseControl = not self.cruiseControl   ##toggle cruise control
                    self.cruiseThrottle = self.throttle           ##set throttle speed
                if joy.buttons[0] == 1:                           ##Reverse, A button hit twice and hold down
                    self.joy_cmd.twist.linear.z = 1
                else: 
                    self.joy_cmd.twist.linear.z = 0
                if self.cruiseControl == True: 
                    self.joy_cmd.twist.linear.x = self.cruiseThrottle
                else: 
                    self.joy_cmd.twist.linear.x = self.throttle 
                rospy.loginfo(self.cruiseControl)
                self.joy_time = time.time()
                #rospy.loginfo(self.joy_time)
                #self.vel_pub.publish(self.joy_cmd)
            else:
                pass
            if 'X_pressed' in events:
                    self.cruiseControl = not self.cruiseControl ##toggle cruise control
                    self.cruiseThrottle = self.throttle 
                
        except Exception as f:
            print(f)
            

    def cmdRouter(self):     
        if self.joy_time+self.joy_timeout >= time.time():  ## If last joy message was recieved less than 2 seconds ago Joystick controlled      
            rospy.loginfo("Joy cmd")
            rospy.loginfo(self.joy_time+self.joy_timeout)
            rospy.loginfo(time.time())
            self.vel_pub.publish(self.joy_cmd) 
        elif self.lid_time+self.lid_timeout >= time.time(): ##else if last lidar twist was recieved less than 0.3 seconds ago lidar evasion controlled
            self.vel_pub.publish(self.lid_cmd)  
            rospy.loginfo("LIDAR cmd")
        elif self.cnn_time+self.cnn_timeout >= time.time(): ## else if last neural twist was recieved less than 0.1 seconds ago neural net controlled
            self.vel_pub.publish(self.cnn_cmd)
            rospy.loginfo("CNN cmd")
        else:                                               ##else if no command, neutral throttle and steering
            self.joy_cmd.twist.linear.x = 0.5
            self.joy_cmd.twist.angular.z = 0.5
            self.vel_pub.publish(self.joy_cmd)
            rospy.loginfo("Joy cmd else")

if __name__ == '__main__':
    try:
        twistControl()
    except rospy.ROSInterruptException:
        pass