<?php
/**
 * Please see weepie-framework.php for more details.
 */

namespace WpieFw\Multilang;

use WpieFw\Wpie\WpieGlobals;
use WpieFw\Notices\WpieNotices;
use WpieFw\Helpers\WpieMultilangHelper;
use WpieFw\Settings\WpieSettingsProcessor;
use WpieFw\Multilang\WpieMultilangValues;
use WpieFw\Multilang\Plugins\WpieMultilangPluginInterface;
use WpieFw\Exceptions\WpieRuntimeException;
use WpieFw\Exceptions\WpieExceptionInterface;

if( ! defined( 'ABSPATH' ) ) exit;

final class WpieMultilangProcessor
{

	/**
	 * @var WpieMultilangPluginInterface
	 */
	private $plugin;

	/**
	 * @since 2.0
	 *
	 * @var WpieMultilangValues
	 */
	private $values;

	/**
	 * WpieGlobals instance
	 *
	 * @since 1.4.0
	 *
	 * @var WpieGlobals
	 */
	private $globals;

	/**
	 * Flag if plugin is activated
	 *
	 * @since 1.2
	 *
	 * @var bool
	 */
	public $isPluginActive = false;

	/**
	 * Flag if plugin setup is done
	 *
	 * @since 1.2
	 *
	 * @var bool
	 */
	public $isPluginReady = false;

	/**
	 * Flag if settings are langueged
	 *
	 * @since 1.2
	 *
	 * @var bool
	 */
	public $isLanguaged = false;

	/**
	 * Flag if current active lang is 'all'
	 *
	 * @since 1.2
	 *
	 * @var bool
	 */
	public $isAll = false;

	/**
	 * The default language code
	 *
	 * @since 1.2
	 *
	 * @var string
	 */
	public $defaultCode = '';

	/**
	 * The default language locale
	 *
	 * @since 1.2
	 *
	 * @var string
	 */
	public $defaultLocale = '';

	/**
	 * The active language code
	 *
	 * i.e. en
	 *
	 * @since 1.2
	 *
	 * @var string
	 */
	public $activeCode = '';

	/**
	 * The active language locale
	 *
	 * // former activeLang
	 *
	 * i.e. en_US
	 *
	 * @since 1.2
	 *
	 * @var string
	 */
	public $activeLocale = '';

	/**
	 * All Available language codes
	 *
	 * NOTE: the code => locale pairs are stored in self::OPTION_NAME_LANGUAGES
	 *
	 * @since 1.2
	 *
	 * @var array
	 */
	public $codes = [];

	/**
	 * All Available locales
	 *
	 * @since 1.2
	 *
	 * @var array
	 */
	public $locales = [];

	/**
	 * Flag if a multiple locales are present
	 *
	 * @var bool
	 */
	public $hasMultipleLocales = false;

	/**
	 * All Available language codes stored in the wpie_languages option
	 *
	 * Langs are stored as code => locale entries
	 *
	 * @since 1.2
	 *
	 * @var array
	 */
	public $wpieLocales = [];

	public $removedCodes = [];

	public $newCodes = [];

	public $hasRemovedLang = false;

	public $hasNewLang = false;


	public function __construct( WpieMultilangPluginInterface $plugin, WpieMultilangValues $values, WpieGlobals $globals )
	{
		$this->plugin = $plugin;
		$this->values = $values;
		$this->globals = $globals;

		// fall back in case no multi lang plugin is active
		$this->activeLocale =  $this->globals->get( 'locale' );
		$this->activeCode = WpieMultilangHelper::mapLocaleToCode( $this->activeLocale );
	}

	/**
	 * When activating, update the wpie languages
	 *
	 * Save the current languages to the wp_options table
	 *
	 * @access public
	 *
	 * @uses WpieMultilangValues::delete()
	 * @uses WpieMultilangValues::persist()
	 *
	 * @since 1.2
	 */
	public function activatePlugin()
	{
		if( !$this->isPluginReady ) {
			$this->values->delete();
		} else {
			$this->values->persist( $this->locales );
		}
	}

