# Copyright (c) 2012 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.

'''This file contains item formatters for resource_map_header and
resource_map_source files.  A resource map is a mapping between resource names
(string) and the internal resource ID.'''

from __future__ import print_function

import os
from functools import partial

from grit import util


def GetFormatter(type):
  if type == 'resource_map_header':
    return _FormatHeader
  if type == 'resource_file_map_source':
    return partial(_FormatSource, _GetItemPath)
  if type == 'resource_map_source':
    return partial(_FormatSource, _GetItemName)


def GetMapName(root):
  '''Get the name of the resource map based on the header file name.  E.g.,
  if our header filename is theme_resources.h, we name our resource map
  kThemeResourcesMap.

  |root| is the grd file root.'''
  outputs = root.GetOutputFiles()
  rc_header_file = None
  for output in outputs:
    if 'rc_header' == output.GetType():
      rc_header_file = output.GetFilename()
  if not rc_header_file:
    raise Exception('unable to find resource header filename')
  filename = os.path.splitext(os.path.split(rc_header_file)[1])[0]
  filename = filename[0].upper() + filename[1:]
  while True:
    pos = filename.find('_')
    if pos == -1 or pos >= len(filename):
      break
    filename = filename[:pos] + filename[pos + 1].upper() + filename[pos + 2:]
  return 'k' + filename


def _FormatHeader(root, lang='en', output_dir='.'):
  '''Create the header file for the resource mapping.  This file just declares
  an array of name/value pairs.'''
  return '''\
// This file is automatically generated by GRIT.  Do not edit.

#include <stddef.h>

#ifndef GRIT_RESOURCE_MAP_STRUCT_
#define GRIT_RESOURCE_MAP_STRUCT_
struct GritResourceMap {
  const char* const name;
  int value;
};
#endif // GRIT_RESOURCE_MAP_STRUCT_

extern const GritResourceMap %(map_name)s[];
extern const size_t %(map_name)sSize;
''' % { 'map_name': GetMapName(root) }


def _FormatSourceHeader(root, output_dir):
  '''Create the header of the C++ source file for the resource mapping.'''
  rc_header_file = None
  map_header_file = None
  for output in root.GetOutputFiles():
    type = output.GetType()
    if 'rc_header' == type:
      rc_header_file = util.MakeRelativePath(output_dir,
                                             output.GetOutputFilename())
    elif 'resource_map_header' == type:
      map_header_file = util.MakeRelativePath(output_dir,
                                              output.GetOutputFilename())
  if not rc_header_file or not map_header_file:
    raise Exception('resource_map_source output type requires '
        'a resource_map_header and rc_header outputs')
  return '''\
// This file is automatically generated by GRIT.  Do not edit.

#include "%(map_header_file)s"

#include <stddef.h>

#include "base/stl_util.h"

#include "%(rc_header_file)s"

const GritResourceMap %(map_name)s[] = {
''' % { 'map_header_file': map_header_file,
        'rc_header_file': rc_header_file,
        'map_name': GetMapName(root),
      }


def _FormatSourceFooter(root):
  # Return the footer text.
  return '''\
};

const size_t %(map_name)sSize = base::size(%(map_name)s);
''' % { 'map_name': GetMapName(root) }


def _FormatSource(get_key, root, lang, output_dir):
  from grit.node import include, structure, message
  id_map = root.GetIdMap()
  yield _FormatSourceHeader(root, output_dir)
  seen = set()
  for item in root.ActiveDescendants():
    if not item.IsResourceMapSource():
      continue
    key = get_key(item)
    tid = item.attrs['name']
    if tid not in id_map or key in seen:
      continue
    seen.add(key)
    yield '  {"%s", %s},\n' % (key, tid)
  yield _FormatSourceFooter(root)


def _GetItemName(item):
  return item.attrs['name']

# Check if |path2| is a subpath of |path1|.
def _IsSubpath(path1, path2):
  path1_abs = os.path.abspath(path1)
  path2_abs = os.path.abspath(path2)
  common = os.path.commonprefix([path1_abs, path2_abs])
  return path1_abs == common

def _GetItemPath(item):
  path = item.GetInputPath().replace("\\", "/")

  # Handle the case where the file resides within the output folder,
  # by expanding any variables as well as replacing the output folder name with
  # a fixed string such that the key added to the map does not depend on a given
  # developer's setup.
  #
  # For example this will convert the following path:
  # ../../out/gchrome/${root_gen_dir}/ui/webui/resources/js/foo.js
  # to:
  # @out_folder@/gen/ui/webui/resources/js/foo.js

  real_path = item.ToRealPath(item.GetInputPath())
  if (item.attrs.get('use_base_dir', 'true') != 'true' and
          _IsSubpath(os.path.curdir, real_path)):
    path = os.path.join(
        '@out_folder@', os.path.relpath(real_path)).replace("\\", "/")

  assert '$' not in path, 'all variables should have been expanded'
  return path
