<?php
namespace Kanews\Util;

class Cache {
	private static $instance;
	public $cache_time;
	public $cache_key;
	private $timer;
	private $cache_enabled = false;

	public static function getInstance() {
		if ( null === static::$instance ) {
			static::$instance = new static();
		}
		return static::$instance;
	}
	
	function __construct(){
	
		$this->cache_key  = 'kanews-cache';
		$this->cache_time = 8 * HOUR_IN_SECONDS;
		$this->timer = microtime(true); // Initialize the property
		$this->cache_enabled = (bool) kanews_get_option('kanews-cache');

	
		if( $this->cache_enabled ) {
			add_action( 'wp_update_nav_menu',        array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'add_category',              array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'delete_category',           array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'edit_category',             array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'edit_terms',                array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'delete_term',               array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'delete_attachment',         array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'edit_attachment',           array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'trashed_post',              array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'untrashed_post',            array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'deleted_post',              array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'save_post',                 array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'switch_theme',              array( $this, 'wp_cache_flush' ) );
			add_action( 'upgrader_process_complete', array( $this, 'wp_cache_flush' ) );
			add_action( 'deleted_comment',           array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'untrashed_comment',         array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'spammed_comment',           array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'unspammed_comment',         array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'wp_set_comment_status',     array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'activated_plugin',          array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'deactivated_plugin',        array( $this, 'wp_cache_flush_widget' ) );
			add_action( 'csf_kanews_options_save_after', array( $this, 'wp_cache_flush' ) );
			
			// Nav Menu
			add_filter( 'pre_wp_nav_menu', 					array( $this, 'pre_wp_nav_menu' ), 10, 2 );
			add_filter( 'wp_nav_menu', 							array( $this, 'wp_nav_menu' ), 10, 2 );
			
			// Widgets
			add_filter( 'widget_display_callback',  array( $this, '_cache_widget_output' ), 10, 3 );
			add_filter( 'in_widget_form', 					array( $this, 'in_widget_form' ), 10, 3 );
			add_filter( 'widget_update_callback', 	array( $this, 'widget_update_callback' ), 10, 4 );
		}

		add_filter( 'admin_bar_menu', array($this, 'purge_cache_admin_menu'), 2000 );
		add_filter( 'admin_init', array($this, 'purge_cache') );
		add_action( 'kanews_clear_cache', array($this, 'wp_cache_flush') );	
		add_filter( 'switch_theme', array($this, 'clear_scheduled') );	

		$this->maybe_schedule_cache_event();

	}
	
	private function maybe_schedule_cache_event() {
		if ( $this->cache_enabled ) {
			if ( !wp_next_scheduled( 'kanews_clear_cache' ) ) {
				wp_schedule_event( time(), 'weekly', 'kanews_clear_cache' );
			}
		} else {
			$this->clear_scheduled();
		}
	}
	
	private function delete_transients_like( $pattern ) {
		global $wpdb;
		$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s", $pattern ) );
	}
	
	private function should_skip_widget_cache( $widget, $args, &$instance ) {
		if ( false === $instance ) {
			return true;
		}
		
		if ( $widget->is_preview() || is_admin() || is_archive() ) {
			return true;
		}
		
		if ( ! $this->cache_enabled ) {
			return true;
		}
		
		if ( strpos( $args['widget_id'], 'kanews_widget_survey' ) !== false  || strpos( $args['widget_id'], 'kanews_widget_posts_x' ) !== false  || strpos( $args['widget_id'], 'kanews_widget_game_center' ) !== false  || strpos( $args['widget_id'], 'kanews_widget_currencies' ) !== false  || strpos( $args['widget_id'], 'kanews_widget_ilan' ) !== false || strpos( $args['widget_id'], 'kanews_widget_weather' ) !== false || strpos( $args['widget_id'], 'kanews_widget_ads' ) !== false ) {
			return true;
		}
		
		if(!isset($instance['wc_cache']))
			$instance['wc_cache'] = false;
		
		if($instance['wc_cache'] == true) {
			return true;
		}
		
		if(isset($instance['sortby']) && $instance['sortby'] == 'random') {
			return true;
		}
		
		return false;
	}

	/**
	 * Cached version of get_users.
	 * 
	 * Count number of users who have each of the user roles.
	 * @return array
	 */
	public static function get_users($args = '') {
		if ( ! $users = wp_cache_get( 'users_'.md5( serialize( array( $args ) ) ) , 'kanews' ) ) {
			$users = get_users($args);
			wp_cache_set( 'users_'.md5( serialize( array( $args ) ) ) , $users, 'kanews' );
		}

		return $users;
	}