	/**
	 * @param WpieSettingsProcessor $settingsProcessor
	 */
	public function afterInitSettings( WpieSettingsProcessor $settingsProcessor )
	{
		if( $settingsProcessor->settings->hasCollection() ) {
			$settingNames = $settingsProcessor->settings->getSettingNames();

			// @TODO test loop for WpieSettingsCollection
			foreach ( $settingsProcessor->settings as $setting ) {
				$this->isLanguaged = $setting->isLanguaged();
				break;
			}

			$this->wpieLocales = $this->values->getLanguages();

			if( empty( $this->wpieLocales ) && $this->isLanguaged ) {
				$keys = array_keys( $setting );
				if( in_array( $this->activeLocale, $keys ) ) {
					$this->wpieLocales[$this->activeCode] = $this->activeLocale;
					$this->values->persist( $this->wpieLocales );
				}
			}
		}

		// @todo need this sync method?
		if( $this->hasRemovedLang || $this->hasNewLang ) {
			$this->sync( $settingsProcessor );
		}
	}

	/**
	 * @param WpieSettingsProcessor $settingsProcessor
	 */
	public function afterInitSettingsPage( WpieSettingsProcessor $settingsProcessor )
	{
		if( $this->plugin->isReady() && $settingsProcessor->settingsPage->onSettingsPage ) {
			$this->plugin->doSettingsPageHooks( $this->globals );
		}
	}

	public function init()
	{
		// retrieve language data from the plugin specific class
		$params = $this->plugin->getParams();

		$this->isPluginActive = $params->isActive;
		if( $this->isPluginActive ) {
			$this->isPluginReady = $params->isReady;
		}

		// When activating and multilang plugin is NOT ready, delete the option
		if( ( $this->globals->get( 'isActivating' ) && !$this->isPluginReady ) || !$this->isPluginActive ) {
			$this->values->delete();
			return false;
		}

		if( $this->isPluginActive && $this->isPluginReady ) {

			/*
			The $params parameter should be an object as follows:

			$params->isActive
			$params->isReady
			$params->defaultCode
			$params->defaultLocale
			$params->activeCode
			$params->activeLocale
			$params->allCodes
			$params->allLocales
			*/

			$this->defaultCode = $params->defaultCode;
			$this->defaultLocale = $params->defaultLocale;
			$this->activeCode = $params->activeCode;
			$this->activeLocale = $params->activeLocale;
			$this->codes = $params->allCodes;
			$this->locales = $params->allLocales; // indexed by language code

			// locales are only useful if more than 1
			if( 1 < count( $this->locales ) ) {
				$this->hasMultipleLocales = true;
			}

			// @todo: fix termonology codes vs langs
			$this->wpieLocales = $this->values->getLanguages();
			$this->isAll = ( 'all' === $params->activeCode ) ? true : false;

			if( $this->isAll ) {
				// if the current active language (code) is 'all',
				// set the params to the default language
				// if activating, switch the language

				if( $this->globals->get( 'isActivating' ) || get_option( $this->globals->get( 'optionActivating' ) ) ) {
					// no $code switches to default
					$this->plugin->switchLanguage();
				}

				$this->activeCode = $params->defaultCode;
				$this->activeLocale = $params->defaultLocale;
			} else {
				if( !empty( $this->wpieLocales ) ) {
					$exist = $this->getLangSyncStatusses( $this->wpieLocales );

					$removed = array_keys( $exist, 2 );
					if( !empty( $removed ) ) {
						$this->removedCodes = $removed;
						$this->hasRemovedLang = true;
					}
					$new = array_keys( $exist, 0, true);
					if( !empty( $new ) ) {
						$this->newCodes = $new;
						$this->hasNewLang = true;
					}
				} else {
					if( !empty( $this->locales ) ) {
						$this->values->persist( $this->locales );
					}
				}
			}
		}

		return true;
	}

