#!/usr/bin/env python
# Copyright (C) 2011-2015 Swift Navigation Inc.
# Contact: Fergus Noble <fergus@swift-nav.com>
#
# This source is subject to the license found in the file 'LICENSE' which must
# be be distributed together with this source. All other rights reserved.
#
# THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
# EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.

"""
The :mod:`piksi_tools.serial_link` module contains functions related to
setting up and running SBP message handling.
"""

import sys
import time

from sbp.piksi                          import SBP_MSG_PRINT, SBP_MSG_RESET
from sbp.client.drivers.file_driver     import FileDriver
from sbp.client.drivers.pyserial_driver import PySerialDriver
from sbp.client.drivers.pyftdi_driver   import PyFTDIDriver
from sbp.client.loggers.byte_logger     import ByteLogger
from sbp.client.loggers.json_logger     import JSONLogger
from sbp.client.loggers.null_logger     import NullLogger
from sbp.client.loggers.pickle_logger   import PickleLogger
from sbp.client.handler                 import Handler

LOG_FILENAME = time.strftime("serial-link-%Y%m%d-%H%M%S.log")

SERIAL_PORT  = "/dev/ttyUSB0"
SERIAL_BAUD  = 1000000

def get_ports():
  """
  Get list of serial ports.
  """
  import serial.tools.list_ports
  return [p for p in serial.tools.list_ports.comports() if p[1][0:4] != "ttyS"]

def get_args():
  """
  Get and parse arguments.
  """
  import argparse
  parser = argparse.ArgumentParser(description="Swift Navigation SBP Client.")
  parser.add_argument("-p", "--port",
                      default=[SERIAL_PORT], nargs=1,
                      help="specify the serial port to use.")
  parser.add_argument("-b", "--baud",
                      default=[SERIAL_BAUD], nargs=1,
                      help="specify the baud rate to use.")
  parser.add_argument("-v", "--verbose",
                      action="store_true",
                      help="print extra debugging information.")
  parser.add_argument("-f", "--ftdi",
                      action="store_true",
                      help="use pylibftdi instead of pyserial.")
  parser.add_argument("-l", "--log",
                      action="store_true",
                      help="serialize SBP messages to autogenerated log file.")
  parser.add_argument("-j", "--json",
                      action="store_true",
                      help="JSON serialize SBP messages.")
  parser.add_argument("-t", "--timeout",
                      default=[None], nargs=1,
                      help="exit after TIMEOUT seconds have elapsed.")
  parser.add_argument("-r", "--reset",
                      action="store_true",
                      help="reset device after connection.")
  parser.add_argument("-i", "--input-filename",
                      default=[None], nargs=1,
                      help="use input file to read SBP messages from.")
  parser.add_argument("-o", "--log-filename",
                      default=[LOG_FILENAME], nargs=1,
                      help="file to log output to.")
  parser.add_argument("-s", "--byte",
                      action="store_true",
                      help="Serialize SBP messages to bytes.")
  return parser.parse_args()

def get_driver(use_ftdi, port, baud, filename=None):
  """
  Get a driver based on configuration options

  Parameters
  ----------
  use_ftdi : bool
    For serial driver, use the pyftdi driver, otherwise use the pyserial driver.
  port : string
    Serial port to read.
  baud : int
    Serial port baud rate to set.
  filename : string
    File to read SBP messages from.
  """
  if filename:
    return FileDriver(filename)
  if use_ftdi:
    return PyFTDIDriver(baud)
  return PySerialDriver(port, baud)

def get_logger(use_log, use_json, use_byte, filename):
  """
  Get a logger based on configuration options.

  Parameters
  ----------
  use_log : bool
    Whether to log or not.
  use_json : bool
    Log out JSON records, otherwise log out bytes or pickled records.
  use_byte : bool
    Log out bytes, otherwise log out pickled records.
  filename : string
    File to log to.
  """
  if not use_log:
    return NullLogger()
  print "Logging at %s." % filename
  if use_json:
    return JSONLogger(filename)
  if use_byte:
    return ByteLogger(filename)
  return PickleLogger(filename)

def printer(sbp_msg):
  """
  Default print callback

  Parameters
  ----------
  sbp_msg: SBP
    SBP Message to print out.
  """
  sys.stdout.write(sbp_msg.payload)

def main():
  """
  Get configuration, get driver, get logger, and build handler and start it.
  """
  args = get_args()
  port = args.port[0]
  baud = args.baud[0]
  verbose = args.verbose
  use_ftdi = args.ftdi
  use_log = args.log
  use_json = args.json
  use_byte = args.byte
  timeout = args.timeout[0]
  reset = args.reset
  input_filename = args.input_filename[0]
  log_filename = args.log_filename[0]
  # Driver with context
  with get_driver(use_ftdi, port, baud, input_filename) as driver:
    # Handler with context
    with Handler(driver.read, driver.write, verbose) as link:
      # Logger with context
      with get_logger(use_log, use_json, use_byte, log_filename) as logger:
        link.add_callback(printer, SBP_MSG_PRINT)
        link.add_callback(logger)
        link.start()
        if reset:
          link.send(SBP_MSG_RESET, "")
        try:
          if timeout is None:
            while True:
              time.sleep(0.1)
          else:
            time.sleep(float(timeout))
        except KeyboardInterrupt:
          pass

if __name__ == "__main__":
  main()

