# Copyright 2016 Google Inc.
#
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

from __future__ import print_function
import re
import time
import subprocess
import sys

class Adb:
  def __init__(self, device_serial=None, adb_binary=None, echo=False):
    self.__invocation = [adb_binary]
    if device_serial:
      self.__invocation.extend(['-s', device_serial])
    self.__echo = echo
    self.__is_root = None
    self.__has_established_connection = False

  def shell(self, cmd):
    if self.__echo:
      self.__echo_shell_cmd(cmd)
    self.__invoke('shell', cmd)

  def check(self, cmd):
    if self.__echo:
      self.__echo_shell_cmd(cmd)
    self.__establish_connection()
    result = subprocess.check_output(self.__invocation + ['shell', cmd], encoding='utf-8')
    if self.__echo:
      print(result, file=sys.stderr)
    return result

  def root(self):
    if not self.is_root():
      self.__invoke('root')
      self.__has_established_connection = False
      self.__is_root = None
    return self.is_root()

  def is_root(self):
    if self.__is_root is None:
      self.__is_root = ('root' == self.check('whoami').strip())
    return self.__is_root

  def remount(self):
    self.__invoke('remount')

  def reboot(self):
    self.__is_root = None
    self.shell('reboot')
    self.__has_established_connection = False

  def __echo_shell_cmd(self, cmd):
    escaped = [re.sub(r'([^a-zA-Z0-9])', r'\\\1', x)
               for x in cmd.strip().splitlines()]
    self.__invoke('shell', 'echo', '$(whoami)@$(getprop ro.serialno)$',
                  " '\n>' ".join(escaped))

  def __invoke(self, *args):
    self.__establish_connection()
    subprocess.call(self.__invocation + list(args), stdout=sys.stderr)

  def __establish_connection(self):
    if self.__has_established_connection:
      return
    self.__has_established_connection = True
    self.__invoke('wait-for-device')
    while True:
      time.sleep(1)
      if '1' == self.check('getprop sys.boot_completed').strip():
        break
