#!/usr/bin/env python
############################################################################
#
#   Copyright (C) 2013-2014 PX4 Development Team. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in
#    the documentation and/or other materials provided with the
#    distribution.
# 3. Neither the name PX4 nor the names of its contributors may be
#    used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
############################################################################

"""Fetch files via nsh console

Usage: python fetch_file.py [-l] [-f] [-d device] [-s speed] [-o out_path] path
\t-l\tList files
\t-f\tOverwrite existing files
\t-d\tSerial device
\t-s\tSerial baudrate
\t-o\tOutput path
\tpath\tPath to list/fetch, if ends with "/" then directory will be fetched recursively"""

__author__  = "Anton Babushkin"
__version__ = "1.1"

import serial, time, sys, os

def _wait_for_string(ser, s, timeout):
    t0 = time.time()
    buf = []
    res = []
    while (True):
        c = ser.read()
        buf.append(c)
        if len(buf) > len(s):
            res.append(buf.pop(0))
        if "".join(buf) == s:
            break
        if timeout > 0.0 and time.time() - t0 > timeout:
            raise Exception("Timeout while waiting for: " + s)
    return "".join(res)

def _exec_cmd(ser, cmd, timeout):
    ser.write(cmd + "\n")
    ser.flush()
    _wait_for_string(ser, cmd + "\r\n", timeout)
    return _wait_for_string(ser, "nsh> \x1b[K", timeout)

def _ls_dir_raw(ser, dir, timeout):
    return _exec_cmd(ser, "ls -l " + dir, timeout)

def _ls_dir(ser, dir, timeout):
    res = []
    for line in _ls_dir_raw(ser, dir, timeout).splitlines():
        if line == dir + ":":
            continue
        if line.startswith("nsh: ls: no such directory:"):
            raise Exception("No such file: " + dir)
        res.append((line[20:], int(line[11:19].strip()), line[1] == "d"))
    return res

def _get_file(ser, fn, fn_out, force, timeout):
    print "Get %s:" % fn,
    if not force:
        # Check if file already exists with the same size
        try:
            os.stat(fn_out)
        except:
            pass
        else:
            print "already fetched, skip"
            return

    cmd = "dumpfile " + fn
    ser.write(cmd + "\n")
    ser.flush()
    _wait_for_string(ser, cmd + "\r\n", timeout)
    res = _wait_for_string(ser, "\n", timeout)
    if res.startswith("OK"):
        # Got correct responce, open temp file
        fn_out_part = fn_out + ".part"
        fout = open(fn_out_part, "wb")

        size = int(res.split()[1])
        sys.stdout.write(" [%i bytes] " % size)
        n = 0
        while (n < size):
            buf = ser.read(min(size - n, 8192))
            n += len(buf)
            sys.stdout.write(".")
            sys.stdout.flush()
            fout.write(buf)
        print " done"
        fout.close()
        os.rename(fn_out_part, fn_out)
    else:
        raise Exception("Error reading file")
    _wait_for_string(ser, "nsh> \x1b[K", timeout)

def _get_files_in_dir(ser, path, path_out, force, timeout):
    try:
        os.mkdir(path_out)
    except:
        pass
    for fn in _ls_dir(ser, path, timeout):
        path_fn = os.path.join(path, fn[0])
        path_fn_out = os.path.join(path_out, fn[0])
        if fn[2]:
            _get_files_in_dir(ser, path_fn[:-1], path_fn_out[:-1], force, timeout)
        else:
            _get_file(ser, path_fn, path_fn_out, force, timeout)

def _usage():
    print """Usage: python fetch_file.py [-l] [-f] [-d device] [-s speed] [-o out_path] path
\t-l\tList files
\t-f\tOverwrite existing files
\t-d\tSerial device
\t-s\tSerial baudrate
\t-o\tOutput path
\tpath\tPath to list/fetch, if ends with "/" then directory will be fetched recursively"""

def _main():
    dev = "/dev/tty.usbmodem1"
    speed = "57600"
    cmd = "get"
    path = None
    path_out = None
    force = False
    
    opt = None
    for arg in sys.argv[1:]:
        if opt != None:
            if opt == "d":
                dev = arg
            elif opt == "s":
                speed = arg
            elif opt == "o":
                path_out = arg
            opt = None
        else:
            if arg == "-l":
                cmd = "ls"
            elif arg == "-f":
                force = True
            elif arg == "-d":
                opt = "d"
            elif arg == "-s":
                opt = "s"
            elif arg == "-o":
                opt = "o"
            elif path == None:
                path = arg

    if path == None:
        _usage()
        exit(0)

    # Connect to serial port
    ser = serial.Serial(dev, speed, timeout=0.2)

    timeout = 1.0

    try:
        if cmd == "ls":
            # List directory
            print _ls_dir_raw(ser, path, timeout)
        elif cmd == "get":
            # Get file(s)
            if path.endswith("/"):
                # Get all files from directory recursively
                if path_out == None:
                    path_out = os.path.split(path[:-1])[1]
                _get_files_in_dir(ser, path[:-1], path_out, force, timeout)
            else:
                # Get one file
                if path_out == None:
                    path_out = os.path.split(path)[1]
                _get_file(ser, path, os.path.split(path)[1], force, timeout)
    except Exception as e:
        print e
    
    ser.close()

if __name__ == "__main__":
    _main()
