# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.


def AlignUp(v, align):
  return (v + align - 1) // align * align


def StripPlural(s):
  assert s.endswith('s'), 'Expect %s to be plural' % s
  return s[:-1]


class Color:

  def _MakeColor(code):
    t = '\033[' + code + 'm%s\033[0m'
    return lambda s: t % s

  NONE = staticmethod(lambda s: s)
  RED = staticmethod(_MakeColor('31'))
  GREEN = staticmethod(_MakeColor('32'))
  YELLOW = staticmethod(_MakeColor('33'))
  BLUE = staticmethod(_MakeColor('34'))
  MAGENTA = staticmethod(_MakeColor('35'))
  CYAN = staticmethod(_MakeColor('36'))
  WHITE = staticmethod(_MakeColor('37'))
  GRAY = staticmethod(_MakeColor('30;1'))


class TagInfo:
  """Stores resource_ids tag entry (e.g., {"includes": 100} pair)."""

  def __init__(self, raw_key, raw_value):
    """TagInfo Constructor.

    Args:
      raw_key: parser.AnnotatedValue for the parsed key, e.g., "includes".
      raw_value: parser.AnnotatedValue for the parsed value, e.g., 100.
    """
    # Tag name, e.g., 'include' (no "s" at end).
    self.name = StripPlural(raw_key.val)
    # |len(raw_value) > 1| is possible, e.g., see grd_reader_unittest.py's
    # testAssignFirstIdsMultipleMessages. This feature seems unused though.
    # TODO(huangs): Reconcile this (may end up removing multi-value feature).
    assert len(raw_value) == 1
    # Inclusive start *position* of the tag's start ID in resource_ids.
    self.lo = raw_value[0].lo
    # Exclusive end *position* of the tag's start ID in resource_ids.
    self.hi = raw_value[0].hi
    # The tag's start ID. Initially the old value, but may be reassigned to new.
    self.id = raw_value[0].val
    # The number of IDs the tag uses, to be assigned by ItemInfo.SetUsages().
    self.usage = None


class ItemInfo:
  """resource_ids item, containing multiple TagInfo."""

  def __init__(self, lo, grd, raw_item):
    # Inclusive start position of the item's key. Serve as unique identifier.
    self.lo = lo
    # The GRD filename for the item.
    self.grd = grd
    # Optional META information for the item.
    self.meta = None
    # List of TagInfo associated witih the item.
    self.tags = []
    for k, v in raw_item.items():
      if k.val == 'META':
        assert self.meta is None
        self.meta = v  # Not flattened.
      else:
        self.tags.append(TagInfo(k, v))
    self.tags.sort(key=lambda tag: tag.lo)

  def SetUsages(self, tag_name_to_usage):
    for tag in self.tags:
      tag.usage = tag_name_to_usage.get(tag.name, 0)


def BuildItemList(root_obj):
  """Extracts ID assignments and structure from parsed resource_ids.

  Returns: A list of ItemInfo, ordered by |lo|.
  """
  item_list = []
  grd_seen = set()
  for raw_key, raw_item in root_obj.items():  # Unordered.
    grd = raw_key.val
    if grd == 'SRCDIR':
      continue
    if not grd.endswith('.grd'):
      raise ValueError('Invalid GRD file: %s' % grd)
    if grd in grd_seen:
      raise ValueError('Duplicate GRD: %s' % grd)
    grd_seen.add(grd)
    item_list.append(ItemInfo(raw_key.lo, grd, raw_item))
  item_list.sort(key=lambda item: item.lo)
  return item_list
