<?php
// Don't load directly
defined( 'WPINC' ) or die;

class Tribe__Events__Aggregator__Record__Activity {
	/**
	 * Holds a Log of what has been done on This Queue
	 * @var array
	 */
	protected $items = array();

	/**
	 * Allows easier quick shortcodes to access activity
	 * @var array
	 */
	private $map = array();

	public $total = 0;

	/**
	 * Creates an easy way to test valid Actions
	 * @var array
	 */
	private static $actions = array(
		'created' => array(),
		'updated' => array(),
		'skipped' => array(),
	);

	public function __construct() {
		// The items are registered on the wakeup to avoid saving unnecessary data
		$this->__wakeup();
	}

	/**
	 * Register the Activities Tracked
	 */
	public function __wakeup() {
		// Entry for Events CPT
		$this->register( Tribe__Events__Main::POSTTYPE, array( 'event', 'events' ) );

		// Entry for Organizers CPT
		$this->register( Tribe__Events__Organizer::POSTTYPE, array( 'organizer', 'organizers' ) );

		// Entry for Venues CPT
		$this->register( Tribe__Events__Venue::POSTTYPE, array( 'venue', 'venues' ) );

		// Entry for Terms in Events Cat
		$this->register( Tribe__Events__Main::TAXONOMY, array( 'category', 'categories', 'cat', 'cats' ) );

		// Entry for Attachment
		$this->register( 'attachment', array( 'attachments', 'image', 'images' ) );

		/**
		 * Fires during record activity wakeup to allow other plugins to inject/register activity entries
		 * for other custom post types
		 *
		 * @param Tribe__Events__Aggregator__Record__Activity $this
		 */
		do_action( 'tribe_aggregator_record_activity_wakeup', $this );
	}

	/**
	 * Prevents Mapping to be saved on the DB object
	 * @return array
	 */
	public function __sleep() {
		return array( 'items' );
	}

	/**
	 * Register a Specific Activity and it's mappings
	 *
	 * @param  string $slug Name of this Activity
	 * @param  array  $map  (optional) Other names in which you can access this activity
	 *
	 * @return boolean       [description]
	 */
	public function register( $slug, $map = array() ) {
		if ( empty( $this->items[ $slug ] ) ) {
			// Clone the Default action values
			$this->items[ $slug ] = (object) self::$actions;
		} else {
			$this->items[ $slug ] = (object) array_merge( (array) self::$actions, (array) $this->items[ $slug ] );
		}

		// Add the base mapping
		$this->map[ $slug ] = $slug;

		// Allow short names for the activities
		foreach ( $map as $to ) {
			$this->map[ $to ] = $slug;
		}

		return true;
	}

	/**
	 * Logs an Activity
	 *
	 * @param string       $slug Name of this Activity
	 * @param string|array $items Type of activity
	 * @param array        $ids   items inside of the action
	 *
	 * @return boolean
	 */
	public function add( $slug, $items, $ids = array() ) {
		if ( ! $this->exists( $slug ) ) {
			return false;
		}

		if ( ! isset( $this->map[ $slug ] ) ) {
			return false;
		}

		// Map the Slug
		$slug = $this->map[ $slug ];

		if ( is_scalar( $items ) ) {
			// If it's a scalar and it's not one of the registered actions we skip it
			if ( ! isset( self::$actions[ $items ] ) ) {
				return false;
			}

			// Make the actual Array of items
			$items = array( $items => $ids );
		} else {
			$items = (object) $items;

			// Doesn't contain any of the Possible Actions
			if ( 0 === count( array_intersect_key( self::$actions, (array) $items ) ) ) {
				return false;
			}
		}

		foreach ( $items as $action => $ids ) {
			// Skip Empty ids
			if ( empty( $ids ) ) {
				continue;
			}

			$this->items[ $slug ]->{ $action } = array_unique( array_filter( array_merge( $this->items[ $slug ]->{ $action }, (array) $ids ) ) );
		}

		return true;
	}

	/**
	 * Returns the merged version of two Activities classes
	 *
	 * @param  self   $activity Which activity should be merged here
	 *
	 * @return self
	 */
	public function merge( self $activity ) {
		$items = $activity->get();

		foreach ( $items as $slug => $data ) {
			$this->add( $slug, $data );
		}

		return $this;
	}

	/**
	 * Removes a activity from the Registered ones
	 *
	 * @param  string  $slug   The Slug of the Activity
	 *
	 * @return boolean
	 */
	public function remove( $slug ) {
		if ( ! $this->exists( $slug ) ) {
			return false;
		}

		if ( ! isset( $this->map[ $slug ] ) ) {
			return false;
		}

		// Map the Slug
		$slug = $this->map[ $slug ];

		// Remove it
		unset( $this->items[ $slug ] );
		return true;
	}

	/**
	 * Fetches a registered Activity
	 *
	 * @param  string  $slug   (optional) The Slug of the Activity
	 * @param  string  $action (optional) Which action
	 *
	 * @return null|array|object
	 */
	public function get( $slug = null, $action = null ) {
		if ( is_null( $slug ) ) {
			return $this->items;
		}

		if ( ! isset( $this->map[ $slug ] ) ) {
			return null;
		}

		// Map the Slug
		$slug = $this->map[ $slug ];

		// Check if it actually exists
		if ( empty( $this->items[ $slug ] ) ) {
			return null;
		}

		$actions = $this->items[ $slug ];

		// If we trying to get a specific action and
		if ( is_null( $action ) ) {
			return $this->items[ $slug ];
		} elseif ( ! empty( $actions->{ $action } ) ) {
			return $actions->{ $action };
		} else {
			return null;
		}
	}

	/**
	 * Fetches a registered Activity counter
	 *
	 * @param  string  $slug   (optional) The Slug of the Activity
	 * @param  string  $action (optional) Which action
	 *
	 * @return int
	 */
	public function count( $slug = null, $action = null ) {
		$actions = $this->get( $slug );

		if ( empty( $actions ) ) {
			return 0;
		}

		// Sum all of the Actions
		if ( is_null( $action ) ) {
			return array_sum( array_map( 'count', (array) $actions ) );
		} elseif ( ! empty( $actions->{ $action } ) ) {
			return count( $actions->{ $action } );
		}

		return 0;
	}

	/**
	 * Checks if a given Activity type exists
	 *
	 * @param  string  $slug The Slug of the Tab
	 *
	 * @return boolean
	 */
	public function exists( $slug ) {
		if ( is_null( $slug ) ) {
			return false;
		}

		if ( ! isset( $this->map[ $slug ] ) ) {
			return false;
		}

		// Map the Slug
		$slug = $this->map[ $slug ];

		// Check if it actually exists
		return ! empty( $this->items[ $slug ] ) ;
	}
}
