/*
 * Copyright (c) 2010, 2023 BSI Business Systems Integration AG
 *
 * This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 */
import {AbstractLayout, Dimension, Group, HtmlComponent, HtmlCompPrefSizeOptions} from '../index';

export class GroupLayout extends AbstractLayout {
  group: Group;

  constructor(group: Group) {
    super();
    this.group = group;
  }

  override layout($container: JQuery) {
    let htmlComp = this.group.htmlComp;
    let containerSize = htmlComp.availableSize()
      .subtract(htmlComp.insets());

    let htmlHeader = this.group.htmlHeader;
    let headerSize = htmlHeader.prefSize({
      widthHint: containerSize.width
    });
    headerSize.width = containerSize.width;
    headerSize = headerSize.subtract(htmlHeader.margins());
    htmlHeader.setSize(headerSize);

    let htmlFooter = this.group.htmlFooter;
    if (htmlFooter.isVisible()) {
      let footerSize = htmlFooter.prefSize({
        includeMargin: false,
        useCssSize: true
      });
      footerSize.width = containerSize.width;
      htmlFooter.setSize(footerSize.subtract(htmlFooter.margins()));
    }

    // 1st condition: Set size only if group is expanded
    // 2nd condition: There is no need to update it during the expand animation (the body will be layouted correctly before the animation starts)
    // 3rd condition: When Group.setCollapsed(false) has been called an event is triggered that might cause invalidating layout on other all groups (inclusive currently expanding group).
    //                The body of the currently expanding group is not rendered at this time.
    // 4th condition: When body is invisible by property (bodyVisible)
    if (this.group.collapsed || this.group.bodyAnimating || !this.group.body.rendered || !this.group.body.visible) {
      return;
    }

    let htmlBody = this.group.body.htmlComp;
    let bodySize = containerSize.subtract(htmlBody.margins());
    bodySize.height -= headerSize.height;
    if (htmlFooter.isVisible()) {
      bodySize.height -= htmlFooter.prefSize(true).height;
    }
    htmlBody.setSize(bodySize);
  }

  override invalidate(htmlSource: HtmlComponent) {
    let htmlBody = this.group.body.htmlComp;
    // If a child triggers a layout invalidation, the animation should be stopped and restarted because the body will likely have another height.
    // This will happen for sure if a child is an image which will be loaded during the animation.
    if (htmlBody && this.group.bodyAnimating && htmlSource && htmlSource.isDescendantOf(this.group.htmlComp)) {
      // Stop running animation
      this.group.body.$container.stop();

      // Resize to new height
      this.group.resizeBody();
    }
  }

  override preferredLayoutSize($container: JQuery, options?: HtmlCompPrefSizeOptions): Dimension {
    options = options || {};
    let prefSize;
    let htmlComp = this.group.htmlComp;
    let htmlHeader = this.group.htmlHeader;
    let htmlBody = this.group.body.htmlComp;
    let htmlFooter = this.group.htmlFooter;

    // HeightHint not supported
    options.heightHint = null;

    if (this.group.bodyAnimating) {
      // Return the current size when the body is collapsing or expanding
      // so that the widgets on the bottom and on top move smoothly with the animation
      prefSize = htmlBody.size(true);
    } else if (this.group.collapsed || !this.group.body.rendered || !this.group.body.visible) {
      // Body may not be rendered even if collapsed is false if property has changed but _renderCollapse not called yet
      // (if revalidateLayoutTree is called during collapsed property event)
      prefSize = new Dimension(0, 0);
    } else {
      prefSize = htmlBody.prefSize(options)
        .add(htmlBody.margins());
    }
    prefSize = prefSize.add(htmlComp.insets({
      includeMargin: true
    }));
    prefSize.height += htmlHeader.prefSize(options)
      .add(htmlHeader.margins()).height;
    if (htmlFooter.isVisible()) {
      prefSize.height += htmlFooter.prefSize({
        includeMargin: true,
        useCssSize: true
      }).height;
    }
    return prefSize;
  }
}
