<?php
/**
 * @package     Freemius
 * @copyright   Copyright (c) 2015, Freemius, Inc.
 * @license     https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
 * @since       1.0.3
 */

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

/**
 * 3-layer lazy options manager.
 *      layer 3: Memory
 *      layer 2: Cache (if there's any caching plugin and if WP_FS__DEBUG_SDK is FALSE)
 *      layer 1: Database (options table). All options stored as one option record in the DB to reduce number of DB
 *      queries.
 *
 * If load() is not explicitly called, starts as empty manager. Same thing about saving the data - you have to
 * explicitly call store().
 *
 * Class Freemius_Option_Manager
 */
class FS_Option_Manager {
	/**
	 * @var string
	 */
	private $_id;
	/**
	 * @var array
	 */
	private $_options;
	/**
	 * @var FS_Logger
	 */
	private $_logger;

	/**
	 * @var FS_Option_Manager[]
	 */
	private static $_MANAGERS = array();

	/**
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.3
	 *
	 * @param string $id
	 * @param bool $load
	 */
	private function __construct( $id, $load = false ) {
		$this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_opt_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );

		$this->_logger->entrance();
		$this->_logger->log( 'id = ' . $id );

		$this->_id = $id;

		if ( $load ) {
			$this->load();
		}
	}

	/**
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.3
	 *
	 * @param $id
	 * @param $load
	 *
	 * @return FS_Option_Manager
	 */
	static function get_manager( $id, $load = false ) {
		$id = strtolower( $id );

		if ( ! isset( self::$_MANAGERS[ $id ] ) ) {
			self::$_MANAGERS[ $id ] = new FS_Option_Manager( $id, $load );
		} // If load required but not yet loaded, load.
		else if ( $load && ! self::$_MANAGERS[ $id ]->is_loaded() ) {
			self::$_MANAGERS[ $id ]->load();
		}

		return self::$_MANAGERS[ $id ];
	}

	private function _get_option_manager_name() {
//			return WP_FS__SLUG . '_' . $this->_id;
		return $this->_id;
	}

	/**
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.3
	 *
	 * @param bool $flush
	 */
	function load( $flush = false ) {
		$this->_logger->entrance();

		$option_name = $this->_get_option_manager_name();

		if ( $flush || ! isset( $this->_options ) ) {
			if ( isset( $this->_options ) ) {
				// Clear prev options.
				$this->clear();
			}

			if ( ! WP_FS__DEBUG_SDK ) {
				$this->_options = wp_cache_get( $option_name, WP_FS__SLUG );
			}

//				$this->_logger->info('wp_cache_get = ' . var_export($this->_options, true));

//				if ( is_array( $this->_options ) ) {
//					$this->clear();
//				}

			$cached = true;

			if ( empty( $this->_options ) ) {
				$this->_options = get_option( $option_name );

				if ( is_string( $this->_options ) ) {
					$this->_options = json_decode( $this->_options );
				}

//					$this->_logger->info('get_option = ' . var_export($this->_options, true));

				if ( false === $this->_options ) {
					$this->clear();
				}

				$cached = false;
			}

			if ( ! WP_FS__DEBUG_SDK && ! $cached ) // Set non encoded cache.
			{
				wp_cache_set( $option_name, $this->_options, WP_FS__SLUG );
			}
		}
	}

	/**
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.3
	 *
	 * @return bool
	 */
	function is_loaded() {
		return isset( $this->_options );
	}

	/**
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.3
	 *
	 * @return bool
	 */
	function is_empty() {
		return ( $this->is_loaded() && false === $this->_options );
	}

	/**
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.6
	 *
	 * @param bool $flush
	 */
	function clear( $flush = false ) {
		$this->_logger->entrance();

		$this->_options = array();

		if ( $flush ) {
			$this->store();
		}
	}

	/**
	 * Delete options manager from DB.
	 *
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.9
	 */
	function delete() {
		delete_option( $this->_get_option_manager_name() );
	}

	/**
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.6
	 *
	 * @param string $option
	 *
	 * @return bool
	 */
	function has_option( $option ) {
		return array_key_exists( $option, $this->_options );
	}

	/**
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.3
	 *
	 * @param string $option
	 * @param mixed $default
	 *
	 * @return mixed
	 */
	function get_option( $option, $default = null ) {
		$this->_logger->entrance( 'option = ' . $option );

		if ( is_array( $this->_options ) ) {
			return isset( $this->_options[ $option ] ) ? $this->_options[ $option ] : $default;
		} else if ( is_object( $this->_options ) ) {
			return isset( $this->_options->{$option} ) ? $this->_options->{$option} : $default;
		}

		return $default;
	}

	/**
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.3
	 *
	 * @param string $option
	 * @param mixed $value
	 * @param bool $flush
	 */
	function set_option( $option, $value, $flush = false ) {
		$this->_logger->entrance( 'option = ' . $option );

		if ( ! $this->is_loaded() ) {
			$this->clear();
		}

		if ( is_array( $this->_options ) ) {
			$this->_options[ $option ] = $value;
		} else if ( is_object( $this->_options ) ) {
			$this->_options->{$option} = $value;
		}

		if ( $flush ) {
			$this->store();
		}
	}

	/**
	 * Unset option.
	 *
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.3
	 *
	 * @param string $option
	 * @param bool $flush
	 */
	function unset_option( $option, $flush = false ) {
		$this->_logger->entrance( 'option = ' . $option );

		if ( is_array( $this->_options ) ) {
			if ( ! isset( $this->_options[ $option ] ) ) {
				return;
			}

			unset( $this->_options[ $option ] );

		} else if ( is_object( $this->_options ) ) {
			if ( ! isset( $this->_options->{$option} ) ) {
				return;
			}

			unset( $this->_options->{$option} );
		}

		if ( $flush ) {
			$this->store();
		}
	}

	/**
	 * Dump options to database.
	 *
	 * @author Vova Feldman (@svovaf)
	 * @since  1.0.3
	 */
	function store() {
		$this->_logger->entrance();

		$option_name = $this->_get_option_manager_name();

		if ( $this->_logger->is_on() ) {
			$this->_logger->info( $option_name . ' = ' . var_export( $this->_options, true ) );
		}

		// Update DB.
		update_option( $option_name, $this->_options );

		if ( ! WP_FS__DEBUG_SDK ) {
			wp_cache_set( $option_name, $this->_options, WP_FS__SLUG );
		}
	}
}