#!/usr/bin/python

import os
import shlex
import sys

import bisect_driver

BISECT_STAGE = os.environ.get('BISECT_STAGE')
DEFAULT_BISECT_DIR = os.path.expanduser('~/ANDROID_BISECT')
BISECT_DIR = os.environ.get('BISECT_DIR') or DEFAULT_BISECT_DIR


def ProcessArgFile(arg_file):
  args = []
  # Read in entire file at once and parse as if in shell
  with open(arg_file, 'rb') as f:
    args.extend(shlex.split(f.read()))
  return args


class CompilerWrapper():
  def __init__(self, argv):
    self.args = argv
    self.execargs = []
    self.real_compiler = None
    self.argv0 = None
    self.append_flags = []
    self.prepend_flags = []
    self.custom_flags = {
      '--gomacc-path': None
    }

  def set_real_compiler(self):
    """Find the real compiler with the absolute path."""
    compiler_path = os.path.dirname(os.path.abspath(__file__))
    if os.path.islink(__file__):
      compiler = os.path.basename(os.readlink(__file__))
    else:
      compiler = os.path.basename(os.path.abspath(__file__))
    self.real_compiler = os.path.join(
        compiler_path,
        compiler + '.real')
    self.argv0 = self.real_compiler

  def process_gomacc_command(self):
    """Return the gomacc command if '--gomacc-path' is set."""
    gomacc = self.custom_flags['--gomacc-path']
    if gomacc and os.path.isfile(gomacc):
      self.argv0 = gomacc
      self.execargs += [gomacc]

  def parse_custom_flags(self):
    i = 0
    args = []
    while i < len(self.args):
      if self.args[i] in self.custom_flags:
        if i >= len(self.args) - 1:
          sys.exit('The value of {} is not set.'.format(self.args[i]))
        self.custom_flags[self.args[i]] = self.args[i + 1]
        i = i + 2
      else:
        args.append(self.args[i])
        i = i + 1
    self.args = args

  def add_flags(self):
    self.args = self.prepend_flags + self.args + self.append_flags

  def prepare_compiler_args(self):
    self.set_real_compiler()
    self.parse_custom_flags()
    self.process_gomacc_command()
    self.add_flags()
    self.execargs += [self.real_compiler] + self.args

  def invoke_compiler(self):
    self.prepare_compiler_args()
    os.execv(self.argv0, self.execargs)

  def bisect(self):
    self.prepare_compiler_args()
    # Handle @file argument syntax with compiler
    for idx, _ in enumerate(self.execargs):
      # @file can be nested in other @file arguments, use While to re-evaluate
      # the first argument of the embedded file.
      while execargs[idx][0] == '@':
        args_in_file = ProcessArgFile(execargs[idx][1:])
        self.execargs = self.execargs[0:idx] + args_in_file + self.execargs[idx + 1:]
    bisect_driver.bisect_driver(BISECT_STAGE, BISECT_DIR, execargs)


def main(argv):
  cw = CompilerWrapper(argv[1:])
  if BISECT_STAGE not in bisect_driver.VALID_MODES or '-o' not in argv:
    cw.invoke_compiler()
  else:
    cw.bisect()


if __name__ == '__main__':
  main(sys.argv)