#!/usr/bin/env python
#
# Copyright 2014 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.
"""Routines for character coverage of fonts."""

__author__ = 'roozbeh@google.com (Roozbeh Pournader)'

import argparse
import codecs
from os import path
import re

from fontTools import ttLib

from nototools import unicode_data
from nototools import lint_config
from nototools.py23 import unichr


def character_set(font):
  """Returns the character coverage of a font.

  Args:
    font: The input font's file name, or a TTFont.

  Returns:
    A frozenset listing the characters supported in the font.
  """
  if isinstance(font, str):
    font = ttLib.TTFont(font, fontNumber=0)
  cmap_table = font['cmap']
  cmaps = {}
  for table in cmap_table.tables:
    if (table.format, table.platformID, table.platEncID) in [
        (4, 3, 1), (12, 3, 10)
    ]:
      cmaps[table.format] = table.cmap
  if 12 in cmaps:
    cmap = cmaps[12]
  elif 4 in cmaps:
    cmap = cmaps[4]
  else:
    cmap = {}
  return frozenset(cmap.keys())


def convert_set_to_ranges(charset):
  """Converts a set of characters to a list of ranges."""
  working_set = set(charset)
  output_list = []
  while working_set:
    start = min(working_set)
    end = start + 1
    while end in working_set:
      end += 1
    output_list.append((start, end - 1))
    working_set.difference_update(range(start, end))
  return output_list


def _print_char_info(chars):
  for char in chars:
    try:
      name = unicode_data.name(char)
    except ValueError:
      name = '<Unassigned>'
    print('U+%04X %s' % (char, name))


def _write_char_text(chars, filepath, chars_per_line, sep):
  def accept_cp(cp):
    cat = unicode_data.category(cp)
    return cat[0] not in ['M', 'C', 'Z'] or cat == 'Co'

  text = [unichr(cp) for cp in chars if accept_cp(cp)]
  filename, _ = path.splitext(path.basename(filepath))
  m = re.match(r'(.*)-(?:Regular|Bold|Italic|BoldItalic)', filename)
  if m:
    filename = m.group(1)
  filename += '_chars.txt'
  print('writing file: %s' % filename)
  print('%d characters (of %d)' % (len(text), len(chars)))
  if chars_per_line > 0:
    lines = []
    for n in range(0, len(text), chars_per_line):
      substr = text[n:n + chars_per_line]
      lines.append(sep.join(cp for cp in substr))
    text = '\n'.join(lines)
  with codecs.open(filename, 'w', 'utf-8') as f:
    f.write(text)


def _process_font(filepath, args):
  char_set = character_set(filepath)
  if args.limit_set:
    char_set = char_set & args.limit_set
    if not char_set:
      print('limit excludes all chars in %s' % filepath)
      return
  sorted_chars = sorted(char_set)
  if args.info:
    _print_char_info(sorted_chars)
  if args.text:
    _write_char_text(sorted_chars, filepath, args.chars_per_line, args.sep)
  if args.ranges:
    print('ranges:\n  ' + lint_config.write_int_ranges(sorted_chars, True))


def main():
  parser = argparse.ArgumentParser()
  parser.add_argument('files', help='Files to dump', metavar='file', nargs='+')
  parser.add_argument('--ranges',
                      help='Dump cmap as hex ranges',
                      action='store_true')
  parser.add_argument('--text',
                      help='Dump cmap as sample text',
                      action='store_true')
  parser.add_argument('--sep',
                      help='Separator between chars in text, default space',
                      default=' ')
  parser.add_argument('--info',
                      help='Dump cmap as cp and unicode name, one per line',
                      action='store_true')
  parser.add_argument('--chars_per_line',
                      help='Format text in lines of at most this '
                      'many codepoints,  0 to format as a single line',
                      type=int,
                      metavar='N',
                      default=32)
  parser.add_argument('--limit',
                      help='string of hex codepoint ranges limiting cmap '
                      'to output',
                      metavar='ranges')
  args = parser.parse_args()

  if not (args.ranges or args.text or args.info):
    args.info = True

  if args.limit:
    args.limit_set = lint_config.parse_int_ranges(args.limit)
    print('limit to: ' + lint_config.write_int_ranges(args.limit_set))
  else:
    # make sure it exists so checks don't have to care
    args.limit_set = None

  for fontpath in args.files:
    print('Font: ' + path.normpath(fontpath))
    _process_font(fontpath, args)


if __name__ == '__main__':
  main()
