/*
 * Copyright 2008 Google Inc.
 *
 * 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.
 */

package com.google.template.soy.tofu;

import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSortedSet;
import com.google.template.soy.data.SanitizedContent;
import com.google.template.soy.data.SoyRecord;
import com.google.template.soy.msgs.SoyMsgBundle;
import com.google.template.soy.parseinfo.SoyTemplateInfo;
import com.google.template.soy.shared.SoyCssRenamingMap;
import com.google.template.soy.shared.SoyIdRenamingMap;

import java.util.Map;

import javax.annotation.Nullable;


/**
 * SoyTofu is the public interface for a Java object that represents a compiled Soy file set.
 *
 * <p> Important: If you're a user of Soy, you should use the methods here (on a SoyTofu object
 * created by Soy), but should not create your own implementations of this interface.
 *
 */
public interface SoyTofu {


  /**
   * Gets the namespace of this SoyTofu object. The namespace is simply a convenience allowing
   * {@code newRenderer()} to be called with a partial template name (e.g. ".fooTemplate").
   * Note: The namespace may be null, in which case {@code newRenderer()} must be called with the
   * full template name.
   *
   * @return The namespace of this SoyTofu object, or null if no namespace.
   */
  String getNamespace();


  /**
   * Gets a new SoyTofu instance with a different namespace (or no namespace).
   * Note: The new SoyTofu instance will still be backed by the same compiled Soy file set.
   *
   * @param namespace The namespace for the new SoyTofu instance, or null for no namespace.
   * @return A new SoyTofu instance with a different namespace (or no namespace).
   */
  SoyTofu forNamespace(@Nullable String namespace);

  /**
   * Gets a new Renderer for a template.
   *
   * <p> The usage pattern is
   *   soyTofu.newRenderer(...).setData(...).setInjectedData(...).setMsgBundle(...).render()
   * where any of the set* parts can be omitted if it's null.
   *
   * @param templateInfo Info for the template to render.
   * @return A new renderer for the given template.
   */
  Renderer newRenderer(SoyTemplateInfo templateInfo);


  /**
   * Gets a new Renderer for a template.
   *
   * <p> The usage pattern is
   *   soyTofu.newRenderer(...).setData(...).setInjectedData(...).setMsgBundle(...).render()
   * where any of the set* parts can be omitted if it's null.
   *
   * @param templateName The name of the template to render. If this SoyTofu instance is not
   *     namespaced, then this parameter should be the full name of the template including the
   *     namespace. If this SoyTofu instance is namespaced, then this parameter should be a partial
   *     name beginning with a dot (e.g. ".fooTemplate").
   * @return A new renderer for the given template.
   */
  Renderer newRenderer(String templateName);


  /**
   * Gets the set of injected param keys used by a template (and its transitive callees).
   *
   * <p> Note: The {@code SoyTemplateInfo} object already has a method {@code getUsedIjParams()}.
   * That method should produce the same results as this method, unless the bundle of Soy files
   * included when running the SoyParseInfoGenerator is different from the bundle of Soy files
   * included when creating this SoyTofu object.
   *
   * @param templateInfo Info for the template to get injected params of.
   * @return The set of injected param keys used by the given template.
   */
  ImmutableSortedSet<String> getUsedIjParamsForTemplate(SoyTemplateInfo templateInfo);


  /**
   * Gets the set of injected param keys used by a template (and its transitive callees).
   *
   * @param templateName The name of the template to get injected params of.
   * @return The set of injected param keys used by the given template.
   */
  ImmutableSortedSet<String> getUsedIjParamsForTemplate(String templateName);


  // -----------------------------------------------------------------------------------------------
  // Renderer interface.


  /**
   * Renderer for a template.
   *
   * <p> Important: If you're a user of Soy, you should use the methods here (on a Renderer object
   * created by Soy), but should not create your own implementations of this interface.
   */
  interface Renderer {

    /**
     * Sets the data to call the template with. Can be null if the template has no parameters.
     *
     * <p> Note: If you call this method instead of {@link #setData(SoyRecord)}, your template data
     * will be converted to a {@code SoyMapData} object on each call. This may not be a big deal if
     * you only need to use the data object once. But if you need to reuse the same data object for
     * multiple calls, it's more efficient to build your own {@code SoyRecord} object and reuse it
     * with {@link #setData(SoyRecord)}.
     */
    Renderer setData(Map<String, ?> data);

    /**
     * Sets the data to call the template with. Can be null if the template has no parameters.
     */
    Renderer setData(SoyRecord data);

    /**
     * Sets the injected data to call the template with. Can be null if not used.
     *
     * <p> Note: If you call this method instead of {@link #setIjData(SoyRecord)}, the data
     * will be converted to a {@code SoyRecord} object on each call. This may not be a big deal if
     * you only need to use the data object once. But if you need to reuse the same data object for
     * multiple calls, it's more efficient to build your own {@code SoyRecord} object and reuse it
     * with {@link #setIjData(SoyRecord)}.
     */
    Renderer setIjData(Map<String, ?> ijData);

