<?php
/*
** Zabbix
** Copyright (C) 2001-2025 Zabbix SIA
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
**/

use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\StaleElementReferenceException;

/**
 * Trait for objects implementing IWaitable interface.
 */
trait WaitableTrait {

	/**
	 * Get object selector (if any) as text.
	 *
	 * @return string
	 */
	public function getSelectorAsText() {
		if (isset($this->by)) {
			return '"'.$this->by->getValue().'" ('.$this->by->getMechanism().')';
		}

		return null;
	}

	/**
	 * Wait until object is ready.
	 *
	 * @param integer $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilReady($timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::READY, [], $timeout);

		return $this;
	}

	/**
	 * Wait until object is visible.
	 *
	 * @param integer $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilVisible($timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::VISIBLE, [], $timeout);

		return $this;
	}

	/**
	 * Wait until object is not visible.
	 *
	 * @param integer $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilNotVisible($timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::NOT_VISIBLE, [], $timeout);

		return $this;
	}

	/**
	 * Wait until object is present.
	 *
	 * @param integer $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilPresent($timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::PRESENT, [], $timeout);

		return $this;
	}

	/**
	 * Wait until object is not present.
	 *
	 * @param integer $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilNotPresent($timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::NOT_PRESENT, [], $timeout);

		return $this;
	}

	/**
	 * Wait until object text is present.
	 *
	 * @param string  $text        text to be present
	 * @param integer $timeout     timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilTextPresent($text, $timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::TEXT_PRESENT, [$text], $timeout);

		return $this;
	}

	/**
	 * Wait until object text is not present.
	 *
	 * @param string  $text        text not to be present
	 * @param integer $timeout     timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilTextNotPresent($text, $timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::TEXT_NOT_PRESENT, [$text], $timeout);

		return $this;
	}

	/**
	 * Wait until object attribute is present.
	 *
	 * @param string|array $attributes    attributes to be present
	 * @param integer      $timeout       timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilAttributesPresent($attributes, $timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::ATTRIBUTES_PRESENT, [$attributes], $timeout);

		return $this;
	}

	/**
	 * Wait until object attribute is not present.
	 *
	 * @param string|array $attributes    attributes not to be present
	 * @param integer      $timeout       timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilAttributesNotPresent($attributes, $timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::ATTRIBUTES_NOT_PRESENT, [$attributes], $timeout);

		return $this;
	}

	/**
	 * Wait until object class is present.
	 *
	 * @param string|array $classes    classes to be present
	 * @param integer      $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilClassesPresent($classes, $timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::CLASSES_PRESENT, [$classes], $timeout);

		return $this;
	}

	/**
	 * Wait until object class is not present.
	 *
	 * @param string|array $classes    classes not to be present
	 * @param integer      $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilClassesNotPresent($classes, $timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::CLASSES_NOT_PRESENT, [$classes], $timeout);

		return $this;
	}

	/**
	 * Wait until object is clickable.
	 *
	 * @param integer $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilClickable($timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::CLICKABLE, [], $timeout);

		return $this;
	}

	/**
	 * Wait until object is not clickable.
	 *
	 * @param integer $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilNotClickable($timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::NOT_CLICKABLE, [], $timeout);

		return $this;
	}

	/**
	 * Wait until element count is present.
	 *
	 * @param integer      $count      element count to wait for
	 * @param integer      $timeout    timeout in seconds
	 *
	 * @return $this
	 */
	public function waitUntilCount($count, $timeout = null) {
		CElementQuery::waitUntil($this, CElementFilter::COUNT, [$count], $timeout);

		return $this;
	}

	/**
	 * Get logically reversed condition. Acts as "not" statement.
	 *
	 * @param callable $condition    condition to be reversed
	 *
	 * @return callable
	 */
	protected function getReversedCondition($condition) {
		return function () use ($condition) {
			try {
				return !call_user_func($condition);
			}
			catch (NoSuchElementException $e) {
				return true;
			}
			catch (StaleElementReferenceException $e) {
				return true;
			}
		};
	}

	/**
	 * @inheritdoc
	 */
	public function getNotClickableCondition() {
		return $this->getReversedCondition($this->getClickableCondition());
	}

	/**
	 * @inheritdoc
	 */
	public function getNotEnabledCondition() {
		return $this->getReversedCondition($this->getEnabledCondition());
	}

	/**
	 * @inheritdoc
	 */
	public function getNotReadyCondition() {
		return $this->getReversedCondition($this->getReadyCondition());
	}

	/**
	 * @inheritdoc
	 */
	public function getNotPresentCondition() {
		return $this->getReversedCondition($this->getPresentCondition());
	}

	/**
	 * @inheritdoc
	 */
	public function getNotVisibleCondition() {
		return $this->getReversedCondition($this->getVisibleCondition());
	}

	/**
	 * @inheritdoc
	 */
	public function getTextNotPresentCondition($text) {
		return $this->getReversedCondition($this->getTextPresentCondition($text));
	}

	/**
	 * @inheritdoc
	 */
	public function getAttributesNotPresentCondition($attributes) {
		return $this->getReversedCondition($this->getAttributesPresentCondition($attributes));
	}

	/**
	 * @inheritdoc
	 */
	public function getClassesNotPresentCondition($classes) {
		return $this->getReversedCondition($this->getClassesPresentCondition($classes));
	}

	/**
	 * @inheritdoc
	 */
	public function getNotSelectedCondition() {
		return $this->getReversedCondition($this->getSelectedCondition());
	}
}