#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright (C) 2013-2022  Diego Torres Milano
Created on Mar 28, 2013

Culebra helps you create AndroidViewClient scripts generating a working template that can be
modified to suit more specific needs.
                      __    __    __    __
                     /  \  /  \  /  \  /  \
____________________/  __\/  __\/  __\/  __\_____________________________
___________________/  /__/  /__/  /__/  /________________________________
                   | / \   / \   / \   / \   \___
                   |/   \_/   \_/   \_/   \    o \
                                           \_____/--<

@author: Diego Torres Milano
@author: Jennifer E. Swofford (ascii art snake)


"""

from __future__ import print_function

import culebratester_client
from culebratester_client import WindowHierarchy

__version__ = '21.12.0'

import calendar
import codecs
import getopt
import os
import re
import sys
import warnings
from datetime import date

try:
    sys.path.insert(0, os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src'))
except:
    pass

from com.dtmilano.android.viewclient import ViewClient, ViewClientOptions, View, CulebraOptions
from com.dtmilano.android.culebron import Culebron, Operation, Unit
from com.dtmilano.android.concertina import Concertina
from com.dtmilano.android.common import debugArgsToDict

DEBUG = False
USAGE = 'usage: %s [OPTION]... [serialno]'
TAG = 'CULEBRA'


class Descriptor:
    CONTENT_DESCRIPTION = 'content-description'
    TEXT = 'text'
    ID = 'id'

    @staticmethod
    def findBestDescriptor(view):
        '''
        Finds the best possible descriptor for the View
        '''

        cd = view.getContentDescription()
        if cd and options[CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION]:
            return Descriptor.CONTENT_DESCRIPTION
        else:
            t = view.getText()
            if t and options[CulebraOptions.FIND_VIEWS_WITH_TEXT]:
                return Descriptor.TEXT
        return Descriptor.ID


def fillAutoRegexpsRes():
    are = {'clock': re.compile('[012]?\d\\\\:[0-5]\d')}
    d = "("
    for i in range(7):
        d += calendar.day_abbr[i]
        if i != 6:
            d += '|'
    d += '), ('
    for i in range(1, 13):
        d += calendar.month_name[i]
        if i != 12:
            d += '|'
    d += ') [0123]\d'
    are['date'] = re.compile(d, re.IGNORECASE)
    are['battery'] = re.compile('Charging, \d\d%')
    return are


CulebraOptions.AUTO_REGEXPS_RES = fillAutoRegexpsRes()
SB_NO_JAR = 'no-jar'
SB_JAR = 'jar'
SB_JAR_LINUX = 'jar-linux'
SHEBANG = {
    SB_NO_JAR: '#! /usr/bin/env python3',
    SB_JAR: '#! /usr/bin/env shebang monkeyrunner -plugin $ANDROID_VIEW_CLIENT_HOME/bin/androidviewclient-$ANDROID_VIEW_CLIENT_VERSION.jar @!',
    SB_JAR_LINUX: '#! /usr/local/bin/shebang monkeyrunner -plugin $AVC_HOME/bin/androidviewclient-$AVC_VERSION.jar @!'
}

indent = ''
prefix = ''


def shortAndLongOptions():
    '''
    @return: the list of corresponding (short-option, long-option) tuples
    '''

    short_opts = CulebraOptions.SHORT_OPTS.replace(':', '')
    if len(short_opts) != len(CulebraOptions.LONG_OPTS):
        _s = ""
        for i in range(max(len(short_opts), len(CulebraOptions.LONG_OPTS))):
            l = ''
            try:
                l = short_opts[i]
            except IndexError:
                pass
            L = ''
            try:
                L = CulebraOptions.LONG_OPTS[i]
            except IndexError:
                pass
            _s += "%3s - %s\n" % (l, L)
        raise Exception('There is a mismatch between short and long options: short=%d, long=%d\n%s' %
                        (len(short_opts), len(CulebraOptions.LONG_OPTS), _s))
    t = tuple(short_opts) + tuple(CulebraOptions.LONG_OPTS)
    l2 = int(len(t) / 2)
    sl = []
    for i in range(l2):
        sl.append((t[i], t[i + l2]))
    return sl


def usage(exitVal=1):
    print(USAGE % progname, file=sys.stderr)
    print("Try '%s --help' for more information." % progname, file=sys.stderr)
    sys.exit(exitVal)


def _help():
    print(USAGE % progname, file=sys.stderr)
    print(file=sys.stderr)
    print("Options:", file=sys.stderr)
    for so, lo in shortAndLongOptions():
        o = '  -%c, --%s' % (so, lo)
        if lo[-1] == '=':
            o += CulebraOptions.LONG_OPTS_ARG[lo[:-1]]
        try:
            o = '%-34s %-45s' % (o, CulebraOptions.OPTS_HELP[so])
        except:
            pass
        print(o, file=sys.stderr)
    sys.exit(0)


def version():
    print(progname, __version__)
    sys.exit(0)


def autoRegexpsHelp():
    print("Available %s options:" % CulebraOptions.AUTO_REGEXPS, file=sys.stderr)
    print("\thelp: prints this help", file=sys.stderr)
    print("\tall: includes all the available regexps", file=sys.stderr)
    for r in CulebraOptions.AUTO_REGEXPS_RES:
        print("\t%s: %s" % (r, CulebraOptions.AUTO_REGEXPS_RES[r].pattern), file=sys.stderr)
    print(file=sys.stderr)
    sys.exit(0)


def concertinaConfigHelp():
    print("Default Concertina configuration:", file=sys.stderr)
    print(Concertina.getConcertinaConfigDefault(), file=sys.stderr)
    sys.exit(0)


def error(msg, fatal=False):
    print("%s: ERROR: %s" % (progname, msg), file=sys.stderr)
    if fatal:
        sys.exit(1)


def notNull(val, default):
    if val:
        return val
    return default


def printVerboseComments(view):
    """
    Prints the verbose comments for view.
    """

    print('\n%s# class=%s' % (indent, view.getClass()), end=' ')
    try:
        text = view.getText()
        if text:
            u = 'u' if isinstance(text, str) else ''
            if '\n' in text:
                text = re.sub(r'\n(.)', r'\n#\1', text)
            print(" text=%c'%s'" % (u, text), end=' ')
    except:
        pass
    try:
        contentDescription = view.getContentDescription()
        if contentDescription:
            print(" cd='%s'" % contentDescription, end=' ')
    except:
        pass
    try:
        tag = view.getTag()
        if tag and tag != 'null':
            print(' tag=%s' % tag, end=' ')
    except:
        pass
    print()


def variableNameFromIdOrKey(view):
    '''
    Returns a suitable variable name from the id.

    @type view: L{View}
    @param id: the View from where the I{uniqueId} is obtained

    @return: the variable name from the id
    '''

    var = View.variableNameFromId(view)
    if options[CulebraOptions.USE_DICTIONARY]:
        return '%sviews[\'%s\']' % (
            prefix, notNull(dictionaryKeyFrom(options[CulebraOptions.DICTIONARY_KEYS_FROM], view), var))
    else:
        return var


def dictionaryKeyFrom(key, view):
    if key == 'id':
        return view.getUniqueId()
    elif key == 'text':
        return view.getText()
    elif key == 'content-description':
        return view.getContentDescription()
    else:
        raise Exception('Not a valid dictionary key: %s' % key)


def escapeRegexpSpecialChars(text):
    return re.escape(text)


def printFindViewWithText(view, useregexp, op=Operation.ASSIGN, arg=None):
    '''
    Prints the corresponding statement.

    @type view: L{View}
    @param view: the View
    '''

    if isinstance(view, View):
        text = view.getText()
    else:
        text = view.text

    isUnicode = isinstance(text, str)
    if isUnicode and sys.stdout.encoding is None:
        warnings.warn('''\
You are trying to print unicode characters to an unencoded stdout, it will probably fail.
You have to set PYTHONIOENCODING environment variable. For example:
       export PYTHONIOENCODING=utf-8
        ''')

    u = 'u' if isUnicode else ''
    if text:
        var = variableNameFromIdOrKey(view)
        if text.find("\n") > 0 or text.find("'") > 0:
            # 2 quotes + 1 quote = 3 quotes
            text = "''%s''" % text
        if useregexp:
            # if there are special chars already in the text escape them
            text = escapeRegexpSpecialChars(text)
            if options[CulebraOptions.AUTO_REGEXPS]:
                for r in options[CulebraOptions.AUTO_REGEXPS]:
                    autoRegexp = CulebraOptions.AUTO_REGEXPS_RES[r]
                    if autoRegexp.match(text):
                        text = autoRegexp.pattern
                        break
            text = "re.compile(%s'%s')" % (u, text)
        else:
            text = "%s'%s'" % (u, text)

        if op == Operation.ASSIGN:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                logAction('finding view with text=%s' % text)
                print('%s%s = %svc.findViewWithTextOrRaise(%s)' % (indent, var, prefix, text))
        elif op == Operation.LONG_TOUCH_VIEW:
            root = ', root=%svc.findViewByIdOrRaise(\'%s\')' % (prefix, arg.getUniqueId()) if arg else ''
            if options[CulebraOptions.MULTI_DEVICE]:
                logAction('long touch view with text=%s on ${serialno}' % text)
                print('%s[_vc.findViewWithTextOrRaise(%s%s).longTouch() for _vc in %sallVcs()]' % (
                    indent, text, root, prefix))
            else:
                logAction('long touch view with text=%s' % text)
                print('%s%svc.findViewWithTextOrRaise(%s%s).longTouch()' % (indent, prefix, text, root))
        elif op == Operation.TOUCH_VIEW:
            root = ', root=%svc.findViewByIdOrRaise(\'%s\')' % (prefix, arg.getUniqueId()) if arg else ''
            if options[CulebraOptions.MULTI_DEVICE]:
                logAction('touching view with text=%s on ${serialno}' % text)
                print('%s[_vc.findViewWithTextOrRaise(%s%s).touch() for _vc in %sallVcs()]' % (
                    indent, text, root, prefix))
            else:
                logAction('touching view with text=%s' % text)
                print('%s%svc.findViewWithTextOrRaise(%s%s).touch()' % (indent, prefix, text, root))
        elif op == Operation.TYPE:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                print('%s%svc.findViewWithTextOrRaise("%s").type(u"%s")' % (indent, prefix, text, arg))
        elif op == Operation.SET_TEXT:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                print('%s%svc.findViewWithTextOrRaise(%s).setText(u"%s")' % (indent, prefix, text, arg))
        elif op in [Operation.FLING_BACKWARD, Operation.FLING_FORWARD, Operation.FLING_TO_BEGINNING,
                    Operation.FLING_TO_END]:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                commandName = Operation.toCommandName(op)
                logAction('flinging view with text=%s %s' % (text, commandName))
                print('%s%svc.findViewWithTextOrRaise(%s).uiScrollable.%s()' % (indent, prefix, text, commandName))
        elif op == Operation.TEST:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                print('%s%sassertIsNotNone(%svc.findViewWithText(%s))' % (indent, prefix, prefix, text))
    elif kwargs1[CulebraOptions.VERBOSE]:
        warnings.warn('View with id=%s has no text' % view.getUniqueId())


def printFindViewWithContentDescription(view, useregexp, op=Operation.ASSIGN, arg=None):
    '''
    Prints the corresponding statement.

    @type view: L{View}
    @param view: the View
    '''

    if isinstance(view, View):
        contentDescription = view.getContentDescription()
    else:
        contentDescription = view.content_description

    if contentDescription:
        var = variableNameFromIdOrKey(view)
        if useregexp:
            if options[CulebraOptions.AUTO_REGEXPS]:
                for r in options[CulebraOptions.AUTO_REGEXPS]:
                    autoRegexp = CulebraOptions.AUTO_REGEXPS_RES[r]
                    if autoRegexp.match(contentDescription):
                        contentDescription = autoRegexp.pattern
                        break
            contentDescription = "re.compile(u'''%s''')" % contentDescription
        else:
            contentDescription = "u'''%s'''" % contentDescription

        if op == Operation.ASSIGN:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                logAction('finding view with content-description=%s' % contentDescription)
                print('%s%s = %svc.findViewWithContentDescriptionOrRaise(%s)' % (
                    indent, var, prefix, contentDescription))
        elif op == Operation.LONG_TOUCH_VIEW:
            if options[CulebraOptions.MULTI_DEVICE]:
                logAction('long touch view with content-description=%s on ${serialno}' % contentDescription)
                # print u'%s[_vc.findViewWithTextOrRaise(%s).touch() for _vc in %sallVcs()]' % (indent, text, root)
                print('%s[_vc.findViewWithContentDescriptionOrRaise(%s).longTouch() for _vc in %sallVcs()]' % (
                    indent, contentDescription, prefix))
            else:
                logAction('long touch view with content-description=%s' % contentDescription)
                print('%s%svc.findViewWithContentDescriptionOrRaise(%s).longTouch()' % (
                    indent, prefix, contentDescription))
        elif op == Operation.TOUCH_VIEW:
            if options[CulebraOptions.MULTI_DEVICE]:
                logAction('touching view with content-description=%s on ${serialno}' % contentDescription)
                # print u'%s[_vc.findViewWithTextOrRaise(%s).touch() for _vc in %sallVcs()]' % (indent, text, root)
                print('%s[_vc.findViewWithContentDescriptionOrRaise(%s).touch() for _vc in %sallVcs()]' % (
                    indent, contentDescription, prefix))
            else:
                logAction('touching view with content-description=%s' % contentDescription)
                print('%s%svc.findViewWithContentDescriptionOrRaise(%s).touch()' % (indent, prefix, contentDescription))
        elif op == Operation.TYPE:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                print('%s%svc.findViewWithContentDescriptionOrRaise(%s).type(u"%s")' % (
                    indent, prefix, contentDescription, arg))
        elif op == Operation.SET_TEXT:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                print('%s%svc.findViewWithContentDescriptionOrRaise(%s).setText(u"%s")' % (
                    indent, prefix, contentDescription, arg))
        elif op == Operation.TEST:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                print('%s%sassertEquals(%svc.findViewWithContentDescriptionOrRaise(%s).getText(), u\'\'\'%s\'\'\')' % (
                    indent, prefix, prefix, contentDescription, arg))
        elif op in [Operation.FLING_BACKWARD, Operation.FLING_FORWARD, Operation.FLING_TO_BEGINNING,
                    Operation.FLING_TO_END]:
            if options[CulebraOptions.MULTI_DEVICE]:
                warnings.warn('Multi-device not implemented yet for this case')
            else:
                commandName = Operation.toCommandName(op)
                logAction('flinging view with content-description=%s %s' % (contentDescription, commandName))
                print('%s%svc.findViewWithContentDescriptionOrRaise(%s).uiScrollable.%s()' % (
                    indent, prefix, contentDescription, commandName))
        else:
            error("Invalid operation in %s: %s" % (sys._getframe().f_code.co_name), op)

    elif kwargs1[CulebraOptions.VERBOSE]:
        warnings.warn('View with id=%s has no content-description' % view.getUniqueId())


def printFindViewById(view, op=Operation.ASSIGN, arg=None):
    '''
    Prints the corresponding statement.

    @type view: L{View}
    @param view: the View
    '''

    if isinstance(view, View):
        _id = view.getId() or view.getUniqueId()
    else:
        _id = view.resource_id or view.unique_id

    var = variableNameFromIdOrKey(view)
    if op == Operation.ASSIGN:
        if options[CulebraOptions.MULTI_DEVICE]:
            logAction('finding view with id=%s on ${serialno}' % _id)
            print('%s%s = [_vc.findViewByIdOrRaise("%s") for _vc in %sallVcs()]' % (indent, var, _id, prefix))
        else:
            logAction('finding view with id=%s' % _id)
            print('%s%s = %svc.findViewByIdOrRaise("%s")' % (indent, var, prefix, _id))
    elif op == Operation.LONG_TOUCH_VIEW:
        if options[CulebraOptions.MULTI_DEVICE]:
            logAction('long touch view with id=%s on ${serialno}' % _id)
            print('%s[_vc.findViewByIdOrRaise("%s").longTouch() for _vc in %sallVcs()]' % (indent, _id, prefix))
        else:
            logAction('long touch view with id=%s' % _id)
            print('%s%svc.findViewByIdOrRaise("%s").longTouch()' % (indent, prefix, _id))
    elif op == Operation.TOUCH_VIEW:
        if options[CulebraOptions.MULTI_DEVICE]:
            logAction('touching view with id=%s on ${serialno}' % _id)
            print('%s[_vc.findViewByIdOrRaise("%s").touch() for _vc in %sallVcs()]' % (indent, _id, prefix))
        else:
            logAction('touching view with id=%s' % _id)
            print('%s%svc.findViewByIdOrRaise("%s").touch()' % (indent, prefix, _id))
    elif op == Operation.TYPE:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            logAction('typing "%s" on view with id=%s' % (arg, _id))
            print('%s%svc.findViewByIdOrRaise("%s").type(u"%s")' % (indent, prefix, _id, arg))
    elif op == Operation.SET_TEXT:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            logAction('setting text "%s" on view with id=%s' % (arg, _id))
            print('%s%svc.findViewByIdOrRaise("%s").setText(u"%s")' % (indent, prefix, _id, arg))
    elif op == Operation.TEST:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            print('%s%sassertEquals(%svc.findViewByIdOrRaise("%s").getText(), u\'\'\'%s\'\'\')' % (
                indent, prefix, prefix, _id, arg))
    elif op in [Operation.FLING_BACKWARD, Operation.FLING_FORWARD, Operation.FLING_TO_BEGINNING,
                Operation.FLING_TO_END]:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            commandName = Operation.toCommandName(op)
            logAction('flinging view with id=%s %s' % (_id, commandName))
            print('%s%svc.findViewWithIdOrRaise(u"%s").uiScrollable.%s()' % (indent, prefix, _id, commandName))
    else:
        error("Invalid operation in %s: %s" % (sys._getframe().f_code.co_name, op))


def printTraverse(dump=None):
    '''
    Prints the result of traversing the tree.
    A previously obtained dump can be passed as a parameter and in such case that
    tree is used.
    
    @param dump: Dump of Views previously obtained via L{ViewClient.dump()}
    @type dump: list
    '''

    print()
    if dump:
        for view in dump:
            transform(view)
    else:
        vc.traverse(transform=transform)
    print()


def printDump(window, dump=None):
    '''
    Prints a dump.
    
    @param window: The window id to use to print the dump
    @type window: int or str
    @param dump: Dump of Views previously obtained via L{ViewClient.dump()}
    @type dump: list
    '''

    print('🐸 culebra.printDump', file=sys.stderr)
    if options[CulebraOptions.MULTI_DEVICE]:
        logAction('dumping content of window=%s on ${serialno}' % window)
        print('%s[_vc.dump(window=%s) for _vc in %sallVcs()]' % (indent, window, prefix))
    else:
        logAction('dumping content of window=%s' % window)
        print('%s%svc.dump(window=%s)' % (indent, prefix, window))

    if not options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]:
        if DEBUG:
            print('printing dump: %s' % dump.__class__.__name__)
        printTraverse(dump)


def printSleep(secs):
    '''
    Prints a sleep.

    This method relies on shortcut variables being set (i.e. _s)
    '''

    if options[CulebraOptions.MULTI_DEVICE]:
        print('%s[_vc.sleep(%s) for _vc in %sallVcs()]' % (indent, secs if secs != Operation.DEFAULT else '_s', prefix))
    else:
        print('%s%svc.sleep(%s)' % (indent, prefix, secs if secs != Operation.DEFAULT else '_s'))


def printSleepUiAutomatorHelper(secs=5):
    """
    Prints a sleep.
    """

    if options[CulebraOptions.MULTI_DEVICE]:
        print(f'{indent}[_vc.sleep({secs}) for _vc in %sallVcs()]')
    else:
        print(f'{indent}time.sleep({secs})')


def printWake():
    '''
    Prints a wake.

    :return:
    '''

    if options[CulebraOptions.MULTI_DEVICE]:
        print('%s[_vc.device.wake() for _vc in %sallVcs()]' % (indent, prefix))
    else:
        print('%s%svc.device.wake()' % (indent, prefix))


def printSay(text):
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        print('%sViewClient.sayText("%s")' % (indent, text))


def printPress(keycode):
    '''
    Prints a key press
    '''

    if options[CulebraOptions.MULTI_DEVICE]:
        logAction('pressing key=%s on ${serialno}' % keycode)
        print('%s[_d.press(\'%s\') for _d in %sallDevices()]' % (indent, keycode, prefix))
    else:
        logAction('pressing key=%s' % keycode)
        print('%s%sdevice.press(\'%s\')' % (indent, prefix, keycode))
        # FIXME: we should support both cases: CulebraTester2 and adbclient
        # print('%s%shelper.ui_device.press_key_code(KEY_EVENT[\'%s\'])' % (indent, prefix, keycode))


def printDrag(start, end, duration, steps, unit, orientation):
    '''
    Prints a drag
    '''

    if unit == Unit.PX:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            print('%s%sdevice.drag(%s, %s, %d, %d, %d)' % (indent, prefix, start, end, duration, steps, orientation))
    elif unit == Unit.DIP:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            print('%s%sdevice.dragDip(%s, %s, %d, %d, %d)' % (indent, prefix, start, end, duration, steps, orientation))
    else:
        raise RuntimeError('Invalid unit: %s' % unit)


def printTouch(x, y, unit, orientation):
    '''
    Prints a touch
    '''

    if unit == Unit.PX:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            logAction('touching point by PX @ (%s, %s) orientation=%s' % (x, y, orientation))
            print('%s%sdevice.touch(%s, %s, %s)' % (indent, prefix, x, y, orientation))
    elif unit == Unit.DIP:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            logAction('touching point by DIP @ (%s, %s) orientation=%s' % (x, y, orientation))
            print('%s%sdevice.touchDip(%s, %s, %s)' % (indent, prefix, x, y, orientation))
    else:
        raise RuntimeError('Invalid unit: %s' % unit)


def printLongTouch(x, y, duration, unit, orientation):
    '''
    Prints a long touch
    '''

    if unit == Unit.PX:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            logAction(
                'long touching point by PX @ (%s, %s) duration=%s orientation=%s' % (x, y, duration, orientation))
            print('%s%sdevice.longTouch(%s, %s, %s, %s)' % (indent, prefix, x, y, duration, orientation))
    elif unit == Unit.DIP:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            logAction(
                'long touching point by DIP @ (%s, %s) duration=%s orientation=%s' % (x, y, duration, orientation))
            print('%s%sdevice.longTouch(%s, %s, %s, %s)' % (indent, prefix, x, y, duration, orientation))
    else:
        raise RuntimeError('Invalid unit: %s' % unit)


def printSaveViewScreenshot(view, filename, _format):
    '''
    Prints the writeImageToFile for the specified L{View}.
    
    @type view: L{View}
    @param view: the View
    @type filename: str
    @param filename: the filename to store the image
    @type _format: str
    @param _format: The image format (i.e. PNG)
    '''

    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        # FIXME: if -u was passed in the command line then we are not saving the variables and thus
        # next line will generate an error in the script as the variable is 'undefined'
        print('%s%s.writeImageToFile(\'%s\', \'%s\')' % (indent, View.variableNameFromId(view), filename, _format))


def printFlingBackward(view):
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        # FIXME: if -u was passed in the command line then we are not saving the variables and thus
        # next line will generate an error in the script as the variable is 'undefined'
        logAction('flinging backward view with id=%s' % view.getId())
        print('%s%s.uiScrollable.flingBackward()' % (indent, view.variableNameFromId()))


def printFlingForward(view):
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        # FIXME: if -u was passed in the command line then we are not saving the variables and thus
        # next line will generate an error in the script as the variable is 'undefined'
        logAction('flinging forward view with id=%s' % view.getId())
        print('%s%s.uiScrollable.flingForward()' % (indent, view.variableNameFromId()))


def printFlingToBeginning(view):
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        # FIXME: if -u was passed in the command line then we are not saving the variables and thus
        # next line will generate an error in the script as the variable is 'undefined'
        logAction('flinging to beginning view with id=%s' % view.getId())
        print('%s%s.uiScrollable.flingToBeginning()' % (indent, view.variableNameFromId()))


def printFlingToEnd(view):
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        # FIXME: if -u was passed in the command line then we are not saving the variables and thus
        # next line will generate an error in the script as the variable is 'undefined'
        logAction('flinging to end view with id=%s' % view.getId())
        print('%s%s.uiScrollable.flingToEnd()' % (indent, view.variableNameFromId()))


def printOpenNotification():
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction('opening Notification')
        print('%s%svc.uiDevice.openNotification()' % (indent, prefix))


def printOpenQuickSettings():
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction('opening Quick Settings')
        print('%s%svc.uiDevice.openQuickSettings()' % (indent, prefix))


def printChangeLanguage(code):
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction('Changing language to %s' % code)
        print('%s%svc.uiDevice.changeLanguage("%s")' % (indent, prefix, code))


def printTakeSnapshot(filename, _format, deviceart, dropshadow, screenglare):
    '''
    Prints the corresponding writeImageToFile() to take a snapshot
    '''

    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction('taking snapshot @ %s format=%s %s %s %s' % (filename, _format, deviceart, dropshadow, screenglare))
        if deviceart:
            deviceart = '\'%s\'' % deviceart
        print('%s%svc.writeImageToFile(\'%s\', \'%s\', %s, %s, %s)' % (
            indent, prefix, filename, _format, deviceart, dropshadow, screenglare))


def printTakeSnapshotUiAutomatorHelper(filename, _format='PNG'):
    '''
    Prints the corresponding take_screenshot() to take a snapshot
    '''

    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction(f'taking screenshot @ {filename} format={_format}')
        print(f'{indent}{prefix}helper.ui_device.take_screenshot(filename=\'{filename}\')')


def traverseAndPrint(view):
    '''
    Traverses the View tree and prints the corresponding statement.

    @type view: L{View}
    @param view: the View
    '''

    if DEBUG:
        print("🐞 culebra.traverseAndPrint(view=%s)" % view.id, file=sys.stderr)
    if type(view) == WindowHierarchy:
        # The root element is a WindowHierarchy (id=hierarchy)
        return
    if options[CulebraOptions.VERBOSE_COMMENTS]:
        printVerboseComments(view)
    if options[CulebraOptions.FIND_VIEWS_BY_ID]:
        printFindViewById(view)
    if options[CulebraOptions.FIND_VIEWS_WITH_TEXT]:
        printFindViewWithText(view, options[CulebraOptions.USE_REGEXPS])
    if options[CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION]:
        printFindViewWithContentDescription(view, options[CulebraOptions.USE_REGEXPS])
    if options[CulebraOptions.SAVE_VIEW_SCREENSHOTS]:
        _format = 'PNG'
        filename = options[
                       CulebraOptions.SAVE_VIEW_SCREENSHOTS] + os.sep + View.variableNameFromId(
            view) + '.' + _format.lower()
        printSaveViewScreenshot(view, filename, _format)


def printStartActivity(component):
    '''
    Prints the corresponding startActivity().

    :param component: the component
    '''

    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction('starting activity=%s' % (component))
        print('%s%sdevice.startActivity(\'%s\')' % (indent, prefix, component))
    printSleep(3)


def printStartActivityUiAutomatorHelper(component):
    """
    Prints the corresponding startActivity().

    :param component: the component
    """

    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction(f'starting activity={component}')
        pkg, cls = component.split('/')
        print(f'{indent}{prefix}helper.target_context.start_activity(\'{pkg}\', \'{cls}\')')


def printTouchViewUiAutomatorHelper(selector: culebratester_client.Selector) -> None:
    """
    Prints the corresponding touch
    """
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction(f'touching by selector={selector}')
        indented_line = None
        indentation = indent + ' ' * 4
        # FIXME: should be find_objects and before clicking we should verify the list contains
        # only 1 object, otherwise we aren't sure which one is being clicked
        for line in f'{indent}obj_ref = {prefix}helper.until.find_object(body={selector})'.splitlines():
            if not indented_line:
                indented_line = line
            else:
                indented_line = f'{indented_line}\n{indentation}{line}'
        print('')
        print(f'{indented_line}')
        print(f'{indent}obj_ref = {prefix}helper.ui_device.wait(oid=obj_ref.oid)')
        print(f'{indent}{prefix}helper.ui_object2.click(oid=obj_ref.oid)')


def printLongTouchViewUiAutomatorHelper(selector):
    """
    Prints the corresponding long touch
    """
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction(f'long-touching by selector={selector}')
        indented_line = None
        indentation = indent + ' ' * 4
        for line in f'{indent}obj_ref = {prefix}helper.ui_device.find_object(body={selector})'.splitlines():
            if not indented_line:
                indented_line = line
            else:
                indented_line = f'{indented_line}\n{indentation}{line}'
        print(f'{indented_line}')
        print(f'{indent}{prefix}helper.ui_object2.long_click(oid=obj_ref.oid)')


def printSwipeUiAutomatorHelper(startX, startY, endX, endY, steps, unit, orientation):
    '''
    Prints a swipe
    '''

    if unit == Unit.PX:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            print('%s%svc.swipe(startX=%d, startY=%d, endX=%d, endY=%d, steps=%d)' % (
                indent, prefix, startX, startY, endX, endY, steps))
    elif unit == Unit.DIP:
        if options[CulebraOptions.MULTI_DEVICE]:
            warnings.warn('Multi-device not implemented yet for this case')
        else:
            print('%s%svc.swipe(startX=%d, startY=%d, endX=%d, endY=%d, steps=%d)' % (
                indent, prefix, startX, startY, endX, endY, steps))
    else:
        raise RuntimeError('Invalid unit: %s' % unit)


def printPressBackUiAutomatorHelper():
    '''
    Prints the corresponding press
    '''
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction('pressing BACK')
        print('%s%svc.pressBack()' % (indent, prefix))


def printPressHomeUiAutomatorHelper():
    '''
    Prints the corresponding press
    '''
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction('pressing HOME')
        print('%s%svc.pressHome()' % (indent, prefix))


def printPressRecentAppsUiAutomatorHelper():
    '''
    Prints the corresponding press
    '''
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction('pressing RECENT APPS')
        print('%s%svc.pressRecentApps()' % (indent, prefix))


def printLongPress(keycode, duration, dev, scancode, repeat):
    '''
    Prints a key long press
    '''

    if options[CulebraOptions.MULTI_DEVICE]:
        logAction('long pressing key=%s on ${serialno}' % keycode)
        print('%s[_d.longPress(\'%s\', duration=%f, dev=\'%s\', scancode=%d, repeat=%d) for _d in %sallDevices()]' % (
            indent, keycode, duration, dev, scancode, repeat, prefix))
    else:
        logAction('long pressing key=%s' % keycode)
        print('%s%sdevice.longPress(\'%s\', duration=%f, dev=\'%s\', scancode=%d, repeat=%d)' % (
            indent, prefix, keycode, duration, dev, scancode, repeat))


def printPressUiAutomatorHelper(keycode):
    """
    Prints the corresponding press
    """
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction(f'pressing {keycode}')
        print(f'{indent}{prefix}helper.ui_device.press_key_code(KEY_EVENT[\'{keycode}\'])')
        printWaitForWindowUpdateUiAutomatorHelper()


def printWaitForIdleUiAutomatorHelper():
    """
    Prints the corresponding wait for idle
    """
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction(f'waiting for idle')
        print(f'{indent}{prefix}helper.ui_device.wait_for_idle()')


def printWaitForWindowUpdateUiAutomatorHelper():
    """
    Prints the corresponding wait for window update
    """
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction(f'waiting for idle')
        print(f'{indent}{prefix}helper.ui_device.wait_for_window_update()')


def printDumpUiAutomatorHelper(window, dump=None):
    """
    Prints a dump.

    NOTICE: This is a NO-OP for UiAutomatorHelper (i.e. CulebraTester2-public), except for
    invoking traverse

    @param window: The window id to use to print the dump
    @type window: int or str
    @param dump: Dump of Views previously obtained via L{ViewClient.dump()}
    @type dump: list
    """

    if not options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]:
        if DEBUG:
            print('printDumpUiAutomatorHelper: printing dump: %s' % (dump.__class__.__name__ if dump else None),
                  file=sys.stderr)
        printTraverseUiAutomatorHelper(dump)


def printTraverseUiAutomatorHelper(dump=None):
    """
    Prints the result of traversing the tree.
    A previously obtained dump can be passed as a parameter and in such case that
    tree is used.

    @param dump: Dump of Views previously obtained via L{ViewClient.dump()}
    @type dump: list
    """

    if DEBUG:
        print('🤷‍♀️ printTraverseUiAutomatorHelper', file=sys.stderr)

    print()
    if dump:
        for view in dump:
            transform(view)
    else:
        if DEBUG:
            print('🤷‍♀️ printTraverseUiAutomatorHelper: need to traverse the tree: not implemented yet',
                  file=sys.stderr)
        # We can do something like this for UiAutomatorHelper.
        # adb/dump:
        # vc.traverse(transform=transform)
        # uiAutromatorHelper:
        # wh = vc.uiAutomatorHelper.ui_device.dump_window_hierarchy()
        # whch: WindowHierarchyChild = None
        # for whch in wh.children:
        #    print(whch.id, whch.text, whch.clazz)
    print()


def printTestUiAutomatorHelper(selector):
    """
    Prints the corresponding test
    """
    if options[CulebraOptions.MULTI_DEVICE]:
        warnings.warn('Multi-device not implemented yet for this case')
    else:
        logAction(f'test by selector={selector}')
        indented_line = None
        indentation = indent + ' ' * 4
        # FIXME: we need has_object POST
        # We are just raising the exception if the object is not found, we could trap it
        for line in f'{indent}assert {prefix}helper.ui_device.find_object(body={selector})'.splitlines():
            if not indented_line:
                indented_line = line
            else:
                indented_line = f'{indented_line}\n{indentation}{line}'
        print(f'{indented_line} is not None')


def printOperation(view, op, *args):
    if len(args) == 0:
        # We use tuple values in the rest of this method, so if an empty tuple was passed
        # replace it by one containing None
        args = (None, None)
    elif len(args) < 2:
        # We use tuple values in the rest of this method, so if an empty tuple was passed
        # replace it by one containing None
        args = (args[0], None)
    if DEBUG:
        print("printOperation(", end=' ', file=sys.stderr)
        print(view.__str__(), end=' ', file=sys.stderr)
        print(",", op, ",", end=' ', file=sys.stderr)
        for a in args:
            if isinstance(a, str):
                print(a.encode(encoding='ascii', errors='replace'), ", ", end=' ', file=sys.stderr)
            else:
                print(a, end=' ', file=sys.stderr)
        print(")", file=sys.stderr)
    if op == Operation.SLEEP:
        printSleep(secs=args[0])
        return
    if op == Operation.SLEEP_UI_AUTOMATOR_HELPER:
        printSleepUiAutomatorHelper(secs=args[0])
        return
    elif op == Operation.SAY_TEXT:
        printSay(text=args[0])
        return
    elif op == Operation.PRESS:
        printPress(keycode=args[0])
        return
    elif op == Operation.PRESS_UI_AUTOMATOR_HELPER:
        printPressUiAutomatorHelper(keycode=args[0])
        return
    elif op == Operation.PRESS_BACK:
        printPress('BACK')
        return
    elif op == Operation.PRESS_BACK_UI_AUTOMATOR_HELPER:
        printPressBackUiAutomatorHelper()
        return
    elif op == Operation.PRESS_HOME:
        printPress('HOME')
        return
    elif op == Operation.PRESS_HOME_UI_AUTOMATOR_HELPER:
        printPressHomeUiAutomatorHelper()
        return
    elif op == Operation.PRESS_RECENT_APPS:
        printPress('RECENT_APPS')
        return
    elif op == Operation.PRESS_RECENT_APPS_UI_AUTOMATOR_HELPER:
        printPressRecentAppsUiAutomatorHelper()
        return
    elif op == Operation.LONG_PRESS:
        printLongPress(keycode=args[0], duration=args[1], dev=args[2], scancode=args[3], repeat=args[4])
        return
    elif op == Operation.DUMP:
        printDump(window=args[0], dump=args[1])
        return
    elif op == Operation.DUMP_UI_AUTOMATOR_HELPER:
        printDumpUiAutomatorHelper(window=args[0], dump=args[1])
        return
    elif op == Operation.DRAG:
        printDrag(start=args[0], end=args[1], duration=args[2], steps=args[3], unit=args[4], orientation=args[5])
        return
    elif op == Operation.TOUCH_POINT:
        printTouch(x=args[0], y=args[1], unit=args[2], orientation=args[3])
        return
    elif op == Operation.TOUCH_VIEW_UI_AUTOMATOR_HELPER:
        printTouchViewUiAutomatorHelper(selector=args[0])
        return
    elif op == Operation.LONG_TOUCH_POINT:
        printLongTouch(x=args[0], y=args[1], duration=args[2], unit=args[3], orientation=args[4])
        return
    elif op == Operation.LONG_TOUCH_VIEW_UI_AUTOMATOR_HELPER:
        printLongTouchViewUiAutomatorHelper(selector=args[0])
        return
    elif op == Operation.TEST_UI_AUTOMATOR_HELPER:
        printTestUiAutomatorHelper(selector=args[0])
        return
    elif op == Operation.TRAVERSE:
        printTraverse()
        return
    elif op == Operation.TRAVERSE_UI_AUTOMATOR_HELPER:
        printTraverseUiAutomatorHelper()
        return
    elif op == Operation.SNAPSHOT:
        printTakeSnapshot(filename=args[0], _format=args[1], deviceart=args[2], dropshadow=args[3], screenglare=args[4])
        return
    elif op == Operation.SNAPSHOT_UI_AUTOMATOR_HELPER:
        printTakeSnapshotUiAutomatorHelper(filename=args[0], _format=args[1])
        return
    elif op == Operation.SWIPE_UI_AUTOMATOR_HELPER:
        printSwipeUiAutomatorHelper(startX=args[0], startY=args[1], endX=args[2], endY=args[3], steps=args[4],
                                    unit=args[5], orientation=args[6])
        return
    elif op == Operation.VIEW_SNAPSHOT:
        printSaveViewScreenshot(view, filename=args[0], _format=args[1])
        return
    # FIXME: I moved this to printFindViewWithContentDescription(..,op.,,) to solve the problem of variable not defined
    #  when the script is invoked with -u
    #    elif op == Operation.FLING_BACKWARD:
    #        printFlingBackward(view)
    #        return
    #    elif op == Operation.FLING_FORWARD:
    #        printFlingForward(view)
    #        return
    #    elif op == Operation.FLING_TO_BEGINNING:
    #        printFlingToBeginning(view)
    #        return
    #    elif op == Operation.FLING_TO_END:
    #        printFlingToEnd(view)
    #        return
    elif op == Operation.OPEN_NOTIFICATION:
        printOpenNotification()
        return
    elif op == Operation.OPEN_QUICK_SETTINGS:
        printOpenQuickSettings()
        return
    elif op == Operation.CHANGE_LANGUAGE:
        printChangeLanguage(code=args[0])
        return
    elif op == Operation.START_ACTIVITY:
        printStartActivity(component=args[0])
        return
    elif op == Operation.START_ACTIVITY_UI_AUTOMATOR_HELPER:
        printStartActivityUiAutomatorHelper(component=args[0])
        return
    elif op == Operation.WAIT_FOR_IDLE_UI_AUTOMATOR_HELPER:
        printWaitForIdleUiAutomatorHelper()
        return
    elif op == Operation.WAIT_FOR_WINDOW_UPDATE_UI_AUTOMATOR_HELPER:
        printWaitForWindowUpdateUiAutomatorHelper()
        return
    elif op == Operation.WAKE:
        printWake()
        return

    if view is None:
        warnings.warn('view is None. Perhaps you forgot to add some "op" in the previous if.')

    bd = Descriptor.findBestDescriptor(view)
    if bd == Descriptor.CONTENT_DESCRIPTION:
        printFindViewWithContentDescription(view, options[CulebraOptions.USE_REGEXPS], op, args[0])
    elif bd == Descriptor.TEXT:
        printFindViewWithText(view, options[CulebraOptions.USE_REGEXPS], op, args[0])
    else:
        printFindViewById(view, op, args[0])


def str2bool(v):
    if len(v) < 1 or len(v) > 5:
        # len('false')=5
        warnings.warn("str2bool: strange value for conversion '%s' will be considered 'false'" % v)
    return v.lower() in ("yes", "true", "t", "1", "on")


def value2dictionaryKey(v):
    v = v.lower()
    k = ['id', 'text', 'content-description']
    sk = ['i', 't', 'd']
    if v in k:
        return v
    if v in sk:
        return k[sk.index(v)]
    error("Invalid dictionary key: %s" % v)
    usage()


def getShebangJar():
    if options[CulebraOptions.USE_JAR]:
        import java
        osName = java.lang.System.getProperty('os.name')
        if osName == 'Linux':
            return SHEBANG[SB_JAR_LINUX]
        else:
            return SHEBANG[SB_JAR]
    else:
        return SHEBANG[SB_NO_JAR]


def getWindowOption():
    return options[CulebraOptions.WINDOW] if isinstance(options[CulebraOptions.WINDOW], str) and \
                                             options[CulebraOptions.WINDOW][0] in '-0123456789' else "'%s'" % options[
        CulebraOptions.WINDOW]


def printScriptHeader():
    print(f'''{getShebangJar()}
# -*- coding: utf-8 -*-
"""
Copyright (C) 2013-2022  Diego Torres Milano
Created on {date.today()} by Culebra v{__version__}
                      __    __    __    __
                     /  \  /  \  /  \  /  \ 
