#!/usr/bin/env python3
import argparse
from collections import defaultdict
import difflib
import pickle
from openpilot.selfdrive.car.docs import get_all_car_info
from openpilot.selfdrive.car.docs_definitions import Column
FOOTNOTE_TAG = "{}"
STAR_ICON = '![]() '
VIDEO_ICON = '' + \
             '
'
VIDEO_ICON = '' + \
             ' '
COLUMNS = "|" + "|".join([column.value for column in Column]) + "|"
COLUMN_HEADER = "|---|---|---|{}|".format("|".join([":---:"] * (len(Column) - 3)))
ARROW_SYMBOL = "➡️"
def load_base_car_info(path):
  with open(path, "rb") as f:
    return pickle.load(f)
def match_cars(base_cars, new_cars):
  changes = []
  additions = []
  for new in new_cars:
    # Addition if no close matches or close match already used
    # Change if close match and not already used
    matches = difflib.get_close_matches(new.name, [b.name for b in base_cars], cutoff=0.)
    if not len(matches) or matches[0] in [c[1].name for c in changes]:
      additions.append(new)
    else:
      changes.append((new, next(car for car in base_cars if car.name == matches[0])))
  # Removal if base car not in changes
  removals = [b for b in base_cars if b.name not in [c[1].name for c in changes]]
  return changes, additions, removals
def build_column_diff(base_car, new_car):
  row_builder = []
  for column in Column:
    base_column = base_car.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG)
    new_column = new_car.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG)
    if base_column != new_column:
      row_builder.append(f"{base_column} {ARROW_SYMBOL} {new_column}")
    else:
      row_builder.append(new_column)
  return format_row(row_builder)
def format_row(builder):
  return "|" + "|".join(builder) + "|"
def print_car_info_diff(path):
  base_car_info = defaultdict(list)
  new_car_info = defaultdict(list)
  for car in load_base_car_info(path):
    base_car_info[car.car_fingerprint].append(car)
  for car in get_all_car_info():
    new_car_info[car.car_fingerprint].append(car)
  # Add new platforms to base cars so we can detect additions and removals in one pass
  base_car_info.update({car: [] for car in new_car_info if car not in base_car_info})
  changes = defaultdict(list)
  for base_car_model, base_cars in base_car_info.items():
    # Match car info changes, and get additions and removals
    new_cars = new_car_info[base_car_model]
    car_changes, car_additions, car_removals = match_cars(base_cars, new_cars)
    # Removals
    for car_info in car_removals:
      changes["removals"].append(format_row([car_info.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) for column in Column]))
    # Additions
    for car_info in car_additions:
      changes["additions"].append(format_row([car_info.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) for column in Column]))
    for new_car, base_car in car_changes:
      # Column changes
      row_diff = build_column_diff(base_car, new_car)
      if ARROW_SYMBOL in row_diff:
        changes["column"].append(row_diff)
      # Detail sentence changes
      if base_car.detail_sentence != new_car.detail_sentence:
        changes["detail"].append(f"- Sentence for {base_car.name} changed!\n" +
                                 "  ```diff\n" +
                                 f"  - {base_car.detail_sentence}\n" +
                                 f"  + {new_car.detail_sentence}\n" +
                                 "  ```")
  # Print diff
  if any(len(c) for c in changes.values()):
    markdown_builder = ["### ⚠️ This PR makes changes to [CARS.md](../blob/master/docs/CARS.md) ⚠️"]
    for title, category in (("## 🔀 Column Changes", "column"), ("## ❌ Removed", "removals"),
                            ("## ➕ Added", "additions"), ("## 📖 Detail Sentence Changes", "detail")):
      if len(changes[category]):
        markdown_builder.append(title)
        if category not in ("detail",):
          markdown_builder.append(COLUMNS)
          markdown_builder.append(COLUMN_HEADER)
        markdown_builder.extend(changes[category])
    print("\n".join(markdown_builder))
