#!/usr/bin/env python3
# -*- mode: python; coding: utf-8 -*-
"""
Retrieve information from VOC

Usage:
  voc (-h | --help)
  voc --version
  voc [-v|-vv] [options] list
  voc [-v|-vv] [options] status
  voc [-v|-vv] [options] trips
  voc [-v|-vv] [options] owntracks
  voc [-v|-vv] [options] print [<attribute>]
  voc [-v|-vv] [options] (lock | unlock)
  voc [-v|-vv] [options] heater (start | stop)
  voc [-v|-vv] [options] call <method>

Options:
  -u <username>         VOC username
  -p <password>         VOC password
  -r <region>           VOC region (na, cn, etc.)
  -s <url>              VOC service URL
  -i <vin>              Vehicle VIN or registration number
  -g                    Geolocate position
  --owntracks_key=<key> Owntracks encryption password
  -h --help             Show this message
  -v,-vv                Increase verbosity
  --version             Show version
"""

import docopt
import logging
from datetime import datetime
from time import time
from json import dumps as to_json
from base64 import b64encode
from volvooncall import __version__, read_credentials, Connection

_LOGGER = logging.getLogger(__name__)

def lookup_position(lat, lon):
    try:
        from geopy.geocoders import Nominatim
        geolocator = Nominatim()
        return geolocator.reverse((lat, lon))
    except ImportError:
        _LOGGER.info('geopy not installed. position lookup not available')

def print_vehicle(vehicle, geolocate=False):
    s = '%s %dkm' % (
        vehicle,
        vehicle.odometer / 1000)

    if vehicle.fuel_amount_level:
        s += ' (fuel %s%% %skm)' % (
            vehicle.fuel_amount_level,
            vehicle.distance_to_empty)

    print(s)
    try:
        lat, lon = (vehicle.position['latitude'],
                    vehicle.position['longitude'])
        pos = lookup_position(lat, lon) if geolocate else None
        if pos:
            print('    position: %.14f,%.14f (%s)' % (lat, lon, pos.address))
        else:
            print('    position: %.14f,%.14f' % (lat, lon))
    except AttributeError:
        pass

    print('    locked: %s' % ('yes' if vehicle.is_locked else 'no'))
    print('    heater: %s' % ('on' if vehicle.is_heater_on else 'off'))


def encrypt(msg, key):
    try:
        import libnacl
    except ImportError:
        exit('libnacl missing')
    except OSError:
        exit('libsodium missing')
    from libnacl import crypto_secretbox_KEYBYTES as keylen
    from libnacl.secret import SecretBox as secret
    key = key.encode('utf-8')
    key = key[:keylen]
    key = key.ljust(keylen, b'\0')
    msg = msg.encode('utf-8')
    ciphertext = secret(key).encrypt(msg)
    ciphertext = b64encode(ciphertext)
    ciphertext = ciphertext.decode('ascii')
    return ciphertext

def main():
    """Command line interface."""
    args = docopt.docopt(__doc__,
                         version=__version__)

    if args['-v'] == 2:
        level=logging.DEBUG
    elif args['-v']:
        level=logging.INFO
    else:
        level=logging.ERROR

    FORMAT = '%(asctime)s %(name)s: %(message)s'
    logging.basicConfig(level=level, format=FORMAT, datefmt='%H:%M:%S')

    credentials = read_credentials()

    if args['-u'] and args['-p']:
        credentials.update(username=args['-u'],
                           password=args['-p'])
    if args['-r']:
        credentials.update(region = args['-r'])
    if args['-s']:
        credentials.update(service_url = args['-s'])

    try:
        connection = Connection(**credentials)
    except TypeError:
        exit('Could not read credentials and none supplied.')

    if not connection.update():
        exit('Could not connect to the server.')

    if args['list']:
        for vehicle in connection.vehicles:
            print(vehicle)
            exit()

    if args['-i']:
        vehicle = connection.vehicle(args['-i'])
    else:
        vehicle = next(connection.vehicles, None)

    if not vehicle:
        exit('Vehicle not found')

    if args['status']:
        print_vehicle(vehicle, args['-g'])
    elif args['trips']:
        print(to_json(vehicle.trips, indent=4))
    elif args['print']:
        attr = args['<attribute>']
        if attr:
            if not attr in vehicle.data:
                exit('unknown attribute')
            print(vehicle.data[attr])
        else:
            print(vehicle.json)
    elif args['owntracks']:
        def serialize(obj):
            if isinstance(obj, datetime):
                return obj.isoformat()
        msg = to_json(
            dict(_type='location',
                 tid='volvo',
                 t='p',
                 lat=vehicle.position['latitude'],
                 lon=vehicle.position['longitude'],
                 acc=1,
                 tst=int(time()),
                 _vehicle=vehicle.data),
            indent=4, default=serialize)
        key = args['--owntracks_key'] or credentials.get('owntracks_key')
        if key:
            msg = to_json(
                dict(_type='encrypted',
                     data=encrypt(msg, key)))
        print(msg)
    elif args['heater']:
        if args['start']:
            vehicle.start_heater()
        else:
            vehicle.stop_heater()
    elif args['lock']:
        vehicle.lock()
    elif args['unlock']:
        vehicle.unlock()
    elif args['call']:
        vehicle.call(args['<method>'])


if __name__ == '__main__':
   main()
