#!/usr/bin/env python
#
# Copyright 2015 Google Inc. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Update internal svn repo with .ttf files from unpacked font drops.

This checks the font files direct from the vendor to our internal repo.
Other files are not checked in at the moment.

This uses compare_summary to generate a comment for the submit, since
we can't view the file contents, just so we have some idea of what is
going in."""

__author__ = 'dougfelt@google.com (Doug Felt)'

import argparse
import filecmp
import os
import os.path
import re
import shutil
import subprocess
import sys

from nototools import notoconfig
from nototools import compare_summary

class RedirectStdout(object):
  """Redirect stdout to file."""
  def __init__(self, filename):
    self._filename = filename

  def __enter__(self):
    self._stdout = sys.stdout
    sys.stdout = open(self._filename, 'w')

  def __exit__(self, etype, evalue, etraceback):
    file = sys.stdout
    sys.stdout = self._stdout
    try:
      file.close()
    except Exception:
      if not etype:
        raise
    # else raise the original exception

def push_to_noto_alpha(alphadir, srcdir, dry_run):
  # strip possible trailing slash for later relpath manipulations
  alphadir = os.path.abspath(alphadir)
  srcdir = os.path.abspath(srcdir)

  # could try to use pysvn, but that would be a separate dependency
  # poke svn first in case there's some issue with username/pw, etc.
  os.chdir(alphadir)
  subprocess.check_call(['svn', 'up'], stderr=subprocess.STDOUT)

  # TODO(dougfelt): make sure there's nothing already staged in the
  # repo, and that there's no files in the alpha tree that aren't
  # actually part of the repo.

  # collect file info, we do this so we can generate msg header
  font_paths = []
  added = 0
  updated = 0
  for root, dirs, files in os.walk(srcdir):
    for file in files:
      if file.endswith('.ttf'):
        src_path = os.path.join(root, file)
        rel_path = src_path[len(srcdir)+1:]
        dst_path = os.path.join(alphadir, rel_path)
        # skip files that are the same as targets
        if not os.path.exists(dst_path):
          added += 1
          font_paths.append(src_path)
        elif not filecmp.cmp(src_path, dst_path):
          updated += 1
          font_paths.append(src_path)

  if not font_paths:
    print('All .ttf files compare identical.  Exiting.')
    return

  # summarize fonts in this commit
  name_rx = re.compile(r'.*/Noto(?:Sans|Serif)?(.+?)(?:UI)?-.*')
  name_info = {}
  for f in font_paths:
    hinted = f.find('unhinted') == -1
    result = name_rx.match(f)
    if not result:
      raise ValueError('Could not match root font name in %s' % f)
    root_name = result.group(1)
    new_label = 'h' if hinted else 'u'
    cur_label = name_info.get(root_name, None)
    if cur_label:
      if cur_label.find(new_label) != -1:
        new_label = None
      else:
        # Using 'uh' would cause find to fail, if we processed unhinted first.
        # Which we don't, but relying on that is kind of obscure.
        new_label = 'h/u'
    if new_label:
      name_info[root_name] = new_label
    names = ', '.join(sorted(['%s(%s)' % (k, v) for k, v in name_info.items()]))

  # get date of the drop from srcdir
  result = re.search(r'\d{4}_\d{2}_\d{2}', srcdir)
  if not result:
    raise ValuError('no date in ' + srcdir)
  date_str = result.group().replace('_', '/')

  if added:
    if updated:
      operation = 'Add/Update'
    else:
      operation = 'Add'
  else:
    operation = 'Update'
  one_line_msg = '%s %s from delivery on %s.' % (operation, names, date_str)

  # generate compare file to use as checkin log
  checkin_msg_file = '/tmp/svn_checkin.txt'
  with RedirectStdout(checkin_msg_file):
    print(one_line_msg)
    print()
    compare_summary.compare_summary(
      alphadir, srcdir, None, compare_summary.tuple_compare, True, False, False, False)

  # make the changes
  for src_path in font_paths:
    rel_path = src_path[len(srcdir)+1:]
    dst_path = os.path.join(alphadir, rel_path)
    need_add = not os.path.exists(dst_path)
    # assume if it exists, its under version control
    if not dry_run:
      shutil.copy2(src_path, dst_path)
      if need_add:
        subprocess.check_call(['svn', 'add', rel_path], stderr=subprocess.STDOUT)

  # commit the change
  # leave this out for now, it's useful to check before the commit to make sure
  # nothing screwed up.
  # subprocess.check_call(['svn', 'commit', '-F', checkin_msg_file],
  #                       stderr=subprocess.STDOUT)

  with open(checkin_msg_file) as f:
    checkin_msg = f.read().strip()

  print('%s\n-----\n%s\n-----' % ('dry run' if dry_run else 'summary', checkin_msg))
  if not dry_run:
    print('command to update: svn commit -F \'%s\'' % checkin_msg_file)


def main():
  values = notoconfig.values
  default_alpha = values.get('alpha')

  parser = argparse.ArgumentParser()
  parser.add_argument('srcdir', help='source to push to noto-alpha')
  parser.add_argument('--alpha', help='local noto-alpha svn repo (default %s)' %
                      default_alpha, default=default_alpha)
  parser.add_argument('--dry-run', dest='dry_run',
                      help='do not stage changes for svn', action='store_true')
  args = parser.parse_args()

  if not os.path.isdir(args.srcdir):
    print('%s does not exist or is not a directory' % args.srcdir)
    return

  if not os.path.exists(args.alpha):
    print('%s does not exist or is not a directory' % args.alpha)
    return

  push_to_noto_alpha(args.alpha, args.srcdir, args.dry_run)

if __name__ == '__main__':
    main()
