'''
A Python class for interacting with the Brookstone Rover 2.0.  

Copyright (C) 2014 Simon D. Levy

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as 
published by the Free Software Foundation, either version 3 of the 
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
'''

import struct
import threading
import socket
import time

from blowfish import Blowfish
from adpcm import decodeADPCMToPCM
from byteutils import *

    
class Rover:

    def __init__(self):
        ''' Creates a Rover object that you can communicate with. 
        '''
      
        self.HOST = '192.168.1.100'
        self.PORT = 80
        
        TARGET_ID = 'AC13'
        TARGET_PASSWORD = 'AC13'      
        
        self.TREAD_DELAY_SEC = 1.0
        self.KEEPALIVE_PERIOD_SEC = 60
        
                            
        # Create command socket connection to Rover      
        self.commandsock = self._newSocket()
        
        # Send login request with four arbitrary numbers
        self._sendCommandIntRequest(0, [0, 0, 0, 0])
                
        # Get login reply
        reply = self._receiveCommandReply(82)
                
        # Extract Blowfish key from camera ID in reply
        cameraID = reply[25:37].decode('utf-8')
        key = TARGET_ID + ':' + cameraID + '-save-private:' + TARGET_PASSWORD
        
        # Extract Blowfish inputs from rest of reply
        L1 = bytes_to_int(reply, 66)
        R1 = bytes_to_int(reply, 70)
        L2 = bytes_to_int(reply, 74)
        R2 = bytes_to_int(reply, 78)
        
        # Make Blowfish cipher from key
        bf = _RoverBlowfish(key)
        
        # Encrypt inputs from reply
        L1,R1 = bf.encrypt(L1, R1)
        L2,R2 = bf.encrypt(L2, R2)
        
        # Send encrypted reply to Rover
        self._sendCommandIntRequest(2, [L1, R1, L2, R2])     
        
        # Ignore reply from Rover
        self._receiveCommandReply(26)
        
        # Start timer task for keep-alive message every 60 seconds
        self._startKeepaliveTask()
        
        # Set up treads
        self.leftTread = _RoverTread(self, 4)
        self.rightTread = _RoverTread(self, 1)
        
        # Set up camera position
        self.cameraIsMoving = False
                      
        # Send video-start request
        self._sendCommandIntRequest(4, [1])       
        
        # Get reply from Rover
        reply = self._receiveCommandReply(29)
                
        # Create media socket connection to Rover      
        self.mediasock = self._newSocket()

        # Send video-start request based on last four bytes of reply
        self._sendRequest(self.mediasock, 'V', 0, 4, map(ord, reply[25:]))
        
        # Send audio-start request
        self._sendCommandByteRequest(8, [1])
        
        # Ignore audio-start reply
        self._receiveCommandReply(25)
        
        # Receive images on another thread until closed
        self.is_active = True
        self.reader_thread = _MediaThread(self)
        self.reader_thread.start()
            
        
    def close(self):
        ''' Closes off commuincation with Rover.
        '''
        
        # Stop moving treads
        self.setTreads(0, 0)
                
        self.keepalive_timer.cancel()
        
        self.is_active = False
        self.commandsock.close()
        
        if self.mediasock:
            self.mediasock.close()
        
        
    def getBatteryPercentage(self):
        ''' Returns percentage of battery remaining.
        '''
        self._sendCommandByteRequest(251)
        reply = self._receiveCommandReply(32)
        return 15 * ord(reply[23])    
        
    def moveCamera(self, where):
        ''' Moves the camera up or down, or stops moving it.  A nonzero value for the 
            where parameter causes the camera to move up (+) or down (-).  A
            zero value stops the camera from moving.
        '''
        if where == 0:
            if self.cameraIsMoving: