/* Tabpanel.ts

{{IS_NOTE
	Purpose:

	Description:

	History:
		Fri Jan 23 10:33:02 TST 2009, Created by Flyworld
}}IS_NOTE

Copyright (C) 2008 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
}}IS_RIGHT
*/
/**
 * A tab panel.
 * @defaultValue {@link getZclass}: z-tabpanel.
 */
@zk.WrapClass('zul.tab.Tabpanel')
export class Tabpanel extends zul.ContainerWidget {
	override parent!: zul.tab.Tabpanels | undefined;
	/** @internal */
	_lastScrollTop?: number | undefined;

	/**
	 * @returns the tabbox owns this component.
	 */
	getTabbox(): zul.tab.Tabbox | undefined {
		return this.parent ? this.parent.parent : undefined;
	}

	override isVisible(strict?: boolean): boolean {
		return super.isVisible() && this.isSelected();
	}

	override setVisible(visible: boolean): this {
		super.setVisible(visible);
		if (this.desktop && !this.isSelected()) //Bug ZK-1618: not show if current tabpanel is not selected
			this.$n_().style.display = 'none';
		return this;
	}

	/** @internal */
	override domClass_(no?: zk.DomClassOptions): string {
		var /*safe*/ cls = super.domClass_(no),
			tabbox = this.getTabbox()!;
		if (tabbox.inAccordionMold())
			cls += ' ' + this.$s('content');
		return cls;
	}

	/**
	 * @returns the tab associated with this tab panel.
	 */
	getLinkedTab(): zul.tab.Tab | undefined {
		var tabbox = this.getTabbox();
		if (!tabbox) return undefined;

		var tabs = tabbox.getTabs();
		return tabs ? tabs.getChildAt<zul.tab.Tab>(this.getIndex()) : undefined;
	}

	/**
	 * @returns the index of this panel, or -1 if it doesn't belong to any
	 * tabpanels.
	 */
	getIndex(): number {
		return this.getChildIndex();
	}

	/**
	 * @returns whether this tab panel is selected.
	 */
	isSelected(): boolean {
		return !!this.getLinkedTab()?.isSelected();
	}

	// Bug 3026669
	/** @internal */
	_changeSel(oldPanel: zul.tab.Tabpanel | undefined): void {
		if (oldPanel) {
			var cave = this.$n('cave'),
				panel: HTMLElement | undefined;
			if (cave && !cave.style.height && (panel = oldPanel.$n('cave')))
				cave.style.height = panel.style.height;
		}
	}

	/** @internal */
	_sel(toSel: boolean, animation?: boolean): void { //don't rename (zkmax counts on it)!!
		var tabbox = this.getTabbox();

		// B95-ZK-4695.zul, the Tabpanel is removed so that its desktop should be null.
		if (!tabbox || !this.desktop) return; //Bug ZK-1808 removed tabpanel is no longer in hierarchy, and cannot be removed
		var accd = tabbox.inAccordionMold();

		if (accd && animation) {
			var zkp = zk(this.$n('cave'));
			if (toSel) {
				/* ZK-1441
				 * When a tabpanel is animating, set tabbox.animating
				 * to block other tabpanels enter _sel().
				 * Reference: doClick_() in Tab.js
				 */
				tabbox._animating = true;
				zkp.slideDown(
					this,
					{'afterAnima': function () {delete tabbox!._animating;}}
				);
			} else {
				zkp.slideUp(this);
			}
		} else {
			var $pl = jq(accd ? this.$n_('cave') : this.$n_()),
				vis = $pl.zk.isVisible();
			if (toSel) {
				if (!vis) {
					$pl.show();
					// Bug ZK-1454: Scrollbar forgets its position when switching tabs in Tabbox
					if (zk.webkit)
						$pl.scrollTop(this._lastScrollTop!);
					zUtl.fireShown(this);
				}
			} else if (vis) {
				zWatch.fireDown('onHide', this);
				// Bug ZK-1454: Scrollbar forgets its position when switching tabs in Tabbox
				if (zk.webkit)
					this._lastScrollTop = $pl.scrollTop();
				$pl.hide();
			}
		}
	}

