# Copyright 2011 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.

r"""Command line tool to run the Google APIs template expander.

This tool takes a triplet of:
  - an API discovery document
  - a language (and variant)
  - a template tree name
and expands the template tree in the context of that API and tree.

Usage:
  expand_templates.py \
    --discovery=myapi.json \
    --language=python \
    --templates=my_templates \
    --output_dir=my_expanded_templates
"""

__author__ = 'aiuto@google.com (Tony Aiuto)'

import json
import os

from absl import app
from absl import flags
from googleapis.codegen import generator
from googleapis.codegen import generator_lookup
from googleapis.codegen import language_model
from googleapis.codegen.api import Api
from googleapis.codegen.filesys import package_writer_foundry
from googleapis.codegen.targets import Targets

FLAGS = flags.FLAGS

flags.DEFINE_string(
    'discovery',
    None,
    'A discovery document captured from a discovery service.')
flags.DEFINE_boolean(
    'include_timestamp',
    False,
    'Adds a timestamp to the generated source files.')
flags.DEFINE_enum(
    'language',
    'any',
    ['any'] + generator_lookup.SupportedLanguages(),
    'Target language for the generated code')
flags.DEFINE_string(
    'language_variant',
    'default',
    'which variant of "language" to generate for. E.g. "stable" vs. "head".')
flags.DEFINE_string(
    'output_dir',
    None,
    'A path to a directory where the generated files will be created.')
flags.DEFINE_string(
    'output_file',
    None,
    'An output file path to contain the archive for the generated library.'
    ' The contents of the file are determined by the output_format parameter')
flags.DEFINE_enum(
    'output_format',
    'zip',
    ['zip', 'tgz', 'tar'],
    'What format to use for --output_file.')
flags.DEFINE_enum(
    'output_type',
    'plain',
    ['plain', 'full'],
    'What kind of output to make.'
    ' plain=just the source,'
    ' full=turn on all the optional parts (useful for testing the generator).'
    )
flags.DEFINE_string(
    'templates',
    None,
    'The name of the template suite (w.r.t. language and variant)')
flags.DEFINE_bool('version_package', False, 'Put API version in package paths')

flags.declare_key_flag('discovery')
flags.declare_key_flag('include_timestamp')
flags.declare_key_flag('language')
flags.declare_key_flag('language_variant')
flags.declare_key_flag('output_dir')
flags.declare_key_flag('output_file')
flags.declare_key_flag('output_format')
flags.declare_key_flag('output_type')
flags.declare_key_flag('templates')
flags.declare_key_flag('version_package')


def main(unused_argv):
  if not FLAGS.discovery:
    raise app.UsageError('You must specify --discovery')
  if not (FLAGS.output_dir or FLAGS.output_file):
    raise app.UsageError(
        'You must specify one of --output_dir or --output_file')
  if not FLAGS.templates:
    raise app.UsageError('You must specify --templates')

  f = open(FLAGS.discovery)
  discovery_doc = json.loads(f.read())
  f.close()

  options = {
      # Include other files needed to compile (e.g. base jar files)
      'include_dependencies': False,
      # Include the timestamp in the generated library
      'include_timestamp': FLAGS.include_timestamp,
      # Put API version in the package
      'version_package': FLAGS.version_package,
      }
  if FLAGS.output_type == 'full':
    options['include_dependencies'] = True

  # try to determine what generator to use from the language variant.
  language_variants = Targets().VariationsForLanguage(FLAGS.language)
  if language_variants:
    features = language_variants.GetFeatures(FLAGS.language_variant)
    template_dir = features.template_dir
    generator_name = features.get('generator', FLAGS.language)
  else:
    # Not described by targets.json. The template dir should just be down
    # in the language specific folder
    template_dir = os.path.join(os.path.dirname(__file__), 'languages',
                                FLAGS.language)
    features = None
    generator_name = FLAGS.language

  # Instantiate the right code generator
  lang_model = None
  try:
    if FLAGS.language == 'any':
      api = Api(discovery_doc)
      # TODO(user): A default language model should be built in to the
      #   templates
      lang_model = language_model.DocumentingLanguageModel()
    else:
      generator_class = generator_lookup.GetGeneratorByLanguage(generator_name)
      generator_instance = generator_class(discovery_doc)
      api = generator_instance.api
      lang_model = generator_instance.language_model

  except ValueError:
    raise app.UsageError('Unsupported language option: %s' % FLAGS.language)

  api.VisitAll(lambda o: o.SetLanguageModel(lang_model))
  gen = TemplateExpander(api, options=options)

  if features:
    gen.SetFeatures(features)

  template_dir = os.path.join(template_dir, FLAGS.templates)
  if not os.path.isdir(template_dir):
    raise app.UsageError('Can not find template tree at: %s' % template_dir)
  gen.SetTemplateDir(template_dir)

  # Get an output writer
  package_writer = package_writer_foundry.GetPackageWriter(
      output_dir=FLAGS.output_dir, output_file=FLAGS.output_file,
      output_format=FLAGS.output_format)

  # do it
  gen.GeneratePackage(package_writer)
  package_writer.DoneWritingArchive()
  return 0


class TemplateExpander(generator.TemplateGenerator):

  def __init__(self, api, options=None):
    super(TemplateExpander, self).__init__(options=options)
    self._api = api

  def GeneratePackage(self, package_writer):
    """Walk the templates and generate output.

    Overrides the default.

    Args:
      package_writer: (LibraryPackage) output package
    """
    path_replacements = {
        '___package___': self._api.module.path
        }
    variables = {
        'api': self._api,
        'options': self._options,
        }
    self.WalkTemplateTree('.', path_replacements, {}, variables, package_writer)


if __name__ == '__main__':
  app.run(main)