	/**
	 * Get sync statuses for each language
	 *
	 * The languages stored in the wp_options table (self::OPTION_NAME_LANGUAGES)
	 * are compared to against the available languages from the current plugin.
	 *
	 * The following cases are asigned to each plugin language:
	 *
	 * 		0: lang does not exist (new)
	 * 		1: lang already exist
	 * 		2: lang has been removed
	 *
	 * @acces private
	 *
	 * @param array $wpieLocales
	 *
	 * @since 1.2
	 *
	 * @return array
	 */
	private function getLangSyncStatusses( $wpieLocales )
	{
		if( empty( $this->locales ) ) {
			return [];
		}

		$exist = [];
		foreach ( $this->locales as $code => $lang ) {
			$exist[$code] = 0;
		}

		foreach ( $exist as $code => $status ) {
			if( array_key_exists( $code, $wpieLocales ) ) {
				// lang exists already
				$exist[$code] = 1;
			} else {
				// lang does not exist
			}
		}

		// languages that exist in the old array but not in the current are labled with 2,
		// they can be removed.
		foreach ( $wpieLocales as $wpieCode => $wpieLocale ) {
			if( !array_key_exists( $wpieCode, $exist ) ) {
				$exist[$wpieCode] = 2;
			}
		}

		return $exist;
	}

	/**
	 * Sync the settings languages with active multilang plugin languages
	 *
	 * @access protected
	 *
	 * @param WpieSettingsProcessor $settingsProcessor
	 *
	 * @throws WpieRuntimeException if unknown code is found
	 *
	 * @since 1.2
	 */
	private function sync( WpieSettingsProcessor $settingsProcessor )
	{
		try {
			if( !$this->isLanguaged ) {
				return;
			}

			$defaultLocale = null;
			$newCodes = [];
			$settings = [];

			// get the languages as stored in the wp_options table
			$wpieLocales = $this->values->getLanguages();

			$exist = $this->getLangSyncStatusses( $wpieLocales );
			if( isset( $exist['all'] ) ) {
				unset($exist['all']);
			}

			if( !in_array( 0 , $exist, true ) && !in_array( 2 , $exist ) ) {
				WpieNotices::add( $this->globals->nameSpace, __( 'All langugaes are in sync. Nothing to sync.', 'weepie' ), 'success' );
				return;
			}

			foreach ( $this->locales as $code => $locale ) {
				$newCodes[$code] = $locale;
			}

			if( $this->hasNewLang || $this->hasRemovedLang ) {
				// update the language entry in the wp_options table
				$this->values->persist( $newCodes );
			}

			// loop through all settings
			// @todo need namespace here?
			foreach ( $settingsProcessor->settings as $index => $setting ) {
				$option = $setting->getStored();
				$optionNew = $option;

				// TODO: Fix when configuring WPML and stored settings are not languaged
				// $option has no locales, but $setting->isLanguaged() === true
				// -> add a check of stored settings is also Languaged!
				if( $setting->isLanguaged() ) {
					$defaultLocaleExist = array_key_exists( $this->defaultLocale, $option );

					if( $defaultLocaleExist ) {
						$defaultStack = $option[$this->defaultLocale];
					} else {
						$keys = array_keys( $option );
						$firstLocale = array_shift( $keys );
						$defaultStack = $option[$firstLocale];
					}
				} else {
					$defaultStack = $setting->getStack();
				}

				foreach( $exist as $code => $action ) {
					if( 0 === $action ) {
						$locale = WpieMultilangHelper::mapCodeToLocale( $code, $this->locales );
						$optionNew[$locale] = $defaultStack;
						//remove_action( 'set_object_terms', [ 'WPML_Terms_Translations', 'set_object_terms_action' ], 10 );
					} elseif( 1 === $action ) {
						continue;
					} elseif( 2 === $action ) {
						$locale = $wpieLocales[$code];
						unset( $optionNew[$locale] );
					} else {
						throw new WpieRuntimeException( sprintf( 'Parameter $action with value "%d" is not valid.', $action ) );
					}

					/**
					 * Filter the options array before updating
					 *
					 * Let other modules hook into the process of syncing
					 *
					 * @param array $optionNew
					 * @param string $index
					 * @param string $code
					 * @param string $defaultCode
					 * @param string $locale
					 * @param int $action
					 *
					 * @since 2.2.7
					 */
					$optionNew = apply_filters( $this->globals->nameSpace . '_wpml_sync_option_for_locale', $optionNew, $index, $code, $this->defaultCode, $locale, $action );
				}

				$setting->persistWith( $optionNew );
				unset( $optionNew, $option );
			}
		} catch ( WpieExceptionInterface $e ) {
			WpieNotices::add( $this->globals->nameSpace, $e->getMessage(), 'error' );
		}
	}
}