	/**
	 * Cached version of count_users.
	 * 
	 * Count number of users who have each of the user roles.
	 * @return array
	 */
	public static function get_count_users() {
		if ( ! $count = wp_cache_get( 'count_users', 'kanews' ) ) {
			$count = count_users();
			wp_cache_set( 'count_users', $count, 'kanews' );
		}

		return $count;
	}

	/**
	 * cache_term
	 * 
	 * @return array
	 */
	public static function cache_term( $terms ) {
		foreach ( $terms as $term ) {
			wp_cache_add( $term->term_id, $term, 'terms' );
		}
	}

	/**
	 * Cached version of get_categories.
	 * 
	 * Retrieves a list of category objects.
	 * @return array
	 */
	public static function get_categories() {
		if ( ! $categories = wp_cache_get( 'categories', 'kanews' ) ) {
			$categories = get_categories( array( 'hide_empty' => 0 ) );
			wp_cache_set( 'categories', $categories, 'kanews' );
			self::cache_term( $categories );
		}

		return $categories;
	}
	
	/**
	 * get_menu_key
	 * Simple function to generate a unique id for the menu transient
	 * based on the menu arguments and currently requested page.
	 * @param  object $args     An object containing wp_nav_menu() arguments.
	 * @return string
	 */
	function get_menu_key($args){
		return $this->cache_key.'-' . md5( serialize( $args ).serialize(get_queried_object()) );
	}

/**
 * get_menu_transient
 * Simple function to get the menu transient based on menu arguments
 * @param  object $args     An object containing wp_nav_menu() arguments.
 * @return mixed            menu output if exists and valid else false.
 */
	function get_menu_transient($args){
		$key = $this->get_menu_key($args);
		return get_transient($key);
	}

	/**
	 * pre_wp_nav_menu
	 *
	 * This is the magic filter that lets us short-circit the menu generation
	 * if we find it in the cache so anything other then null returend will skip the menu generation.
	 *
	 * @param  string|null $nav_menu    Nav menu output to short-circuit with.
	 * @param  object      $args        An object containing wp_nav_menu() arguments
	 * @return string|null
	 */
	function pre_wp_nav_menu($nav_menu, $args){
			if( is_home() || is_front_page() ) {
				$this->timer = microtime(true);
				$in_cache = $this->get_menu_transient($args);
				$last_updated = get_transient($this->cache_key.'-' . $args->theme_location . '-updated');
				if (isset($in_cache['data']) && isset($last_updated) &&  $last_updated < $in_cache['time'] && ( is_home() || is_front_page() ) ){
					return $in_cache['data'].'<!-- From menu cache in '.number_format( microtime(true) - $this->timer, 5 ).' seconds -->';
				}
			}
			return $nav_menu;
	}

	/**
	 * wp_nav_menu
	 * store menu in cache
	 * @param  string $nav      The HTML content for the navigation menu.
	 * @param  object $args     An object containing wp_nav_menu() arguments
	 * @return string           The HTML content for the navigation menu.
	 */
	function wp_nav_menu( $nav, $args ) {
		if( is_home() || is_front_page() ) {
			$last_updated = get_transient($this->cache_key.'-' . $args->theme_location . '-updated');
			if( ! $last_updated ) {
				set_transient($this->cache_key.'-' . $args->theme_location . '-updated', $this->cache_time);
			}
			$key = $this->get_menu_key($args);
			$data = array('time' => $this->cache_time, 'data' => $nav);

			set_transient( $key, $data ,$this->cache_time);
		}
		return $nav;
	}

	/**
	 * wp_update_nav_menu
	 * refresh time on update to force refresh of cache
	 * @param  int $menu_id
	 * @return void
	 */
	function wp_update_nav_menu($menu_id) {
		$locations = array_flip(get_nav_menu_locations());

		if( isset($locations[$menu_id]) && ( is_home() || is_front_page() ) ) {
			set_transient($this->cache_key.'-' . $locations[$menu_id] . '-updated', $this->cache_time);
		}
	}
	
	/**
	 * get_widget_key 
	 * 
	 * Simple function to generate a unique id for the widget transient
	 * based on the widget's instance and arguments
	 * 
	 * @param  array $i widget instance
	 * @param  array $a widget arguments
	 * @return string md5 hash
	 */
	function get_widget_key($i,$a){
			return 'kanews-widget-cache-' . md5( serialize( array( $i, $a ) ) );
	}

