<?php
/**
 * @TODO
 */

if ( ! class_exists( 'FooGallery_Extensions_API' ) ) {

	define( 'FOOGALLERY_EXTENSIONS_MESSAGE_TRANSIENT_KEY', 'foogallery_extensions_message' );
	define( 'FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY', 'foogallery_extensions_activated' );
	define( 'FOOGALLERY_EXTENSIONS_ERRORS_OPTIONS_KEY', 'foogallery_extensions_errors' );
	define( 'FOOGALLERY_EXTENSIONS_SLUGS_OPTIONS_KEY', 'foogallery_extensions_slugs' );
	define( 'FOOGALLERY_EXTENSIONS_AUTO_ACTIVATED_OPTIONS_KEY', 'foogallery_extensions_auto_activated' );

	/**
	 * Foogalolery Extensions Manager Class
	 * Class FooGallery_Extensions_API
	 */
	class FooGallery_Extensions_API {

		/**
		 * Internal list of all extensions
		 * @var array
		 */
		private $extensions = false;

		/**
		 * Internal list of all extension slugs
		 * @var array
		 */
		private $extension_slugs = false;

		/**
		 * Extension API constructor
		 * @param bool $load
		 */
		function __construct( $load = false ) {
			if ( $load ) {
				$this->load_available_extensions();
			}
		}

		/**
		 * Reset all previous errors
		 */
		public function reset_errors() {
			delete_option( FOOGALLERY_EXTENSIONS_LOADING_ERRORS );
			delete_option( FOOGALLERY_EXTENSIONS_LOADING_ERRORS_RESPONSE );
		}

		/**
		 * Load all available extensions
		 */
		private function load_available_extensions() {
			$this->extensions = array();

			$this->extensions[] =	array(
				'slug' => 'albums',
				'class' => 'FooGallery_Albums_Extension',
				'title' => 'Albums',
				'categories' =>	array( 'Featured', 'Free' ),
				'description' => 'Group your galleries into albums. Boom!',
				'html' => 'Group your galleries into albums. Boom!',
				'author' => 'FooPlugins',
				'author_url' => 'https://fooplugins.com',
				'thumbnail' => '/extensions/albums/foogallery-albums.png',
				'tags' => array( 'functionality' ),
				'source' => 'bundled'
			);

			//FooBox premium
			$this->extensions[] = array(
				'slug' => 'foobox',
				'class' => 'FooGallery_FooBox_Extension',
				'categories' => array( 'Featured', 'Premium' ),
				'file' => 'foobox-free.php',
				'title' => 'FooBox PRO',
				'description' => 'The best lightbox for WordPress just got even better!',
				'price' => '$27',
				'author' => 'FooPlugins',
				'author_url' => 'https://fooplugins.com',
				'thumbnail' => 'https://s3.amazonaws.com/foogallery/extensions/foobox.png',
				'tags' => array( 'premium', 'lightbox', ),
				'source' => 'fooplugins',
				'download_button' =>
					array(
						'text' => 'Buy - $29',
						'target' => '_blank',
						'href' => 'https://fooplugins.com/foobox',
						'confirm' => false,
					),
				'activated_by_default' => true,
				'remove_if_active' => array('foobox-image-lightbox')
			);

			//FooBox lightbox
			$this->extensions[] = array (
				'slug' => 'foobox-image-lightbox',
				'class' => 'FooGallery_FooBox_Free_Extension',
				'categories' => array( 'Featured', 'Free', ),
				'file' => 'foobox-free.php',
				'title' => 'FooBox FREE',
				'description' => 'The best lightbox for WordPress. Free',
				'author' => 'FooPlugins',
				'author_url' => 'https://fooplugins.com',
				'thumbnail' => 'https://s3.amazonaws.com/foogallery/extensions/foobox_free.png',
				'tags' => array( 'lightbox' ),
				'source' => 'repo',
				'activated_by_default' => true,
				'minimum_version' => '1.0.2.1',
			);

			//The NextGen importer
			$this->extensions[] = array(
				'slug' => 'nextgen',
				'class' => 'FooGallery_Nextgen_Gallery_Importer_Extension',
				'categories' => array( 'Free' ),
				'title' => 'NextGen Importer',
				'description' => 'Imports all your existing NextGen galleries',
				'author' => 'FooPlugins',
				'author_url' => 'https://fooplugins.com',
				'thumbnail' => 'https://s3.amazonaws.com/foogallery/extensions/nextgen_importer.png',
				'tags' => array( 'tools' ),
				'source' => 'bundled',
			);
		}

		/**
		 * Get all loaded extensions
		 * @return array
		 */
		function get_all() {

			//check if we need to load
			if ( false === $this->extensions ) {
				$this->load_available_extensions();
			}

			//get any extra extensions from plugins
			$extra_extensions = apply_filters( 'foogallery_available_extensions', array() );

			if ( count( $extra_extensions ) > 0 ) {
				//get a list of slugs so we can determine duplicates!
				$this->extension_slugs = array();
				foreach ( $this->extensions as $extension ) {
					$this->extension_slugs[] = $extension['slug'];
				}

				//only add if not a duplicate
				foreach ( $extra_extensions as $extension ) {
					if ( ! in_array( $extension['slug'], $this->extension_slugs ) ) {
						$this->extensions[] = $extension;
					}
				}
			}

			return $this->extensions;
		}


		/**
		 * return a list of all extensions for the extension view.
		 * This list could be changed based on other plugin
		 */
		function get_all_for_view() {
			$all_extensions = $this->get_all();
			$extensions = array();
			$active_extensions = array();

			//add all extensions to an array using the slug as the array key
			foreach ( $all_extensions as &$extension ) {
				$active = $this->is_active( $extension['slug'], true );
				$extension['downloaded'] = $active || $this->is_downloaded( $extension );
				$extension['is_active'] = $active;
				$extension['has_errors'] = $this->has_errors( $extension['slug'] );

				//build up a list of active extensions
				if ( $active ) {
					$active_extensions[$extension['slug']] = $extension;
				}

				//remove any bundled extensions that are activated_by_default = true
				if ( isset( $extension['activated_by_default'] ) &&
					true === $extension['activated_by_default'] &&
					isset( $extension['source'] ) &&
					'bundled' === $extension['source']) {
					//do not include a bundled extension that is activated by default
				} else {
					$extensions[ $extension['slug'] ] = $extension;
				}
			}

			//loop through all active extensions and remove any other extensions if required based on the 'remove_if_active' property
			foreach ( $active_extensions as $active_extension_slug => $active_extension ) {
				//check if we need to remove any other extensions from the list
				if ( isset( $active_extension['remove_if_active'] ) ) {

					foreach ( $active_extension['remove_if_active'] as $extension_slug_to_remove ) {
						if ( array_key_exists( $extension_slug_to_remove, $extensions ) ) {
							unset( $extensions[ $extension_slug_to_remove ] );
						}
					}
				}
			}

			$extensions = apply_filters( 'foogallery_extensions_for_view', $extensions );

			return $extensions;
		}
		/**
		 * Get all loaded extensions slugs
		 * @return array
		 */
		function get_all_slugs() {
			//load all extensions first!
			$this->get_all();

			return $this->extension_slugs;
		}

		/**
		 * @TODO
		 * @param $slug
		 *
		 * @return bool
		 */
		public function get_extension( $slug ) {
			foreach ( $this->get_all() as $extension ) {
				if ( $extension['slug'] === $slug ) {
					return $extension;
				}
			}
			return false;
		}

		/**
		 * @TODO
		 * @param $file
		 *
		 * @return bool
		 */
		public function get_extension_by_file( $file ) {
			$file = basename( $file ); //normalize to just the filename

			foreach ( $this->get_all() as $extension ) {
				if ( foo_safe_get( $extension, 'file' ) === $file ) {
					return $extension;
				}
			}
			return false;
		}

		/**
		 * @TODO
		 * @param      $slug
		 *
		 * @return bool
		 */
		public function is_active( $slug, $perform_active_check = false ) {
			$active_extensions = $this->get_active_extensions();
			if ( array_key_exists( $slug, $active_extensions ) ) {
				//it has been previously activated through the extensions page
				return true;
			}

			if ( $perform_active_check ) {
				$extension = $this->get_extension( $slug );

				//if we have an 'plugin_active_class' attribute and that class exists, it means our plugin must be active
				if ( isset( $extension['plugin_active_class'] ) ) {
					if ( class_exists( $extension['plugin_active_class'] ) ) {
						return true;
					}
				}

				//if we cannot find the extension class in memory, then check to see if the extension plugin is activated
				if ( isset( $extension['perform_plugin_active_check'] ) && true === $extension['perform_plugin_active_check'] &&
					isset( $extension['file'] ) ) {
					$plugin = $this->find_active_wordpress_plugin( $extension );

					return $plugin !== false;
				}
			}

			return false;
		}

		/**
		 * @TODO
		 *
		 * @param bool $extension
		 *
		 * @param bool $slug
		 *
		 * @return bool
		 */
		public function is_downloaded( $extension = false, $slug = false ) {
			//allow you to pass in a slug rather
			if ( ! $extension && $slug !== false ) {
				$extension = $this->get_extension( $slug );
			}
			if ( $extension ) {
				//first check if the class exists
				if ( class_exists( $extension['class'] ) ) {
					return true;
				}

				//next fallback to see if a plugin exists that has the same file name
				$plugin = $this->find_wordpress_plugin( $extension );
				return false !== $plugin;
			}
			return false;
		}

		/**
		 * @TODO
		 * @param $slug
		 *
		 * @return bool
		 */
		public function has_errors( $slug ) {
			$error_extensions = $this->get_error_extensions();

			if ( $error_extensions ) {
				return array_key_exists( $slug, $error_extensions );
			}
			return false;
		}

		/**
		 * @TODO
		 * @param $plugin
		 */
		public function handle_wordpress_plugin_deactivation( $plugin ) {
			$extension = $this->get_extension_by_file( $plugin );
			if ( $extension ) {
				//we have found a matching extension
				$this->deactivate( $extension['slug'], false );
			}
		}

		/**
		 * @TODO
		 * @param $plugin
		 */
		public function handle_wordpress_plugin_activation( $plugin ) {
			$extension = $this->get_extension_by_file( $plugin );
			if ( $extension ) {
				//we have found a matching extension
				$this->activate( $extension['slug'], false );
			}
		}

		/**
		 * @TODO
		 * @param      $slug
		 * @param bool $deactivate_wordpress_plugin
		 * @param bool $error_loading
		 *
		 * @return array|mixed|void
		 */
		public function deactivate( $slug, $deactivate_wordpress_plugin = true, $error_loading = false ) {
			$extension = $this->get_extension( $slug );
			if ( $extension ) {
				if ( $deactivate_wordpress_plugin && 'bundled' !== foo_safe_get( $extension, 'source', false ) ) {
					$plugin = $this->find_wordpress_plugin( $extension );
					if ( $plugin ) {
						$failure = deactivate_plugins( $plugin['file'], true, false );
						if ( null !== $failure ) {
							return array(
								'message' => sprintf( __( 'The extension %s could NOT be deactivated!', 'foogallery' ), "<strong>{$extension['title']}</strong>" ),
								'type' => 'error'
							);
						}
					}
				}

				$active_extensions = $this->get_active_extensions();
				if ( array_key_exists( $slug, $active_extensions ) ) {
					unset( $active_extensions[ $slug ] );
					if ( empty($active_extensions) ) {
						delete_option( FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY );
					} else {
						update_option( FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY, $active_extensions );
					}
				}

				if ( $error_loading ) {
					$this->add_to_error_extensions( $slug );
				}

				//we are done, allow for extensions to do something after an extension is activated
				do_action( 'foogallery_extension_deactivated-' . $slug );

				return apply_filters( 'foogallery_extensions_deactivated_message-' . $slug, array(
					'message' => sprintf( __( 'The extension %s was successfully deactivated', 'foogallery' ), "<strong>{$extension['title']}</strong>" ),
					'type' => 'success',
				) );
			}
			return array(
				'message' => sprintf( __( 'Unknown extension : %s', 'foogallery' ), $slug ),
				'type' => 'error',
			);
		}

		/**
		 * @TODO
		 *
		 * @param      $slug
		 * @param bool $activate_wordpress_plugin
		 *
		 * @return array|mixed|void
		 */
		public function activate( $slug, $activate_wordpress_plugin = true ) {
			$extension = $this->get_extension( $slug );
			if ( $extension ) {
				//first remove it from our error list (if it was there before)
				$this->remove_from_error_extensions( $slug );

				if ( $activate_wordpress_plugin && 'bundled' !== foo_safe_get( $extension, 'source', false ) ) {
					//activate the plugin, WordPress style!
					$plugin = $this->find_wordpress_plugin( $extension );

					if ( $plugin ) {

						//check min version
						$minimum_version = foo_safe_get( $extension, 'minimum_version' );
						if ( !empty($minimum_version) ) {
							$actual_version = $plugin['plugin']['Version'];
							if ( version_compare( $actual_version, $minimum_version ) < 0 ) {
								$this->add_to_error_extensions( $slug, sprintf( __( 'Requires %s version %s','foogallery' ), $extension['title'], $minimum_version ) );
								return array(
									'message' => sprintf( __( 'The extension %s could not be activated, because you are using an outdated version! Please update %s to at least version %s.', 'foogallery' ), $extension['title'], $extension['title'], $minimum_version ),
									'type' => 'error',
								);
							}
						}

						//try to activate the plugin
						$failure = activate_plugin( $plugin['file'], '', false, false );
						if ( null !== $failure ) {
							return array(
								'message' => sprintf( __( 'The extension %s could NOT be activated!', 'foogallery' ), "<strong>{$extension['title']}</strong>" ),
								'type' => 'error',
							);
						}
					}
				}
				//load an instance of the extension class into memory
				$loader = new FooGallery_Extensions_Loader();
				$loader->load_extension( $slug, foo_safe_get( $extension, 'class', false ) );

				//then add the extension to our saved option so that it can be loaded on startup
				$this->add_to_activated_extensions( $extension );

				//we are done, allow for extensions to do something after an extension is activated
				do_action( 'foogallery_extension_activated-' . $slug );

				//return our result
				return apply_filters( 'foogallery_extension_activated_message-' . $slug, array(
					'message' => sprintf( __( 'The extension %s was successfully activated', 'foogallery' ), "<strong>{$extension['title']}</strong>" ),
					'type' => 'success',
				) );
			}
			return array(
				'message' => sprintf( __( 'Unknown extension : %s', 'foogallery' ), $slug ),
				'type' => 'error',
			);
		}

		/**
		 * @TODO
		 * @param boolean $extension
		 *
		 * @return array|bool
		 */
		private function find_wordpress_plugin( $extension ) {
			$plugins = get_plugins();
			foreach ( $plugins as $plugin_file => $plugin ) {
				if ( isset($extension['file']) && foo_ends_with( $plugin_file, $extension['file'] ) ) {
					return array(
						'file' => $plugin_file,
						'plugin' => $plugin,
						'active' => is_plugin_active( $plugin_file ),
					);
				}
			}
			return false;
		}

		/**
		 * @TODO
		 * @param boolean $extension
		 *
		 * @return array|bool
		 */
		private function find_active_wordpress_plugin( $extension ) {
			$plugins = get_plugins();
			foreach ( $plugins as $plugin_file => $plugin ) {
				if ( is_plugin_active( $plugin_file ) && isset($extension['file']) && foo_ends_with( $plugin_file, $extension['file'] ) ) {
					return array(
						'file' => $plugin_file,
						'plugin' => $plugin
					);
				}
			}
			return false;
		}

		/**
		 * @TODO
		 * @param $slug
		 *
		 * @return array|mixed|void
		 */
		public function download( $slug ) {
			$extension = $this->get_extension( $slug );
			if ( $extension ) {

				//we need some files!
				require_once ABSPATH . 'wp-admin/includes/plugin-install.php'; // plugins_api calls
				require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; // Plugin_Upgrader class
				require_once FOOGALLERY_PATH . 'includes/admin/class-silent-installer-skin.php'; //our silent installer skin

				$download_link = isset( $extension['download_link'] ) ? $extension['download_link'] : false;

				if ( 'repo' === $extension['source'] ) {
					$plugins_api = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ) ) );

					if ( is_wp_error( $plugins_api ) ) {
						return array(
							'message' => sprintf( __( 'Unable to connect to the WordPress.org plugin API to download %s. Full error log: %s', 'foogallery' ), $slug,  '<br />' . var_export( $plugins_api, true ) ),
							'type' => 'error',
						);
					}

					//get the download link from the API call
					if ( isset( $plugins_api->download_link ) ) {
						$download_link = $plugins_api->download_link;
					}
				}

				//check we have something to download
				if ( empty( $download_link ) ) {
					return array(
						'message' => sprintf( __( 'The extension %s has no download link!', 'foogallery' ), $slug ),
						'type' => 'error',
					);
				}

				$skin = new FooGallery_Silent_Installer_Skin();

				//instantiate Plugin_Upgrader
				$upgrader = new Plugin_Upgrader( $skin );

				$upgrader->install( $download_link );

				if ( 'process_failed' === $skin->feedback ) {
					$error_message = is_wp_error( $skin->result ) ? $skin->result->get_error_message() : __( 'Unknown!', 'foogallery' );

					//save the error message for the extension
					$this->add_to_error_extensions( $slug, sprintf( __('Could not be downloaded! Error : %s', 'foogallery' ), $error_message ) );

					//we had an error along the way
					return apply_filters( 'foogallery_extensions_download_failure-' . $slug, array(
						'message' => sprintf( __( 'The extension %s could NOT be downloaded! Error : %s', 'foogallery' ), "<strong>{$extension['title']}</strong>", $error_message ),
						'type' => 'error'
					) );
				}

				//return our result
				return apply_filters( 'foogallery_extensions_download_success-' . $slug, array(
					'message' => sprintf( __( 'The extension %s was successfully downloaded and can now be activated. %s', 'foogallery' ),
						"<strong>{$extension['title']}</strong>",
						'<a href="' . esc_url( add_query_arg( array(
								'action' => 'activate',
								'extension' => $slug, ) ) ) . '">' . __( 'Activate immediately', 'foogallery' ) . '</a>'
					),
					'type' => 'success',
				) );
			}
			return array(
				'message' => sprintf( __( 'Unknown extension : %s', 'foogallery' ), $slug ),
				'type' => 'error',
			);
		}

		/**
		 * @TODO
		 * @return mixed|void
		 */
		public function get_active_extensions() {
			//should we not rather get back all plugins that are active?
			return get_option( FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY, array() );
		}

		/**
		 * @TODO
		 * @return mixed|void
		 */
		public function get_error_extensions() {
			return get_option( FOOGALLERY_EXTENSIONS_ERRORS_OPTIONS_KEY, array() );
		}

		public function get_error_message( $slug ) {
			$error_extensions = $this->get_error_extensions();
			if ( array_key_exists( $slug, $error_extensions ) ) {
				return $error_extensions[ $slug ];
			}
			return '';
		}

		/**
		 * @TODO
		 * @param $extension
		 */
		private function add_to_activated_extensions( $extension ) {
			$slug = $extension['slug'];
			$active_extensions = $this->get_active_extensions();
			if ( !array_key_exists( $slug, $active_extensions ) ) {
				$active_extensions[ $slug ] = $extension['class'];
				update_option( FOOGALLERY_EXTENSIONS_ACTIVATED_OPTIONS_KEY, $active_extensions );
			}
		}

		/**
		 * @TODO
		 * @param $slug
		 */
		public function add_to_error_extensions( $slug, $error_message = '' ) {
			$error_extensions = $this->get_error_extensions();

			if ( empty($error_message) ) {
				$error_message = __( 'Error loading extension!', 'foogallery' );
			}

			if ( array_key_exists( $slug, $error_extensions ) &&
				$error_message === $error_extensions[$slug]) {
				//do nothing!
			} else {
				$error_extensions[$slug] = $error_message;
				update_option( FOOGALLERY_EXTENSIONS_ERRORS_OPTIONS_KEY, $error_extensions );
			}
		}

		/**
		 * @TODO
		 * @param $slug
		 */
		private function remove_from_error_extensions( $slug ) {
			$error_extensions = $this->get_error_extensions();
			if ( array_key_exists( $slug, $error_extensions ) ) {
				unset( $error_extensions[ $slug ] );
				update_option( FOOGALLERY_EXTENSIONS_ERRORS_OPTIONS_KEY, $error_extensions );
			}
		}

		/**
		 * @TODO
		 */
		public function auto_activate_extensions() {
			foreach ( $this->get_all() as $extension ) {
				if ( true === foo_safe_get( $extension, 'activated_by_default' ) ) {
					//check to see if the extension is downloaded
					if ( $this->is_downloaded( $extension ) ) {
						$this->add_to_activated_extensions( $extension );
					}
				}
			}
		}
	}
}