	// Could return NaN. Should validate the return value before using it.
	/** @internal */
	getPanelContentHeight_(): number {
		// NOTE: Adding undefined to a number results in NaN. If any argument of
		// Math.max is undefined, the result is NaN. Fortunately, zul.tab.Tabs.prototype._fixHgh
		// validates the return value of this function before using it.
		const node = this.$n(),
			tabpanelsNode = this.parent?.$n(),
			panelContentHeight = (tabpanelsNode?.scrollHeight!) + zk(tabpanelsNode).padBorderHeight();

		return Math.max(node?.offsetHeight!, panelContentHeight); // B50-ZK-298: concern panel height
	}

	/** @internal */
	_fixPanelHgh(): void {
		var tabbox = this.getTabbox()!,
			tbx = tabbox.$n_(),
			hgh: string | number = tbx.style.height;
		if (!hgh && tabbox._vflex)
			hgh = tbx.offsetHeight;

		if (hgh && hgh != 'auto') {
			if (!tabbox.inAccordionMold()) {
				const n = this.$n_(),
					extraHgh = tabbox.isHorizontal() ? zk(tabbox.tabs).offsetHeight()
													 : zk(n.parentNode).padBorderHeight();
				// B50-ZK-473: Tabpanel in vertical Tabbox should always have full height
				n.style.height = jq.px0(zk(tabbox).contentHeight() - extraHgh);
			} else {
				const n = this.$n_();
				let hgh = tbx.offsetHeight - zk(n.parentNode).padBorderHeight();
				for (let e = n.parentNode!.firstChild; e; e = e.nextSibling)
					if (e != n)
						hgh -= (e as HTMLElement).offsetHeight;
				hgh -= (n.firstChild as HTMLElement).offsetHeight;
				this.$n_('cave').style.height = jq.px0(hgh);
			}
		}
	}

	override onSize(): void {
		var tabbox = this.getTabbox()!;
		if (tabbox.inAccordionMold() && !zk(this.$n('cave')).isVisible())
			return;
		this._fixPanelHgh();		//Bug 2104974
	}

	//bug #3014664
	override setVflex(vflex: boolean | string | undefined): this { //vflex ignored for Tabpanel
		if (vflex != 'min') vflex = false;
		return super.setVflex(vflex);
	}

	//bug #3014664
	override setHflex(hflex: boolean | string | undefined): this { //hflex ignored for Tabpanel
		if (hflex != 'min') hflex = false;
		return super.setHflex(hflex);
	}

	/** @internal */
	override bind_(desktop?: zk.Desktop, skipper?: zk.Skipper, after?: CallableFunction[]): void {
		super.bind_(desktop, skipper, after);
		zWatch.listen({onSize: this});
		// B50-ZK-660: Dynamically generated accordion tabs cannot be closed
		var tab: zul.tab.Tab | undefined;
		if (this.getTabbox()!.inAccordionMold()
				&& (tab = this.getLinkedTab())) {

			if (!tab.$n())
				tab.unbind().bind(desktop);
			else if (!jq.isAncestor(this.$n(), tab.$n())) {
				// not display if got wrong tab,
				// it will fixed by Tabpanels#onChildAdded_ if tab add first
				// or by afterMount in tab#bind_ if panel add first
				var cave = this.$n('cave');
				if (cave) cave.style.display = 'none';
			}
		}
	}

	/** @internal */
	override unbind_(skipper?: zk.Skipper, after?: CallableFunction[], keepRod?: boolean): void {
		zWatch.unlisten({onSize: this});
		this._lastScrollTop = undefined;
		super.unbind_(skipper, after, keepRod);
	}
}