	/**
	 * _cache_widget_output 
	 * @param array     $instance The current widget instance's settings.
	 * @param WP_Widget $this     The current widget instance.
	 * @param array     $args     An array of default widget arguments.
	 * @return mixed array|boolean
	 */
	function _cache_widget_output($instance, $widget, $args){
		if ( $this->should_skip_widget_cache( $widget, $args, $instance ) )
			return $instance;
		
//simple timer to clock the widget rendring
		// $timer_start = microtime(true);

		//create a uniqe transient ID for this widget instance
		$transient_name = $this->get_widget_key($instance,$args);

		if( strpos( $args['widget_id'], 'manset' ) !== false  )
		$transient_name .= kan_is_mobile() ? '-mobile' : '-desktop';

		if(isset($instance['hide_mobile']) && $instance['hide_mobile'])  {
			if(!kan_is_mobile())
			$transient_name = $this->get_widget_key($instance,$args).'-desktop';
			else
			return $instance;
		}

		if(isset($instance['hide_desktop']) && $instance['hide_desktop']) {
			if(kan_is_mobile())
			$transient_name = $this->get_widget_key($instance,$args).'-mobile';
			else
			return $instance;
		} 


		//get the "cached version of the widget"
		if ( false === ( $cached_widget = get_transient( $transient_name ) ) ){
			// It wasn't there, so render the widget and save it as a transient
			// start a buffer to capture the widget output
			ob_start();
			//this renders the widget
			$widget->widget( $args, $instance );
			//get rendered widget from buffer
			$cached_widget = ob_get_clean();
			//save/cache the widget output as a transient
			set_transient( $transient_name, $cached_widget, $this->cache_time);
		}

		//output the widget
		echo $cached_widget;
		
		//output rendering time as an html comment
		// echo '<!-- From widget cache in '.number_format( microtime(true) - $timer_start, 5 ).' seconds -->';

		//after the widget was rendered and printed we return false to short-circuit the normal display of the widget
		return false;  
	}
	
	/**
	 * in_widget_form
	 * this method displays a checkbox in the widget panel
	 * 
	 * @param WP_Widget $t     The widget instance, passed by reference.
	 * @param null      $return   Return null if new fields are added.
	 * @param array     $instance An array of the widget's settings.
	 * 
	 */
	function in_widget_form($t,$return,$instance){
		$instance = wp_parse_args( 
			(array) $instance, 
			array( 
					'title' => '', 
					'text' => '', 
					'wc_cache' => null
			)
		);

		if ( !isset($instance['wc_cache']) )
			$instance['wc_cache'] = null;
		?>

		<p>
			<input id="<?php echo $t->get_field_id('wc_cache'); ?>" name="<?php echo $t->get_field_name('wc_cache'); ?>" type="checkbox" <?php checked(isset($instance['wc_cache']) ? $instance['wc_cache'] : 0); ?> />
			<label for="<?php echo $t->get_field_id('wc_cache'); ?>"><?php _e('Bu Bileşeni Ön Belleğe Alma', 'kanews'); ?></label>
		</p>
		<?php
	}

	/**
	 * widget_update_callback
	 * @param array     $instance     The current widget instance's settings.
	 * @param array     $new_instance Array of new widget settings.
	 * @param array     $old_instance Array of old widget settings.
	 * @return array    $instance
	 */
	function widget_update_callback($instance, $new_instance, $old_instance, $args){

		//save the checkbox if its set
		$instance['wc_cache'] = isset($new_instance['wc_cache']);
		
		//clear_transient
		// $transient_name = $this->get_widget_key($old_instance,$args);

		$this->delete_transients_like( '_transient_kanews-widget-cache%' );
		$this->delete_transients_like( '_transient_timeout_kanews-widget-cache%' );
		
		return $instance;

	}
	
	// Clear Cache
	function wp_cache_flush(){
		$this->delete_transients_like( '_transient_kanews%' );
		$this->delete_transients_like( '_transient_timeout_kanews%' );
	}	

	// Clear Cache
	function wp_cache_flush_widget(){
		$this->delete_transients_like( '_transient_kanews-widget-cache%' );
		$this->delete_transients_like( '_transient_timeout_kanews-widget-cache%' );
	}	

	// Purge CAche
	function purge_cache() {
		$clear = isset( $_GET['kan_purge_cache'] ) ? $_GET['kan_purge_cache'] : false;
		if ( $clear && current_user_can( 'manage_options' ) ) {
			$this->delete_transients_like( '_transient_kanews%' );
			$this->delete_transients_like( '_transient_timeout_kanews%' );
		}
	}
	
	function purge_cache_admin_menu() {
		global $wp_admin_bar, $wp;
	
		if(!current_user_can('manage_options'))
		return;
		
		$parent = 'top-secondary';
		$wp_admin_bar->add_menu(array('parent' => $parent, 'title' => __('Temanın Ön Belleğini Temizle', 'kanews'), 'id' => 'kan-purge-cache', 'href' => add_query_arg( 'kan_purge_cache', true, admin_url() ) ));
	}

	function clear_scheduled() {	
		wp_clear_scheduled_hook( 'kanews_clear_cache' );
	}
}