<?php
/**
 * Element class.
 *
 * @package EDD\Invoices\XML\Elements
 */

namespace EDD\Pro\Invoices\XML\Elements;

// Exit if accessed directly.
defined( 'ABSPATH' ) || exit; // @codeCoverageIgnore

/**
 * Class Element
 *
 * @since 1.4.0
 */
abstract class Element {

	/**
	 * The XML document.
	 *
	 * @since 1.4.0
	 * @var \DOMDocument
	 */
	protected $document;

	/**
	 * The order.
	 *
	 * @since 1.4.0
	 * @var \EDD\Orders\Order
	 */
	protected $order;

	/**
	 * The individual item to add to the XML document.
	 *
	 * @since 1.4.0
	 * @var object
	 */
	protected $item;

	/**
	 * Element constructor.
	 *
	 * @since 1.4.0
	 *
	 * @param \SimpleXMLElement $xml  The XML document.
	 * @param object            $item The item.
	 */
	public function __construct( $xml, $item = null ) {
		$this->document = $xml->dom;
		$this->order    = $xml->order;
		$this->item     = $item;
	}

	/**
	 * Add an element to the XML document.
	 *
	 * @since 1.4.0
	 *
	 * @param \DOMElement|null $parent_element The parent element.
	 * @param array            $data           The data for the element.
	 * @return \DOMElement
	 */
	public function add( $parent_element = null, $data = array() ) {
		if ( empty( $data ) ) {
			$data = $this->get_filtered_data();
		}

		if ( empty( $data ) || empty( $data['name'] ) ) {
			return;
		}

		$value   = ! empty( $data['value'] ) ? $data['value'] : '';
		$element = $this->document->createElement( $data['name'], $value );

		if ( ! empty( $data['attributes'] ) ) {
			foreach ( $data['attributes'] as $name => $value ) {
				$element->setAttribute( $name, $value );
			}
		}

		if ( ! empty( $data['children'] ) ) {
			foreach ( $data['children'] as $child ) {
				$this->add( $element, $child );
			}
		}

		if ( ! $parent_element ) {
			$parent_element = $this->document;
		}
		$parent_element->appendChild( $element );

		return $element;
	}

	/**
	 * Get the data.
	 *
	 * @since 1.4.0
	 * @return array
	 */
	abstract protected function get_data(): array;

	/**
	 * Formats a number.
	 *
	 * @since 1.4.0
	 * @param float $number The number to format.
	 * @return string
	 */
	protected function format_number( $number ) {
		return number_format( $number, 2, '.', '' );
	}

	/**
	 * Get the filtered data.
	 *
	 * @since 1.4.0
	 * @return array
	 */
	private function get_filtered_data(): array {
		/**
		 * Filter the data for the element.
		 *
		 * @since 1.4.0
		 * @param array            $data  The data for the element.
		 * @param \EDD\Orders\Order $order The order object.
		 * @param string           $id    The element ID.
		 * @param object           $item  The item.
		 */
		return apply_filters(
			"edd_invoices_xml_element_{$this->get_id()}_data",
			$this->get_data(),
			$this->order,
			$this->get_id(),
			$this->item
		);
	}

	/**
	 * Get the element ID.
	 *
	 * @since 1.4.0
	 * @return string
	 */
	private function get_id() {
		$reflection = new \ReflectionClass( $this );

		return strtolower( $reflection->getShortName() );
	}
}
