#!/usr/bin/env python
# Copyright 2018 the V8 project authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# for py2/py3 compatibility
from __future__ import print_function

import argparse
from datetime import datetime
import re
import subprocess
import sys
from os import path

RE_GITHASH = re.compile(r"^[0-9a-f]{40}")
RE_AUTHOR_TIME = re.compile(r"^author-time (\d+)$")
RE_FILENAME = re.compile(r"^filename (.+)$")


def GetBlame(file_path):
  result = subprocess.check_output(
      ['git', 'blame', '-t', '--line-porcelain', file_path])
  line_iter = iter(result.splitlines())
  blame_list = list()
  current_blame = None
  while True:
    line = next(line_iter, None)
    if line is None:
      break
    if RE_GITHASH.match(line):
      if current_blame is not None:
        blame_list.append(current_blame)
      current_blame = {'time': 0, 'filename': None, 'content': None}
      continue
    match = RE_AUTHOR_TIME.match(line)
    if match:
      current_blame['time'] = datetime.fromtimestamp(int(match.groups()[0]))
      continue
    match = RE_FILENAME.match(line)
    if match:
      current_blame['filename'] = match.groups()[0]
      current_blame['content'] = next(line_iter).strip()
      continue
  blame_list.append(current_blame)
  return blame_list


RE_MACRO_END = re.compile(r"\);")
RE_DEPRECATE_MACRO = re.compile(r"\(.*?,(.*)\);", re.MULTILINE)


def FilterAndPrint(blame_list, macro, options):
  before = options.before
  index = 0
  re_macro = re.compile(macro)
  deprecated = list()
  while index < len(blame_list):
    blame = blame_list[index]
    time = blame['time']
    if time >= before:
      index += 1
      continue
    line = blame['content']
    match = re_macro.search(line)
    if match:
      pos = match.end()
      start = -1
      parens = 0
      while True:
        if pos >= len(line):
          # extend to next line
          index = index + 1
          blame = blame_list[index]
          line = line + blame['content']
        if line[pos] == '(':
          parens = parens + 1
        elif line[pos] == ')':
          parens = parens - 1
          if parens == 0:
            # Exclud closing ")
            pos = pos - 2
            break
        elif line[pos] == '"' and start == -1:
          start = pos + 1
        pos = pos + 1
      # Extract content and replace double quotes from merged lines
      content = line[start:pos].strip().replace('""', '')
      deprecated.append([index + 1, time, content])
    index = index + 1
  print("Marked as " + macro + ": " + str(len(deprecated)))
  for linenumber, time, content in deprecated:
    print("   " + (options.v8_header + ":" +
                   str(linenumber)).rjust(len(options.v8_header) + 5) + "\t" +
          str(time) + "\t" + content)
  return len(deprecated)


def ParseOptions(args):
  parser = argparse.ArgumentParser(
      description="Collect deprecation statistics")
  parser.add_argument("v8_header", nargs='?', help="Path to v8.h")
  parser.add_argument("--before", help="Filter by date")
  options = parser.parse_args(args)
  if options.before:
    options.before = datetime.strptime(options.before, '%Y-%m-%d')
  else:
    options.before = datetime.now()
  if options.v8_header is None:
    options.v8_header = path.join(path.dirname(__file__), '..', 'include', 'v8.h')
  return options


def Main(args):
  options = ParseOptions(args)
  blame_list = GetBlame(options.v8_header)
  FilterAndPrint(blame_list, "V8_DEPRECATE_SOON", options)
  FilterAndPrint(blame_list, "V8_DEPRECATED", options)


if __name__ == "__main__":
  Main(sys.argv[1:])