<?php
/**
 * YITH_WooCommerce_Barcodes class
 *
 * @package YITH\Barcodes\Classes
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

if ( ! class_exists( 'YITH_WooCommerce_Barcodes' ) ) {

	/**
	 * YITH_WooCommerce_Barcodes
	 *
	 * @class   YITH_WooCommerce_Barcodes
	 * @package Yithemes
	 * @since   1.0.0
	 * @author  YITH <plugins@yithemes.com>
	 */
	class YITH_WooCommerce_Barcodes {

		/**
		 * Single instance of the class
		 *
		 * @var instance;
		 * @since 1.0.0
		 */
		protected static $instance;

		/**
		 * Enable_on_orders
		 *
		 * @var bool set if the barcode generation is enabled for orders
		 */
		public $enable_on_orders = true;

		/**
		 * Create_on_orders
		 *
		 * @var bool create automatically on new order
		 */
		public $create_on_orders = false;

		/**
		 * Enable_on_products
		 *
		 * @var bool set if the barcode generation is enabled for products
		 */
		public $enable_on_products = true;

		/**
		 * Manual_value_on_products
		 *
		 * @var bool permit to enter a custom value for the product barcode
		 */
		public $manual_value_on_products = false;

		/**
		 * Create_on_products
		 *
		 * @var bool create automatically barcode for new products
		 */
		public $create_on_products = false;

		/**
		 * Orders_protocol
		 *
		 * @var string default barcode protocol to be used for orders
		 */
		public $orders_protocol = '';

		/**
		 * Show_on_order_page
		 *
		 * @var bool set if the barcode should be shown on order page
		 */
		public $show_on_order_page = false;

		/**
		 * Products_protocol
		 *
		 * @var string default barcode protocol to be used for orders
		 */
		public $products_protocol = '';

		/**
		 * Show_on_product_page
		 *
		 * @var bool set if the barcode should be shown on single product page
		 */
		public $show_on_product_page = true;

		/**
		 * Show_on_email_completed
		 *
		 * @var bool show the order barcode on email sent whem the order is set as completed
		 */
		public $show_on_email_completed = false;

		/**
		 * Show_on_email_all
		 *
		 * @var bool show the order barcode on email sent when the order is set as completed
		 */
		public $show_on_email_all = false;

		/**
		 * Premium_landing
		 *
		 * @var string Premium version landing link
		 */
		protected $premium_landing = 'https://yithemes.com/themes/plugins/yith-woocommerce-barcodes-and-qr-codes/';

		/**
		 * Official_documentation
		 *
		 * @var string Plugin official documentation
		 */
		protected $official_documentation = 'https://docs.yithemes.com/yith-woocommerce-barcodes/';

		/**
		 * Premium_live
		 *
		 * @var string Official plugin landing page
		 */
		protected $premium_live = 'https://plugins.yithemes.com/yith-woocommerce-barcodes/';

		/**
		 * Support
		 *
		 * @var string Official plugin support page
		 */
		protected $support = 'https://yithemes.com/my-account/support/dashboard/';

		/**
		 * Panel_page
		 *
		 * @var string Plugin panel page
		 */
		protected $panel_page = 'yith_woocommerce_barcodes_panel';

		/**
		 * Show_product_barcode_on_email
		 *
		 * @var bool set if product barcode should be shown on completed emails
		 */
		public $show_product_barcode_on_email = false;

		/**
		 * Get_instance
		 *
		 * @return instance
		 */
		public static function get_instance() {
			if ( is_null( self::$instance ) ) {
				self::$instance = new self();
			}

			return self::$instance;
		}

		/**
		 * Constructor
		 *
		 * Initialize plugin and registers actions and filters to be used
		 *
		 * @since  1.0
		 */
		protected function __construct() {
			$this->init_settings();

			$this->includes();
		}

		/**
		 * Includes
		 *
		 * @return void
		 */
		public function includes() {
			/** Load external library, if needed */

			require_once YITH_YWBC_INCLUDES_DIR . 'class-yith-ywbc-shortcodes.php';
			require_once YITH_YWBC_INCLUDES_DIR . 'class-yith-ywbc-manage-barcodes.php';

			if ( is_admin() || ( ! empty( $_GET['wc-ajax'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification
				require_once YITH_YWBC_INCLUDES_DIR . 'class-yith-ywbc-backend.php';
			}

			if ( ! is_admin() || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
				require_once YITH_YWBC_INCLUDES_DIR . 'class-yith-ywbc-frontend.php';
			}

			// Elementor Widgets integration.
			if ( defined( 'ELEMENTOR_VERSION' ) ) {
				require_once YITH_YWBC_DIR . 'includes/third-party/elementor/class-ywbc-elementor.php';
			}
		}

		/**
		 * Initialize plugin settings
		 *
		 * @since  1.0.0
		 */
		public function init_settings() {

			$this->enable_on_orders              = 'yes' === get_option( 'ywbc_enable_on_orders', 'no' );
			$this->create_on_orders              = 'yes' === get_option( 'ywbc_create_on_orders', 'no' );
			$this->orders_protocol               = get_option( 'order_barcode_or_qr', 'barcode' ) === 'barcode' ? get_option( 'ywbc_orders_protocol', 'EAN13' ) : 'QRcode';
			$this->show_on_order_page            = $this->enable_on_orders && ( 'yes' === get_option( 'ywbc_show_on_order_page', 'no' ) );
			$this->enable_on_products            = 'yes' === get_option( 'ywbc_enable_on_products', 'no' );
			$this->manual_value_on_products      = 'yes' === get_option( 'ywbc_product_manual_barcode_product', 'no' );
			$this->create_on_products            = 'yes' === get_option( 'ywbc_create_on_products', 'no' );
			$this->products_protocol             = get_option( 'product_barcode_or_qr', 'barcode' ) === 'barcode' ? get_option( 'ywbc_products_protocol', 'EAN13' ) : 'QRcode';
			$this->show_on_product_page          = $this->enable_on_products && ( 'yes' === get_option( 'ywbc_show_on_product_page', 'no' ) );
			$_show_on_emails                     = get_option( 'ywbc_show_on_emails', 'no' );
			$this->show_on_email_completed       = 'completed' === $_show_on_emails;
			$this->show_on_email_all             = 'all' === $_show_on_emails;
			$this->show_product_barcode_on_email = 'yes' === get_option( 'ywbc_show_product_barcode_on_emails', 'no' );
		}

		/**
		 * Create the barcode values for the order
		 *
		 * @param int    $order_id order_id.
		 * @param string $protocol protocol.
		 * @param string $value value.
		 */
		public function create_order_barcode( $order_id, $protocol = '', $value = '' ) {
			$protocol = $protocol ? $protocol : $this->orders_protocol;

			$order = wc_get_order( $order_id );

			$value_option = get_option( 'ywbc_order_barcode_type', 'id' );

			if ( 'number' === $value_option ) {

				/** APPLY_FILTERS: ywbc_order_barcode_type_force_sequential_order_number
				*
				* Filter the condition to force the sequential order number.
				*/
				if ( defined( 'YWSON_INIT' ) && apply_filters( 'ywbc_order_barcode_type_force_sequential_order_number', true ) ) {
					$value_type = $order_id;
				} else {
					$value_type = $order->get_order_number();
				}
			} elseif ( 'custom_field' === $value_option ) {

				$custom_field = get_option( 'ywbc_order_barcode_type_custom_field' );

				if ( 'order_url' === $custom_field && 'QRcode' === $protocol ) {
					$my_account_url = get_permalink( get_option( 'woocommerce_myaccount_page_id' ) );
					$value_type     = $my_account_url . 'view-order/' . $order_id;
				} else {
					$value_type = $order->get_meta( $custom_field );

				}
			} else {
				$value_type = $order_id;
			}

			$the_value = $value ? $value : $value_type;

			/** APPLY_FILTERS: yith_barcode_new_order_value
			*
			* Filter the value of the barcode image when creating the order barcode.
			*
			* @param $the_value Formatted value.
			* @param $order_id  ID of the order.
			* @param $protocol  Current protocol.
			* @param $value     Current value.
			*/
			$the_value = apply_filters( 'yith_barcode_new_order_value', $the_value, $order_id, $protocol, $value );

			$this->generate_barcode_image( $order_id, $protocol, $the_value );
		}

		/**
		 * Retrieve the filename to be used for the barcode file
		 *
		 * @param int    $obj_id   the object id related to the barcode.
		 * @param string $protocol the Barcode protocol to use.
		 * @param string $value    the value to use as barcode text.
		 *
		 * @return string
		 */
		public function get_server_file_path( $obj_id, $protocol, $value ) {

			if ( is_array( $value ) ) {
				return;
			}

			/** APPLY_FILTERS: yith_ywbc_image_filename
			*
			* Filter the file name of the image from the server.
			*
			* @param $image_name Formatted image name (.png by default).
			* @param $obj_id     ID of current obj.
			* @param $protocol   Current protocol.
			* @param $value      Current value.
			*/
			$image_filename = apply_filters( 'yith_ywbc_image_filename', sprintf( 'Image%s_%s_%s.png', $obj_id, $protocol, $value ), $obj_id, $protocol, $value );

			$image_filename = str_replace( '/', '_', $image_filename );
			$image_filename = str_replace( ':', '_', $image_filename );
			$image_filename = str_replace( '?', '_', $image_filename );
			$image_filename = str_replace( '&', '_', $image_filename );

			return YITH_YWBC_UPLOAD_DIR . '/' . $image_filename;
		}

		/**
		 * Retrieve the filename to be used for the barcode file
		 *
		 * @param YITH_Barcode $barcode the barcode instance whose file should be generated.
		 *
		 * @return string
		 */
		public function get_public_file_path( $barcode ) {

			$pos  = strrpos( $barcode->image_filename, '/' );
			$pos2 = strrpos( $barcode->image_filename, '\\' );

			$pos  = ( false === $pos ) ? - 1 : $pos;
			$pos2 = ( false === $pos2 ) ? - 1 : $pos2;

			$last_index = ( $pos > $pos2 ) ? $pos : $pos2;

			$use_url = apply_filters( 'yith_ywbd_use_barcode_image_url', true );

			if ( $last_index ) {
				return ( $use_url ? YITH_YWBC_UPLOAD_URL : YITH_YWBC_UPLOAD_DIR ) . substr( $barcode->image_filename, $last_index );
			}

			// Previous value.
			return str_replace( YITH_YWBC_UPLOAD_DIR, YITH_YWBC_UPLOAD_URL, $barcode->image_filename );
		}

		/**
		 * Generate a new barcode instance
		 *
		 * @param int    $object_id the id of the object(WC_Product or WC_Order) associated to this barcode.
		 * @param string $protocol  the protocol to use.
		 * @param string $value     the value to use as the barcode value.
		 *
		 * @return YITH_Barcode
		 */
		public function generate_barcode_image( $object_id, $protocol, $value ) {
			$barcode = new YITH_Barcode( $object_id );

			$image_path = $this->get_server_file_path( $object_id, $protocol, $value );
			$barcode->generate( $protocol, $value, $image_path );
			$barcode->save();

			return $barcode;
		}

		/**
		 * Create the barcode values for the products
		 *
		 * @param int    $product_id product_id.
		 * @param string $protocol protocol.
		 * @param string $value value.
		 *
		 * @return YITH_Barcode
		 */
		public function create_product_barcode( $product_id, $protocol = '', $value = '' ) {

			$protocol = $protocol ? $protocol : $this->products_protocol;

			$value_option = get_option( 'ywbc_product_barcode_type', 'id' );

			if ( 'sku' === $value_option ) {
				$product    = wc_get_product( $product_id );
				$value_type = $product->get_sku();
			} elseif ( 'custom_field' === $value_option ) {
				$custom_field = get_option( 'ywbc_product_barcode_type_custom_field' );
				$value_type   = get_post_meta( $product_id, $custom_field, true );
			} elseif ( 'product_url' === $value_option && 'QRcode' === $protocol ) {
				$value_type = get_permalink( $product_id );
			} else {
				$value_type = $product_id;
			}

			$the_value = $value ? $value : $value_type;

			/** APPLY_FILTERS: yith_barcode_new_product_protocol
			*
			* Filter the protocol when creating the products barcodes.
			*
			* @param $protocol   Current protocol.
			* @param $the_value  Current value.
			*/
			$protocol = apply_filters( 'yith_barcode_new_product_protocol', $protocol, $the_value );

			/** APPLY_FILTERS: yith_barcode_new_product_value
			*
			* Filter the value when creating the products barcodes.
			*
			* @param $the_value  Default the_value.
			* @param $product_id ID of the product.
			* @param $protocol   Current protocol.
			* @param $value      Current value.
			*/
			$the_value = apply_filters( 'yith_barcode_new_product_value', $the_value, $product_id, $protocol, $value );

			return $this->generate_barcode_image( $product_id, $protocol, $the_value );
		}

		/**
		 * Build an <img> tag with the barcode image rendered from the base64 format
		 *
		 * @param YITH_Barcode $barcode barcode.
		 * @param bool         $show_value Whether to show barcode value or not.
		 * @param string       $layout     Optional layout for the barcode.
		 * @param string       $context    Context of the rendering.
		 *
		 * @return string
		 */
		public function render_barcode( $barcode, $show_value = false, $layout = '', $context = 'default' ) {

			$barcode_value    = $barcode->get_display_value();
			$barcode_protocol = $barcode->get_protocol();

			if ( ! $barcode->get_filename() ) {
				$aux_id = $barcode_protocol . '-' . $barcode_value;
				$this->generate_barcode_image( $aux_id, $barcode_protocol, $barcode_value );
				$barcode->image_filename = $this->get_server_file_path( $aux_id, $barcode_protocol, $barcode_value );
			}

			/** APPLY_FILTERS: yith_ywbc_barcode_src
			*
			* Filter the sources of the barcode when rendering it.
			*
			* @param $barcode_filename      Image path.
			* @param $barcode_display_value Display value.
			* @param $barcode_protocol      Protocol.
			* @param $context               Context of the rendering.
			*/
			$barcode_src = apply_filters(
				'yith_ywbc_barcode_src',
				$barcode->get_filename() ? esc_url( $this->get_public_file_path( $barcode ) ) : 'data:image/png;base64,' . $barcode->image,
				$barcode_value,
				$barcode_protocol,
				$context
			);

			/** APPLY_FILTERS: yith_ywbc_barcode_image_tag
			*
			* Filter the HTML of the entire barcode tag before printing it.
			*
			* @param $barcode_html  Formatted barcode html.
			* @param $barcode       Barcode obj.
			* @param $barcode_value Barcode value.
			* @param $barcode_src   All the sources of the barcode.
			*/
			$barcode_image_tag = apply_filters( 'yith_ywbc_barcode_image_tag', '<img class="ywbc-barcode-image" src="' . $barcode_src . '">', $barcode, $barcode_value, $barcode_src );

			if ( empty( $layout ) ) {
				// Format the image src:  data:{mime};base64,{data};.
				$src = $barcode_image_tag;

				if ( $show_value ) {
					$src .= '<div class="ywbc-barcode-display-container"><span class="ywbc-barcode-display-value">' . $barcode_value . '</span></div>';
				}
			} else {
				$finds    = array(
					'{barcode_image}',
					'{barcode_code}',
				);
				$replaces = array(
					$barcode_image_tag,
					$barcode_value,
				);

				$src = str_replace( $finds, $replaces, $layout );
			}

			/** APPLY_FILTERS: yith_ywbc_render_barcode_html
			*
			* Filter the HTML of the entire barcode before printing it.
			*
			* @param $src           Formatted barcode html.
			* @param $barcode_src   Formatted barcode html.
			* @param $barcode_value Barcode value.
			* @param $barcode       Barcode obj.
			* @param $show_value    Whether to show barcode value or not.
			* @param $layout        Optional layout for the barcode.
			*/
			return apply_filters( 'yith_ywbc_render_barcode_html', $src, $barcode_src, $barcode_value, $barcode, $show_value, $layout );
		}

		/**
		 * Show the barcode text and graphic
		 *
		 * @param int|YITH_Barcode $obj             The object id or specific YITH_Barcode instance.
		 * @param bool             $hide_if_missing Set if the result should be shown even if no barcode exists for the product.
		 * @param string           $inline_css      CSS style to be used(needs CssInliner).
		 * @param string           $layout          HTML to be used to display barcode; it should contain {barcode_image} and or {barcode_code} placeholders.
		 * @param string           $context         Context when showing the barcode.
		 * .
		 * @since  1.0.0
		 */
		public function show_barcode( $obj, $hide_if_missing = false, $inline_css = '', $layout = '', $context = 'default' ) {
			$barcode = is_numeric( $obj ) ? new YITH_Barcode( $obj ) : $obj;

			if ( $barcode instanceof YITH_Barcode ) {
				ob_start();
				?>
				<div id="ywbc_barcode_value">
					<?php
					if ( $barcode->exists() ) {

						/** APPLY_FILTERS: yith_ywbc_render_barcode_html
						*
						* Filter the condition to show the value of the barcode when displaying it.
						*
						* @param $obj           Formatted barcode html.
						*/
						$show_value = apply_filters( 'yith_ywbc_show_value_on_barcode', true, $obj, $context );
						echo wp_kses_post( $this->render_barcode( $barcode, $show_value, $layout, $context ) );
					} elseif ( ! $hide_if_missing ) {
						?>
						<span class="ywbc-no-barcode-value"><?php esc_html_e( 'No barcode is available for this element.', 'yith-woocommerce-barcodes' ); ?></span>
						<?php
					}
					?>
				</div>

				<?php
				$content = ob_get_clean();

				if ( $inline_css ) {
					try {
						if ( ! class_exists( 'CssInliner' ) ) {
							$css_inliner_path = apply_filters( 'yith_ywbc_css_inliner_path', version_compare( WC()->version, '6.5.0', '>=' ) ? 'wp-content/plugins/woocommerce/vendor/pelago/emogrifier/src/CssInliner.php' : 'wp-content/plugins/woocommerce/vendor/pelago/emogrifier/src/Emogrifier/CssInliner.php' );

							include_once ABSPATH . $css_inliner_path;
						}
						$cssinliner_class = \Pelago\Emogrifier\CssInliner::class;
						$content          = $cssinliner_class::fromHtml( $content )->inlineCss( $inline_css )->render();

					} catch ( Exception $e ) {
						$logger = new WC_Logger();
						$logger->add( 'CssInliner', $e->getMessage() );
					}
				}
			}

			if ( $content ) {
				echo wp_kses_post( $content );
			}
		}
	}
}
