/**
 * Abstract base implementation for {@link IWebPage}.
 *
 * @author Philip Helger
 */
public abstract class AbstractWebPage<WPECTYPE extends IWebPageExecutionContext>
    extends AbstractPage implements IWebPage<WPECTYPE> {
  /** The CSS class to be applied to the help div */
  public static final ICSSClassProvider CSS_PAGE_HELP_ICON =
      DefaultCSSClassProvider.create("page_help_icon");

  private IWebPageIcon m_aIcon;

  /**
   * Constructor
   *
   * @param sID The unique page ID. May not be <code>null</code>.
   */
  public AbstractWebPage(@Nonnull @Nonempty final String sID) {
    super(sID);
  }

  /**
   * Constructor
   *
   * @param sID The unique page ID. May not be <code>null</code>.
   * @param sName The constant (non-translatable) name of the page. May not be <code>null</code>.
   */
  public AbstractWebPage(@Nonnull @Nonempty final String sID, @Nonnull final String sName) {
    super(sID, sName);
  }

  /**
   * Constructor
   *
   * @param sID The unique page ID. May not be <code>null</code>.
   * @param sName The constant (non-translatable) name of the page. May not be <code>null</code>.
   * @param sDescription The constant (non-translatable) description of the page. May be <code>null
   *     </code>.
   */
  public AbstractWebPage(
      @Nonnull @Nonempty final String sID,
      @Nonnull final String sName,
      @Nullable final String sDescription) {
    super(sID, sName, sDescription);
  }

  /**
   * Constructor
   *
   * @param sID The unique page ID. May not be <code>null</code>.
   * @param aName The name of the page. May not be <code>null</code>.
   */
  public AbstractWebPage(
      @Nonnull @Nonempty final String sID, @Nonnull final IReadonlyMultiLingualText aName) {
    super(sID, aName);
  }

  /**
   * Constructor
   *
   * @param sID The unique page ID. May not be <code>null</code>.
   * @param aName The name of the page. May not be <code>null</code>.
   * @param aDescription Optional description of the page. May be <code>null</code>.
   */
  public AbstractWebPage(
      @Nonnull @Nonempty final String sID,
      @Nonnull final IReadonlyMultiLingualText aName,
      @Nullable final IReadonlyMultiLingualText aDescription) {
    super(sID, aName, aDescription);
  }

  @Nullable
  public IWebPageIcon getIcon() {
    return m_aIcon;
  }

  @Nonnull
  public AbstractWebPage<WPECTYPE> setIcon(@Nullable final IWebPageIcon aIcon) {
    m_aIcon = aIcon;
    return this;
  }

  @Nullable
  @OverrideOnDemand
  public String getHeaderText(@Nonnull final WPECTYPE aWPEC) {
    return getDisplayText(aWPEC.getDisplayLocale());
  }

  @Nullable
  @OverrideOnDemand
  public IHCNode getHeaderNode(@Nonnull final WPECTYPE aWPEC) {
    final String sText = getHeaderText(aWPEC);
    if (StringHelper.hasNoText(sText)) return null;
    return HCH1.create(sText);
  }

  @Nonnull
  public static final IRequestWebScopeWithoutResponse getScope() {
    return WebScopeManager.getRequestScope();
  }

  /** @return A form that links to the current page. */
  @Nonnull
  @Deprecated
  public HCForm createFormSelf() {
    return new HCForm(LinkUtils.getSelfHref());
  }

  /** @return A form that links to the current page. */
  @Nonnull
  public HCForm createFormSelf(@Nonnull final ILayoutExecutionContext aLEC) {
    return new HCForm(aLEC.getSelfHref());
  }

  /** @return A file upload form that links to the current page. */
  @Nonnull
  @Deprecated
  public HCForm createFormFileUploadSelf() {
    final HCForm aForm = createFormSelf();
    aForm.setFileUploadEncType();
    return aForm;
  }

  /** @return A file upload form that links to the current page. */
  @Nonnull
  public HCForm createFormFileUploadSelf(@Nonnull final ILayoutExecutionContext aLEC) {
    final HCForm aForm = createFormSelf(aLEC);
    aForm.setFileUploadEncType();
    return aForm;
  }

  /**
   * Check some pre-requisites. This is called as the very first action on each page view.
   *
   * @param aWPEC The web page execution context
   * @return Never <code>null</code>.
   */
  @OverrideOnDemand
  @Nonnull
  protected EValidity isValidToDisplayPage(@Nonnull final WPECTYPE aWPEC) {
    return EValidity.VALID;
  }

  /**
   * This method is called before the main {@link #fillContent(IWebPageExecutionContext)} method is
   * called.
   */
  @OverrideOnDemand
  protected void beforeFillContent() {}

  /**
   * Abstract method to be implemented by subclasses, that creates the main page content, without
   * the help icon.
   *
   * @param aWPEC The web page execution context. Never <code>null</code>.
   */
  protected abstract void fillContent(@Nonnull WPECTYPE aWPEC);

  /**
   * This method is called after the main {@link #fillContent(IWebPageExecutionContext)} method is
   * called.
   */
  @OverrideOnDemand
  protected void afterFillContent() {}

  /**
   * Get the help URL of the current page
   *
   * @param aRequestScope The request web scope to be used. Required for cookie-less handling. May
   *     not be <code>null</code>.
   * @param aDisplayLocale The current display locale. Never <code>null</code>.
   * @return The help URL for this page. May not be <code>null</code>.
   */
  @Nonnull
  @OverrideOnDemand
  protected ISimpleURL getHelpURL(
      @Nonnull final IRequestWebScopeWithoutResponse aRequestScope,
      @Nonnull final Locale aDisplayLocale) {
    return LinkUtils.getURLWithContext(
        aRequestScope, "help/" + getID(), new SMap().add("locale", aDisplayLocale.toString()));
  }

  /**
   * Create the HC node to represent the help icon. This method is only called, if help is available
   * for this page. The created code looks like this by default:<br>
   * <code>
   * &lt;a href="<i>helpURL</i>" title="Show help for page <i>pageName</i>" target="simplehelpwindow"&gt;<br>
   * &lt;span class="page_help_icon"&gt;&lt;/span&gt;<br>
   * &lt;/a&gt;</code>
   *
   * @param aWPEC The web page execution context
   * @return The created help icon node. May be <code>null</code>.
   */
  @Nullable
  @OverrideOnDemand
  protected IHCNode getHelpIconNode(@Nonnull final WPECTYPE aWPEC) {
    final IRequestWebScopeWithoutResponse aRequestScope = aWPEC.getRequestScope();
    final Locale aDisplayLocale = aWPEC.getDisplayLocale();

    final HCA aHelpNode = new HCA(getHelpURL(aRequestScope, aDisplayLocale));
    final String sPageName = getDisplayText(aDisplayLocale);
    aHelpNode.setTitle(
        EWebBasicsText.PAGE_HELP_TITLE.getDisplayTextWithArgs(aDisplayLocale, sPageName));
    aHelpNode.addChild(new HCSpan().addClass(CSS_PAGE_HELP_ICON));
    aHelpNode.setTarget(new HCA_Target(HELP_WINDOW_NAME));
    return aHelpNode;
  }

  /**
   * Check if is help is available for the current execution context
   *
   * @param aWPEC The web page execution context
   * @return <code>true</code> if help is available, <code>false</code> if not
   */
  @OverrideOnDemand
  public boolean isHelpAvailable(@Nonnull final WPECTYPE aWPEC) {
    return false;
  }

  /**
   * Overridable method to attach the help node to the page. This is called as the last action.
   *
   * @param aWPEC The web page execution context. Never <code>null</code>.
   * @param aHelpNode The help node to be inserted. Never <code>null</code>.
   */
  @OverrideOnDemand
  protected void insertHelpNode(@Nonnull final WPECTYPE aWPEC, @Nonnull final IHCNode aHelpNode) {
    // Add the help icon as the first child of the resulting node list
    aWPEC.getNodeList().addChild(0, aHelpNode);
  }

  /**
   * Default implementation calling the abstract {@link #fillContent(IWebPageExecutionContext)}
   * method and creating the help node if desired.
   */
  public final void getContent(@Nonnull final WPECTYPE aWPEC) {
    if (isValidToDisplayPage(aWPEC).isValid()) {
      // "before"-callback
      beforeFillContent();

      // Create the main page content
      fillContent(aWPEC);

      // "after"-callback
      afterFillContent();
    }

    // Is help available for this page?
    if (isHelpAvailable(aWPEC)) {
      final IHCNode aHelpNode = getHelpIconNode(aWPEC);
      if (aHelpNode != null) insertHelpNode(aWPEC, aHelpNode);
    }
  }
}
public abstract class AbstractWebPageExt<WPECTYPE extends IWebPageExecutionContext>
    extends AbstractWebPage<WPECTYPE> {
  /** The width of a single action column in pixels */
  public static final int DEFAULT_ACTION_COL_WIDTH = 20;

  @Deprecated public static final int ACTION_COL_WIDTH = DEFAULT_ACTION_COL_WIDTH;
  public static final String ACTION_CANCEL = "cancel";
  public static final String ACTION_COLLAPSE = CHCParam.ACTION_COLLAPSE;
  public static final String ACTION_COPY = "copy";
  public static final String ACTION_CREATE = CHCParam.ACTION_CREATE;
  public static final String ACTION_DELETE = CHCParam.ACTION_DELETE;
  public static final String ACTION_DELETE_ALL = "delete-all";
  public static final String ACTION_EDIT = CHCParam.ACTION_EDIT;
  public static final String ACTION_EXPAND = CHCParam.ACTION_EXPAND;
  public static final String ACTION_PERFORM = CHCParam.ACTION_PERFORM;
  public static final String ACTION_SAVE = CHCParam.ACTION_SAVE;
  public static final String ACTION_UNDELETE = "undelete";
  public static final String ACTION_UNDELETE_ALL = "undelete-all";
  public static final String ACTION_VIEW = CHCParam.ACTION_VIEW;

  protected static final ICSSClassProvider CSS_CLASS_LEFT = WebBasicsCSS.CSS_CLASS_LEFT;
  protected static final ICSSClassProvider CSS_CLASS_CENTER = WebBasicsCSS.CSS_CLASS_CENTER;
  protected static final ICSSClassProvider CSS_CLASS_RIGHT = WebBasicsCSS.CSS_CLASS_RIGHT;
  protected static final ICSSClassProvider CSS_CLASS_ACTION_COL = WebBasicsCSS.CSS_CLASS_ACTION_COL;
  protected static final ICSSClassProvider CSS_CLASS_EMPTY_ACTION =
      DefaultCSSClassProvider.create("empty-action");

  private static int s_nActionColWidth = DEFAULT_ACTION_COL_WIDTH;

  public AbstractWebPageExt(@Nonnull @Nonempty final String sID, @Nonnull final String sName) {
    super(sID, sName);
  }

  public AbstractWebPageExt(
      @Nonnull @Nonempty final String sID, @Nonnull final IReadonlyMultiLingualText aName) {
    super(sID, aName);
  }

  public AbstractWebPageExt(
      @Nonnull @Nonempty final String sID,
      @Nonnull final String sName,
      @Nullable final String sDescription) {
    super(sID, sName, sDescription);
  }

  public AbstractWebPageExt(
      @Nonnull @Nonempty final String sID,
      @Nonnull final IReadonlyMultiLingualText aName,
      @Nullable final IReadonlyMultiLingualText aDescription) {
    super(sID, aName, aDescription);
  }

  @Nonnull
  public static final IWebPageStyler getStyler() {
    return WebPageStylerManager.getStyler();
  }

  @Nonnegative
  public static int getActionColWidth() {
    return s_nActionColWidth;
  }

  public static void setActionColWidth(@Nonnegative final int nActionColWidth) {
    s_nActionColWidth = nActionColWidth;
  }

  /**
   * Create a HCCol (table column) for the specified number of actions. Each action represents a
   * width of {@link #getActionColWidth()} pixels. At least the width of 3 actions is displayed, so
   * that the header text fits :)
   *
   * @param nActions Number of actions. Must be &ge; 0.
   * @return The column with the according column width.
   */
  @Nonnull
  public static HCCol createActionCol(@Nonnegative final int nActions) {
    // Assume each action icon is 20 pixels (incl. margin) - at least 3 column
    // widths are required for the header
    final int nWidth = getActionColWidth() * Math.max(3, nActions);
    return new HCCol(nWidth);
  }

  @Nonnull
  public static HCSpan createEmptyAction() {
    return new HCSpan()
        .addClass(CSS_CLASS_EMPTY_ACTION)
        .addStyle(CCSSProperties.DISPLAY_INLINE_BLOCK)
        .addStyle(CCSSProperties.WIDTH.newValue(ECSSUnit.px(16)));
  }

  @Nonnull
  public static SimpleURL createCreateURL(
      @Nonnull final ILayoutExecutionContext aLEC, @Nonnull final String sMenuItemID) {
    return aLEC.getLinkToMenuItem(sMenuItemID).add(CHCParam.PARAM_ACTION, ACTION_CREATE);
  }

  @Nonnull
  public static SimpleURL createCreateURL(@Nonnull final ILayoutExecutionContext aLEC) {
    return aLEC.getSelfHref().add(CHCParam.PARAM_ACTION, ACTION_CREATE);
  }

  @Nonnull
  public static SimpleURL createViewURL(
      @Nonnull final ILayoutExecutionContext aLEC, @Nonnull final IHasID<String> aCurObject) {
    return aLEC.getSelfHref()
        .add(CHCParam.PARAM_ACTION, ACTION_VIEW)
        .add(CHCParam.PARAM_OBJECT, aCurObject.getID());
  }

  @Nonnull
  public static <T extends IHasID<String> & IHasDisplayName> HCA createEditLink(
      @Nonnull final ILayoutExecutionContext aLEC, @Nonnull final T aCurObject) {
    return createEditLink(aLEC, aCurObject, (Map<String, String>) null);
  }

  @Nonnull
  public static <T extends IHasID<String> & IHasDisplayName> HCA createEditLink(
      @Nonnull final ILayoutExecutionContext aLEC,
      @Nonnull final T aCurObject,
      @Nullable final Map<String, String> aParams) {
    return createEditLink(
        aLEC,
        aCurObject,
        EWebPageText.OBJECT_EDIT.getDisplayTextWithArgs(
            aLEC.getDisplayLocale(), aCurObject.getDisplayName()),
        aParams);
  }

  @Nonnull
  public static IHCNode getEditImg() {
    return EDefaultIcon.EDIT.getIcon().getAsNode();
  }

  @Nonnull
  public static SimpleURL createEditURL(
      @Nonnull final ILayoutExecutionContext aLEC, @Nonnull final IHasID<String> aCurObject) {
    return aLEC.getSelfHref()
        .add(CHCParam.PARAM_ACTION, ACTION_EDIT)
        .add(CHCParam.PARAM_OBJECT, aCurObject.getID());
  }

  @Nonnull
  public static HCA createEditLink(
      @Nonnull final ILayoutExecutionContext aLEC,
      @Nonnull final IHasID<String> aCurObject,
      @Nullable final String sTitle) {
    return createEditLink(aLEC, aCurObject, sTitle, (Map<String, String>) null);
  }

  @Nonnull
  public static HCA createEditLink(
      @Nonnull final ILayoutExecutionContext aLEC,
      @Nonnull final IHasID<String> aCurObject,
      @Nullable final String sTitle,
      @Nullable final Map<String, String> aParams) {
    final ISimpleURL aEditURL = createEditURL(aLEC, aCurObject).addAll(aParams);
    return new HCA(aEditURL).setTitle(sTitle).addChild(getEditImg());
  }

  @Nonnull
  public static <T extends IHasID<String> & IHasDisplayName> HCA createCopyLink(
      @Nonnull final ILayoutExecutionContext aLEC, @Nonnull final T aCurObject) {
    return createCopyLink(
        aLEC,
        aCurObject,
        EWebPageText.OBJECT_COPY.getDisplayTextWithArgs(
            aLEC.getDisplayLocale(), aCurObject.getDisplayName()));
  }

  @Nonnull
  public static IHCNode getCopyImg() {
    return EDefaultIcon.COPY.getIcon().getAsNode();
  }

  @Nonnull
  public static SimpleURL createCopyURL(
      @Nonnull final ILayoutExecutionContext aLEC, @Nonnull final IHasID<String> aCurObject) {
    return aLEC.getSelfHref()
        .add(CHCParam.PARAM_ACTION, ACTION_COPY)
        .add(CHCParam.PARAM_OBJECT, aCurObject.getID());
  }

  @Nonnull
  public static HCA createCopyLink(
      @Nonnull final ILayoutExecutionContext aLEC,
      @Nonnull final IHasID<String> aCurObject,
      @Nullable final String sTitle) {
    final ISimpleURL aCopyURL = createCopyURL(aLEC, aCurObject);
    return new HCA(aCopyURL).setTitle(sTitle).addChild(getCopyImg());
  }

  @Nonnull
  public static <T extends IHasID<String> & IHasDisplayName> HCA createDeleteLink(
      @Nonnull final ILayoutExecutionContext aLEC, @Nonnull final T aCurObject) {
    return createDeleteLink(
        aLEC,
        aCurObject,
        EWebPageText.OBJECT_DELETE.getDisplayTextWithArgs(
            aLEC.getDisplayLocale(), aCurObject.getDisplayName()));
  }

  @Nonnull
  public static IHCNode getDeleteImg() {
    return EDefaultIcon.DELETE.getIcon().getAsNode();
  }

  @Nonnull
  public static SimpleURL createDeleteURL(
      @Nonnull final ILayoutExecutionContext aLEC, @Nonnull final IHasID<String> aCurObject) {
    return aLEC.getSelfHref()
        .add(CHCParam.PARAM_ACTION, ACTION_DELETE)
        .add(CHCParam.PARAM_OBJECT, aCurObject.getID());
  }

  @Nonnull
  public static HCA createDeleteLink(
      @Nonnull final ILayoutExecutionContext aLEC,
      @Nonnull final IHasID<String> aCurObject,
      @Nullable final String sTitle) {
    final ISimpleURL aURL = createDeleteURL(aLEC, aCurObject);
    return new HCA(aURL).setTitle(sTitle).addChild(getDeleteImg());
  }

  @Nonnull
  public static SimpleURL createUndeleteURL(
      @Nonnull final ILayoutExecutionContext aLEC, @Nonnull final IHasID<String> aCurObject) {
    return aLEC.getSelfHref()
        .add(CHCParam.PARAM_ACTION, ACTION_UNDELETE)
        .add(CHCParam.PARAM_OBJECT, aCurObject.getID());
  }

  @Nonnull
  public static IHCNode getCreateImg() {
    return EDefaultIcon.NEW.getIcon().getAsNode();
  }

  @Nonnull
  public static HCA createNestedCreateLink(
      @Nonnull final ILayoutExecutionContext aLEC,
      @Nonnull final IHasID<String> aCurObject,
      @Nullable final String sTitle) {
    final ISimpleURL aURL = createCreateURL(aLEC).add(CHCParam.PARAM_OBJECT, aCurObject.getID());
    return new HCA(aURL).setTitle(sTitle).addChild(getCreateImg());
  }
}