    /**
     * Sets the injected data to call the template with. Can be null if not used.
     */
    Renderer setIjData(SoyRecord ijData);

    /**
     * Sets the predicate to use for testing whether or not a given {@code delpackage} is active.
     */
    Renderer setActiveDelegatePackageSelector(Predicate<String> activeDelegatePackageSelector);

    /**
     * Sets the bundle of translated messages, or null to use the messages from the Soy source.
     */
    Renderer setMsgBundle(SoyMsgBundle msgBundle);

    /**
     * Sets the ID renaming map.
     */
    Renderer setIdRenamingMap(SoyIdRenamingMap idRenamingMap);

    /**
     * Sets the CSS renaming map.
     */
    Renderer setCssRenamingMap(SoyCssRenamingMap cssRenamingMap);

    /**
     * Sets the expected content kind.
     *
     * <p>An attempt to render a non-strict template or a strict template with a different kind
     * will fail if this has been called.
     */
    Renderer setContentKind(SanitizedContent.ContentKind contentKind);

    /**
     * Renders the template using the data, injected data, and message bundle previously set.
     *
     * <p>Checks the content kind of the template. Non-strict and kind="html" templates are
     * allowed, unless setContentKind was called. The goal is to prevent accidental rendering
     * of unescaped kind="text" in contexts where that could XSS.
     *
     * @throws SoyTofuException if an error occurs during rendering.
     */
    String render();

    /**
     * Renders the strict-mode template as a SanitizedContent object, which can be used as an input
     * to another Soy template, or used to verify that the output type is correct.
     *
     * <p>This returns a SanitizedContent object corresponding to the kind="..." attribute of the
     * template. The expected content kind must be set beforehand, unless HTML is expected, to
     * avoid an exception.
     *
     * @throws IllegalArgumentException If the template is non-strict, or the kind doesn't match
     *     the expected kind (from setContentKind, or the default of HTML).
     * @throws SoyTofuException if an error occurs during rendering.
     */
    SanitizedContent renderStrict();

    /**
     * Renders the template using the data, injected data, and message bundle previously set
     * into the given Appendable.
     *
     * <p>Checks the content kind of the template. Non-strict and kind="html" templates are
     * allowed, unless setContentKind was called. The goal is to prevent accidental rendering
     * of unescaped kind="text" in contexts where that could XSS.
     *
     * @throws SoyTofuException if an error occurs during rendering.
     */
    SanitizedContent.ContentKind render(Appendable out);
  }


  // -----------------------------------------------------------------------------------------------
  // Old render methods.


  /**
   * Renders a template.
   *
   * @param templateInfo Info for the template to render.
   * @param data The data to call the template with. Can be null if the template has no parameters.
   * @param msgBundle The bundle of translated messages, or null to use the messages from the
   *     Soy source.
   * @return The rendered text.
   * @deprecated Use {@link #newRenderer(SoyTemplateInfo)}.
   */
  @Deprecated
  String render(
      SoyTemplateInfo templateInfo, @Nullable SoyRecord data, @Nullable SoyMsgBundle msgBundle);


  /**
   * Renders a template.
   *
   * <p> Note: If you call this method instead of {@link #render(String, SoyRecord, SoyMsgBundle)},
   * your template data will be converted to a {@code SoyRecord} object on each call. This may not
   * be a big deal if you only need to use the data object once. But if you need to reuse the same
   * data object for multiple calls, it's more efficient to build your own {@code SoyRecord} object
   * and reuse it with {@link #render(String, SoyRecord, SoyMsgBundle)}.
   *
   * @param templateName The name of the template to render. If this SoyTofu instance is namespaced,
   *     then this parameter should be a partial name beginning with a dot (e.g. ".fooTemplate").
   * @param data The data to call the template with. Can be null if the template has no parameters.
   * @param msgBundle The bundle of translated messages, or null to use the messages from the
   *     Soy source.
   * @return The rendered text.
   * @deprecated Use {@link #newRenderer(String)}.
   */
  @Deprecated
  String render(
      String templateName, @Nullable Map<String, ?> data, @Nullable SoyMsgBundle msgBundle);


  /**
   * Renders a template.
   *
   * @param templateName The name of the template to render. If this SoyTofu instance is namespaced,
   *     then this parameter should be a partial name beginning with a dot (e.g. ".fooTemplate").
   * @param data The data to call the template with. Can be null if the template has no parameters.
   * @param msgBundle The bundle of translated messages, or null to use the messages from the
   *     Soy source.
   * @return The rendered text.
   * @deprecated Use {@link #newRenderer(String)}.
   */
  @Deprecated
  String render(
      String templateName, @Nullable SoyRecord data, @Nullable SoyMsgBundle msgBundle);

}
