#!/usr/bin/env python3

# Copyright (c) 2013 Quanta Research Cambridge, Inc.

# 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.

from __future__ import print_function

import argparse
import os
import socket
import subprocess
import sys
import discover_tcp

scriptdir=os.path.dirname(sys.argv[0])
sys.path.append(scriptdir)

from adb import adb_commands
from adb import common
from argparse import RawTextHelpFormatter

timelimit=600
if 'RUNTIMELIMIT' in os.environ:
    timelimit = int(os.environ['RUNTIMELIMIT'])
buildbot_build=None
if 'BUILDBOT_BUILD_NUMBER' in os.environ:
    buildbot_build = os.environ['BUILDBOT_BUILD_NUMBER']

if 'RUNPARAM' in os.environ:
    ipaddr = os.environ['RUNPARAM']
else:
    ipaddr = None

buildbot_url='http://connectalbuild.qrclab.com/archive/'
if 'BUILDBOT_URL' in os.environ:
    buildbot_url=os.environ['BUILDBOT_URL']

epilog = '''
Will download the bit file and executable from buildbot if
buildbot-url, project, and build-number are specified.

The buildbot-url defaults to %(buildbot_url)s.

Will download the bit file and executable from an arbitrary location 
using rsync if rsync-path is specified. If you require a non-default 
identity file for rsync, add the  following lines to ~/.ssh/config:

   Host <hostname>
   HostName <url>
   User <username>
   Port 22
   IdentityFile <path_to_key>

---
''' % {
    'buildbot_url': buildbot_url
}

argparser = argparse.ArgumentParser("Run Connectal apps on Android Zynq boards.",
                                    epilog=epilog, formatter_class=RawTextHelpFormatter)
argparser.add_argument('androidexe', help='Android executable for the Zynq', nargs='?')
argparser.add_argument('push', help='Additional files to push to the target before execution', nargs='*', default=[])
argparser.add_argument('--pull', help='Additional files to pull from the target after execution', nargs='*', default=[])
argparser.add_argument('-t', '--timelimit', type=int, default=timelimit, help='Time limit for jobs running on the zedboard. Defaults to value of environment variable RUNTIMELIMIT or 600 seconds.')
argparser.add_argument('-a', '--ipaddr', default=ipaddr, help='IP address of target board')
argparser.add_argument('-u', '--buildbot-url', default=buildbot_url, help='Base URL of buildbot.')
argparser.add_argument('-p', '--project', help='Name of project on buildbot.')
argparser.add_argument('-b', '--build-number', help='Build number on buildbot.')
argparser.add_argument('-n', '--board-name', help='String in \'hostname\' on the SDCard')
argparser.add_argument('-r', '--rsync-path', help='rsync path to android.exe (hostname:/home/.../zedboard/bin/)')


def run_android(androidexe, pushFiles, pullFiles=[]):
    ipaddr = options.ipaddr

    if ipaddr:
        if ipaddr.find(':') == -1:
            ipaddr = ipaddr + ':5555'
    else:
        if (options.board_name):
            found = False
            discover_tcp.detect_network()
            for ip,name in discover_tcp.zedboards:
                if options.board_name == name:
                    ipaddr = ip
                    found = True
                    print("found %s at %s" %(name,ip))
            if not found:
                print("unable to find %s on your subnet" % name)
        else:
            p = subprocess.Popen('checkip')
            ipaddr = p.stdout.read()
            ipaddr = ipaddr.replace('\n', '')
            ipaddr = ipaddr.replace('\r', '')
            ipaddr = ipaddr + ':5555'

    device_serial = ipaddr

    print('connecting to %s' % device_serial)
    connected = False
    while not connected:
        try:
            connection = adb_commands.AdbCommands.ConnectDevice(serial=device_serial)
            connected = True
        except socket.error:
            #print('socket.error', sys.exc_info())
            pass

    print('Reconnecting')
    connected = False
    while not connected:
        try:
            connection = adb_commands.AdbCommands.ConnectDevice(serial=device_serial)
            connected = True
        except socket.error:
            #print('socket.error', sys.exc_info())
            pass

    params = { 'time': options.timelimit,
               'env': '',
               'exe': os.path.basename(androidexe),
               'args': ' '.join([os.path.basename(f) for f in pushFiles]),
             }
    nofpgajtag = os.environ.get('NOFPGAJTAG')
    if nofpgajtag != None:
         params['env'] += 'NOFPGAJTAG= '
    print('Sending files to the zedboard')
    connection.Push(androidexe, '/mnt/sdcard/tmp/%(exe)s' % params)
    connection.Shell('chmod agu+rx /mnt/sdcard/tmp/%(exe)s' % params)
    for f in pushFiles:
        print('Pushing file %s' % f)
        connection.Push(f, '/mnt/sdcard/tmp/%s' % os.path.basename(f))
    connection.Shell("touch /mnt/sdcard/tmp/perf.monkit")
    print('Running %(exe)s with timelimit %(time)d' % params)
    cmd = ("cd /mnt/sdcard/tmp/; rm -f /mnt/sdcard/tmp/exit.status; %(env)s /mnt/sdcard/timelimit -t %(time)d ./%(exe)s %(args)s; echo $? > /mnt/sdcard/tmp/exit.status"
           % params)
    for line in connection.StreamingShell(cmd):
        sys.stdout.write(line)
    connection.Pull('/mnt/sdcard/tmp/exit.status', 'exit.status')
    connection.Pull('/mnt/sdcard/tmp/perf.monkit', 'perf.monkit')
    for f in pullFiles:
        print('pulling %s' % f)
        connection.Pull('/mnt/sdcard/tmp/%s' % f, f)
    connection.Shell('rm -vf %s' % ' '.join([os.path.basename(f) for f in [androidexe] + pushFiles]))
    status = int(open('exit.status').read())
    print('status=%d' % status)
    sys.exit(status)

if __name__ == '__main__':
    options = argparser.parse_args()
    if options.buildbot_url and options.project and options.build_number:
        # download android.exe
        options.androidexe = 'android.exe'
        url = '%s/%s/%s/bin/android.exe' % (options.buildbot_url, options.project, options.build_number)
        print('downloading', url)
        status = subprocess.call(['curl', '-f', '-O', url])
        if status != 0:
            #print 'curl returned error', status
            sys.exit(status)
    elif options.rsync_path:
        options.androidexe = 'android.exe'
        status = subprocess.call(['rsync', '-av', '%s/android.exe' % (options.rsync_path), '.'])
        if status != 0:
            sys.exit(status)
    if not options.androidexe:
        argparser.print_help()
        sys.exit(1)
    run_android(options.androidexe, options.push, options.pull)