if __name__ == "__main__":
  parser = argparse.ArgumentParser()
  parser.add_argument("--path", required=True)
  args = parser.parse_args()
  print_car_info_diff(args.path)
'
COLUMNS = "|" + "|".join([column.value for column in Column]) + "|"
COLUMN_HEADER = "|---|---|---|{}|".format("|".join([":---:"] * (len(Column) - 3)))
ARROW_SYMBOL = "➡️"
def load_base_car_info(path):
  with open(path, "rb") as f:
    return pickle.load(f)
def match_cars(base_cars, new_cars):
  changes = []
  additions = []
  for new in new_cars:
    # Addition if no close matches or close match already used
    # Change if close match and not already used
    matches = difflib.get_close_matches(new.name, [b.name for b in base_cars], cutoff=0.)
    if not len(matches) or matches[0] in [c[1].name for c in changes]:
      additions.append(new)
    else:
      changes.append((new, next(car for car in base_cars if car.name == matches[0])))
  # Removal if base car not in changes
  removals = [b for b in base_cars if b.name not in [c[1].name for c in changes]]
  return changes, additions, removals
def build_column_diff(base_car, new_car):
  row_builder = []
  for column in Column:
    base_column = base_car.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG)
    new_column = new_car.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG)
    if base_column != new_column:
      row_builder.append(f"{base_column} {ARROW_SYMBOL} {new_column}")
    else:
      row_builder.append(new_column)
  return format_row(row_builder)
def format_row(builder):
  return "|" + "|".join(builder) + "|"
def print_car_info_diff(path):
  base_car_info = defaultdict(list)
  new_car_info = defaultdict(list)
  for car in load_base_car_info(path):
    base_car_info[car.car_fingerprint].append(car)
  for car in get_all_car_info():
    new_car_info[car.car_fingerprint].append(car)
  # Add new platforms to base cars so we can detect additions and removals in one pass
  base_car_info.update({car: [] for car in new_car_info if car not in base_car_info})
  changes = defaultdict(list)
  for base_car_model, base_cars in base_car_info.items():
    # Match car info changes, and get additions and removals
    new_cars = new_car_info[base_car_model]
    car_changes, car_additions, car_removals = match_cars(base_cars, new_cars)
    # Removals
    for car_info in car_removals:
      changes["removals"].append(format_row([car_info.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) for column in Column]))
    # Additions
    for car_info in car_additions:
      changes["additions"].append(format_row([car_info.get_column(column, STAR_ICON, VIDEO_ICON, FOOTNOTE_TAG) for column in Column]))
    for new_car, base_car in car_changes:
      # Column changes
      row_diff = build_column_diff(base_car, new_car)
      if ARROW_SYMBOL in row_diff:
        changes["column"].append(row_diff)
      # Detail sentence changes
      if base_car.detail_sentence != new_car.detail_sentence:
        changes["detail"].append(f"- Sentence for {base_car.name} changed!\n" +
                                 "  ```diff\n" +
                                 f"  - {base_car.detail_sentence}\n" +
                                 f"  + {new_car.detail_sentence}\n" +
                                 "  ```")
  # Print diff
  if any(len(c) for c in changes.values()):
    markdown_builder = ["### ⚠️ This PR makes changes to [CARS.md](../blob/master/docs/CARS.md) ⚠️"]
    for title, category in (("## 🔀 Column Changes", "column"), ("## ❌ Removed", "removals"),
                            ("## ➕ Added", "additions"), ("## 📖 Detail Sentence Changes", "detail")):
      if len(changes[category]):
        markdown_builder.append(title)
        if category not in ("detail",):
          markdown_builder.append(COLUMNS)
          markdown_builder.append(COLUMN_HEADER)
        markdown_builder.extend(changes[category])
    print("\n".join(markdown_builder))
if __name__ == "__main__":
  parser = argparse.ArgumentParser()
  parser.add_argument("--path", required=True)
  args = parser.parse_args()
  print_car_info_diff(args.path)