____________________/  __\/  __\/  __\/  __\_____________________________
___________________/  /__/  /__/  /__/  /________________________________
                   | / \   / \   / \   / \   \___
                   |/   \_/   \_/   \_/   \    o \ 
                                           \_____/--<
@author: Diego Torres Milano
@author: Jennifer E. Swofford (ascii art snake)
"""


import os
import re
import sys
import time
''')


def printUnittestImport():
    print('''
import unittest
''')


def printAppendToSysPath():
    print('''
try:
    sys.path.append(os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src'))
except:
    pass
''')


def printPrependToSysPath():
    print('''
try:
    sys.path.insert(0, os.path.join(os.environ['ANDROID_VIEW_CLIENT_HOME'], 'src'))
except:
    pass
''')


def printLogAction(action, priority='D'):
    # FIXME: This works only for CulebraTestCases
    # This relies on shortcut variables already set (i.e. _v)
    if options[CulebraOptions.MULTI_DEVICE]:
        print('%s[_d.Log.%s(TAG, "%s", _v) for _d in %sallDevices()]' % (indent, priority.lower(), action, prefix))
    else:
        print('%s%sdevice.Log.%s(TAG, "%s",  _v)' % (indent, prefix, priority.lower(), action))


def logAction(action):
    if options[CulebraOptions.LOG_ACTIONS]:
        printLogAction(action)


def runCulebron():
    Culebron.checkSupportedSdkVersion(device.getSdkVersion())
    Culebron.checkDependencies()
    culebron = Culebron(vc, device, serialno, printOperation, options[CulebraOptions.SCALE],
                        options[CulebraOptions.CONCERTINA], options[CulebraOptions.CONCERTINA_CONFIG])
    if options[CulebraOptions.DEVICE_ART]:
        culebron.deviceArt = options[CulebraOptions.DEVICE_ART]
        culebron.dropShadow = options[CulebraOptions.DROP_SHADOW]
        culebron.screenGlare = options[CulebraOptions.SCREEN_GLARE]
    culebron.takeScreenshotAndShowItOnWindow()
    culebron.mainloop()


def printShortcutVariables():
    if options[CulebraOptions.UNIT_TEST_CLASS] or options[CulebraOptions.UNIT_TEST_METHOD]:
        print('''\
        _s = CulebraTests.sleep
        _v = CulebraTests.verbose
''')
    else:
        print('''\
_s = 5
_v = '--verbose' in sys.argv
''')


################
# __main__
################
progname = os.path.basename(sys.argv[0])
try:
    optlist, args = getopt.getopt(sys.argv[1:], CulebraOptions.SHORT_OPTS, CulebraOptions.LONG_OPTS)
    sys.argv[1:] = args
except getopt.GetoptError as e:
    error(str(e))
    usage()

kwargs1 = {CulebraOptions.VERBOSE: False, 'ignoresecuredevice': False, 'ignoreversioncheck': False}
serialno = None
kwargs2 = {ViewClientOptions.FORCE_VIEW_SERVER_USE: False, ViewClientOptions.START_VIEW_SERVER: True,
           ViewClientOptions.AUTO_DUMP: False, ViewClientOptions.IGNORE_UIAUTOMATOR_KILLED: True,
           ViewClientOptions.COMPRESSED_DUMP: True,
           ViewClientOptions.USE_UIAUTOMATOR_HELPER: False,
           ViewClientOptions.DEBUG: {},
           }
options = {CulebraOptions.FIND_VIEWS_BY_ID: True, CulebraOptions.FIND_VIEWS_WITH_TEXT: True,
           CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION: True,
           CulebraOptions.USE_REGEXPS: False, CulebraOptions.VERBOSE_COMMENTS: False,
           CulebraOptions.UNIT_TEST_CLASS: False, CulebraOptions.UNIT_TEST_METHOD: None, CulebraOptions.USE_JAR: False,
           CulebraOptions.USE_DICTIONARY: False, CulebraOptions.DICTIONARY_KEYS_FROM: 'id',
           CulebraOptions.AUTO_REGEXPS: None, CulebraOptions.START_ACTIVITY: None, CulebraOptions.OUTPUT: None,
           CulebraOptions.INTERACTIVE: False,
           CulebraOptions.WINDOW: -1, CulebraOptions.PREPEND_TO_SYS_PATH: False,
           CulebraOptions.SAVE_SCREENSHOT: None, CulebraOptions.SAVE_VIEW_SCREENSHOTS: None,
           CulebraOptions.GUI: False,
           CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP: False,
           CulebraOptions.SCALE: 1,
           CulebraOptions.ORIENTATION_LOCKED: None,
           CulebraOptions.MULTI_DEVICE: False,
           CulebraOptions.LOG_ACTIONS: False,
           CulebraOptions.DEVICE_ART: None,
           CulebraOptions.DROP_SHADOW: False,
           CulebraOptions.SCREEN_GLARE: False,
           CulebraOptions.NULL_BACK_END: False,
           CulebraOptions.CONCERTINA: False,
           CulebraOptions.CONCERTINA_CONFIG: None,
           CulebraOptions.INSTALL_APK: None,
           }
transform = traverseAndPrint
for o, a in optlist:
    o = o.strip('-')
    if o in ['H', CulebraOptions.HELP]:
        _help()
    elif o in ['V', CulebraOptions.VERBOSE]:
        kwargs1[CulebraOptions.VERBOSE] = True
    elif o in ['v', CulebraOptions.VERSION]:
        version()
    elif o in ['I', CulebraOptions.IGNORE_SECURE_DEVICE]:
        kwargs1['ignoresecuredevice'] = True
    elif o in ['E', CulebraOptions.IGNORE_VERSION_CHECK]:
        kwargs1['ignoreversioncheck'] = True
    elif o in ['F', CulebraOptions.FORCE_VIEW_SERVER_USE]:
        kwargs2[ViewClientOptions.FORCE_VIEW_SERVER_USE] = True
    elif o in ['S', CulebraOptions.DO_NOT_START_VIEW_SERVER]:
        kwargs2[ViewClientOptions.START_VIEW_SERVER] = False
    elif o in ['k', CulebraOptions.DO_NOT_IGNORE_UIAUTOMATOR_KILLED]:
        kwargs2[ViewClientOptions.IGNORE_UIAUTOMATOR_KILLED] = False
    elif o in ['w', CulebraOptions.WINDOW]:
        options[CulebraOptions.WINDOW] = a
    elif o in ['i', CulebraOptions.FIND_VIEWS_BY_ID]:
        options[CulebraOptions.FIND_VIEWS_BY_ID] = str2bool(a)
    elif o in ['t', CulebraOptions.FIND_VIEWS_WITH_TEXT]:
        options[CulebraOptions.FIND_VIEWS_WITH_TEXT] = str2bool(a)
    elif o in ['d', CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION]:
        options[CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION] = str2bool(a)
    elif o in ['r', CulebraOptions.USE_REGEXPS]:
        options[CulebraOptions.USE_REGEXPS] = True
    elif o in ['C', CulebraOptions.VERBOSE_COMMENTS]:
        options[CulebraOptions.VERBOSE_COMMENTS] = True
    elif o in ['U', CulebraOptions.UNIT_TEST_CLASS]:
        options[CulebraOptions.UNIT_TEST_CLASS] = True
    elif o in ['M', CulebraOptions.UNIT_TEST_METHOD]:
        if not a.startswith('test'):
            warnings.warn('Method name should start with "test"')
        options[CulebraOptions.UNIT_TEST_METHOD] = a
    elif o in ['j', CulebraOptions.USE_JAR]:
        options[CulebraOptions.USE_JAR] = str2bool(a)
    elif o in ['D', CulebraOptions.USE_DICTIONARY]:
        options[CulebraOptions.USE_DICTIONARY] = str2bool(a)
    elif o in ['K', CulebraOptions.DICTIONARY_KEYS_FROM]:
        options[CulebraOptions.DICTIONARY_KEYS_FROM] = value2dictionaryKey(a)
    elif o in ['R', CulebraOptions.AUTO_REGEXPS]:
        options[CulebraOptions.AUTO_REGEXPS] = a.split(',')
        for r in options[CulebraOptions.AUTO_REGEXPS]:
            if r == 'help':
                autoRegexpsHelp()
            if r == 'all':
                options[CulebraOptions.AUTO_REGEXPS] = list(CulebraOptions.AUTO_REGEXPS_RES.keys())
                break
            if r not in CulebraOptions.AUTO_REGEXPS_RES:
                error("invalid auto regexp: %s\n" % r)
                usage()
        # CulebraOptions.AUTO_REGEPXS implies CulebraOptions.USE_REGEXPS
        options[CulebraOptions.USE_REGEXPS] = True
    elif o in ['a', CulebraOptions.START_ACTIVITY]:
        options[CulebraOptions.START_ACTIVITY] = a
    elif o in ['o', CulebraOptions.OUTPUT]:
        options[CulebraOptions.OUTPUT] = a
    elif o in ['p', CulebraOptions.PREPEND_TO_SYS_PATH]:
        options[CulebraOptions.PREPEND_TO_SYS_PATH] = True
    elif o in ['f', CulebraOptions.SAVE_SCREENSHOT]:
        options[CulebraOptions.SAVE_SCREENSHOT] = a
    elif o in ['W', CulebraOptions.SAVE_VIEW_SCREENSHOTS]:
        options[CulebraOptions.SAVE_VIEW_SCREENSHOTS] = a
    elif o in ['G', CulebraOptions.GUI]:
        options[CulebraOptions.GUI] = True
    elif o in ['u', CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]:
        options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP] = True
    elif o in ['P', CulebraOptions.SCALE]:
        options[CulebraOptions.SCALE] = float(a)
    elif o in ['O', CulebraOptions.ORIENTATION_LOCKED]:
        options[CulebraOptions.ORIENTATION_LOCKED] = 'PENDING'
    elif o in ['s', CulebraOptions.SERIALNO]:
        __devices = a.split()
        if len(__devices) > 1:
            warnings.warn('List of devices not supported yet. Using first device instead.')
        serialno = __devices[0]
    elif o in ['m', CulebraOptions.MULTI_DEVICE]:
        options[CulebraOptions.MULTI_DEVICE] = True
    elif o in ['L', CulebraOptions.LOG_ACTIONS]:
        options[CulebraOptions.LOG_ACTIONS] = True
    elif o in ['A', CulebraOptions.DEVICE_ART]:
        options[CulebraOptions.DEVICE_ART] = a
    elif o in ['Z', CulebraOptions.DROP_SHADOW]:
        options[CulebraOptions.DROP_SHADOW] = True
    elif o in ['B', CulebraOptions.SCREEN_GLARE]:
        options[CulebraOptions.SCREEN_GLARE] = True
    elif o in ['0', CulebraOptions.NULL_BACK_END]:
        options[CulebraOptions.NULL_BACK_END] = True
    elif o in ['h', CulebraOptions.USE_UIAUTOMATOR_HELPER]:
        kwargs2[ViewClientOptions.USE_UIAUTOMATOR_HELPER] = True
    elif o in ['c', CulebraOptions.CONCERTINA]:
        options[CulebraOptions.CONCERTINA] = True
    elif o in ['J', CulebraOptions.CONCERTINA_CONFIG]:
        options[CulebraOptions.CONCERTINA_CONFIG] = a
        if options[CulebraOptions.CONCERTINA_CONFIG] == 'help':
            concertinaConfigHelp()
    elif o in ['1', CulebraOptions.INSTALL_APK]:
        options[CulebraOptions.INSTALL_APK] = a
    elif o in ['X', ViewClientOptions.DEBUG]:
        kwargs2[ViewClientOptions.DEBUG] = debugArgsToDict(a)

if not (options[CulebraOptions.FIND_VIEWS_BY_ID] or options[CulebraOptions.FIND_VIEWS_WITH_TEXT] or options[
    CulebraOptions.FIND_VIEWS_WITH_CONTENT_DESCRIPTION]):
    if not options[CulebraOptions.VERBOSE_COMMENTS]:
        warnings.warn('All printing options disabled. Output will be empty.')
    else:
        warnings.warn('Only verbose comments will be printed')

if kwargs2[ViewClientOptions.FORCE_VIEW_SERVER_USE] and options[CulebraOptions.NULL_BACK_END]:
    warnings.warn(
        ViewClientOptions.FORCE_VIEW_SERVER_USE + " and " + CulebraOptions.NULL_BACK_END + " cannot be specified at the same time.")

if options[CulebraOptions.MULTI_DEVICE] and not options[CulebraOptions.UNIT_TEST_CLASS]:
    error(
        "--" + CulebraOptions.MULTI_DEVICE + " only supported for unit tests (please specify --" + CulebraOptions.UNIT_TEST_CLASS + ")",
        fatal=True)

device, serialno = ViewClient.connectToDeviceOrExit(serialno=serialno, **kwargs1)
if options[CulebraOptions.ORIENTATION_LOCKED] == 'PENDING':
    options[CulebraOptions.ORIENTATION_LOCKED] = device.display['orientation']
if options[CulebraOptions.START_ACTIVITY]:
    device.startActivity(component=options[CulebraOptions.START_ACTIVITY])
if not options[CulebraOptions.NULL_BACK_END]:
    vc = ViewClient(device, serialno, **kwargs2)
else:
    vc = None
if options[CulebraOptions.OUTPUT]:
    sys.stdout = codecs.open(options[CulebraOptions.OUTPUT], mode='w', encoding='utf-8', errors='replace')
    import stat

    st = os.stat(options[CulebraOptions.OUTPUT])
    os.chmod(options[CulebraOptions.OUTPUT], st.st_mode | stat.S_IEXEC)

if options[CulebraOptions.UNIT_TEST_CLASS] or (not options[CulebraOptions.UNIT_TEST_METHOD]):
    printScriptHeader()

if options[CulebraOptions.UNIT_TEST_CLASS]:
    printUnittestImport()

if options[CulebraOptions.PREPEND_TO_SYS_PATH]:
    printPrependToSysPath()

if options[CulebraOptions.UNIT_TEST_CLASS] or (not options[CulebraOptions.UNIT_TEST_METHOD]):
    print('''\
from com.dtmilano.android.viewclient import ViewClient, KEY_EVENT%s

TAG = '%s'
''' % (', CulebraTestCase' if options[CulebraOptions.UNIT_TEST_CLASS] else '', TAG))

if options[CulebraOptions.UNIT_TEST_CLASS]:
    print('''
class CulebraTests(CulebraTestCase):

    @classmethod
    def setUpClass(cls):
        cls.kwargs1 = %s
        cls.kwargs2 = %s
        cls.options = %s
        cls.sleep = 5

''' % (kwargs1, kwargs2, options), end=' ')

    print('''
    def setUp(self):
        super(CulebraTests, self).setUp()
''')

    print('''
    def tearDown(self):
        super(CulebraTests, self).tearDown()
''')

    print('''
    def preconditions(self):
        if not super(CulebraTests, self).preconditions():
            return False
''', end=' ')

    if options[CulebraOptions.INSTALL_APK]:
        print('''
        if self.vc.installPackage(\"%s\") != 0:
            return False
''' % (options[CulebraOptions.INSTALL_APK]), end=' ')

    print('''
        return True

    def %s(self):
        if not self.preconditions():
            self.fail('Preconditions failed')
''' % (options[CulebraOptions.UNIT_TEST_METHOD] if options[CulebraOptions.UNIT_TEST_METHOD] else 'testSomething'))

    printShortcutVariables()

    if options[CulebraOptions.SAVE_SCREENSHOT]:
        print('''\
        self.vc.writeImageToFile('%s', 'PNG', %s, %s, %s)
        ''' % (options[CulebraOptions.SAVE_SCREENSHOT], options[CulebraOptions.DEVICE_ART],
               options[CulebraOptions.DROP_SHADOW], options[CulebraOptions.SCREEN_GLARE]))

    if options[CulebraOptions.USE_DICTIONARY]:
        print('''\
        self.views = dict()''')

    if not options[CulebraOptions.GUI]:
        vc.dump(window=options[CulebraOptions.WINDOW])
    indent = ' ' * 8
    prefix = 'self.'

    #     if not options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]:
    #         print '''\
    #         self.vc.dump(%s)
    #         ''' % getWindowOption()
    #         vc.traverse(transform=transform)
    #         print

    if options[CulebraOptions.GUI]:
        runCulebron()
    elif not options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]:
        if kwargs2.get(ViewClientOptions.USE_UIAUTOMATOR_HELPER, False):
            printDumpUiAutomatorHelper(getWindowOption())
        else:
            printDump(getWindowOption())
    else:
        print('''\
        ## your test code here ##
        ''')

    print('''

if __name__ == '__main__':
    CulebraTests.main()
''')
else:
    # Not a unittest class, still could be a unittest method as we allow to generate methods separately from their classes
    if not options[CulebraOptions.UNIT_TEST_METHOD]:
        printShortcutVariables()
        print('''
kwargs1 = %s
device, serialno = ViewClient.connectToDeviceOrExit(**kwargs1)
''' % kwargs1)

    if options[CulebraOptions.START_ACTIVITY] and not options[CulebraOptions.UNIT_TEST_METHOD]:
        print('''device.startActivity(component='%s')''' % options[CulebraOptions.START_ACTIVITY])

    if not options[CulebraOptions.UNIT_TEST_METHOD]:
        print('''kwargs2 = %s
vc = ViewClient(device, serialno, **kwargs2)
''' % (kwargs2))

        if kwargs2[ViewClientOptions.USE_UIAUTOMATOR_HELPER]:
            print('''helper = vc.uiAutomatorHelper''')
        if options[CulebraOptions.USE_DICTIONARY]:
            print('''views = dict()''')
        if options[CulebraOptions.SAVE_SCREENSHOT]:
            print('''vc.writeImageToFile('%s', 'PNG', %s, %s, %s)
''' % (options[CulebraOptions.SAVE_SCREENSHOT], options[CulebraOptions.DEVICE_ART], options[CulebraOptions.DROP_SHADOW],
       options[CulebraOptions.SCREEN_GLARE]))

    if vc:
        vc.dump(window=options[CulebraOptions.WINDOW])
    if options[CulebraOptions.UNIT_TEST_METHOD]:
        print('''
    def %s(self):
        \'\'\'
        Test method.
        \'\'\'

        if not self.preconditions():
            self.fail('Preconditions failed')
''' % (options[CulebraOptions.UNIT_TEST_METHOD]))
        indent = ' ' * 8
        prefix = 'self.'
        printShortcutVariables()

    if options[CulebraOptions.GUI]:
        runCulebron()
    elif not options[CulebraOptions.DO_NOT_VERIFY_SCREEN_DUMP]:
        if kwargs2.get(ViewClientOptions.USE_UIAUTOMATOR_HELPER, False):
            printDumpUiAutomatorHelper(getWindowOption())
        else:
            printDump(getWindowOption())

if kwargs2[ViewClientOptions.USE_UIAUTOMATOR_HELPER]:
    try:
        vc.uiAutomatorHelper.quit()
    except:
        pass

if options[CulebraOptions.INTERACTIVE]:
    import socket

    HOST = 'localhost'
    PORT = 8900
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    s.sendall("RECORD EVENTS START\n")
    fin = open("/dev/tty")
    while True:
        print("Reading events...", file=sys.stderr)
        data = s.recv(1024)
        code = ViewClient.excerpt(data)
        exec(code)
        resp = input("Continue recording events? [Y/n]: ")
        if resp in ['N', 'n']:
            break
    s.sendall("RECORD EVENTS STOP\n")
    s.close()
