<?php
/**
 * @package   The_Grid
 * @author    Themeone <themeone.master@gmail.com>
 * @copyright 2015 Themeone
 */

// Exit if accessed directly
if (!defined('ABSPATH')) { 
	exit;
}

class The_Grid_Instagram {
	
	/**
	* Instagram API Key
	*
	* @since 1.0.0
	* @access private
	*
	* @var integer
	*/
	private $api_key;
	
	/**
	* Instagram transient
	*
	* @since 1.0.0
	* @access private
	*
	* @var string
	*/
	private $transient_sec;
	
	/**
	* Grid data
	*
	* @since 1.0.0
	* @access private
	*
	* @var array
	*/
	private $grid_data;

	/**
	* Instagram call number
	*
	* @since 1.0.0
	* @access private
	*
	* @var integer
	*/
	private $call_nb;
	
	/**
	* Instagram usernames
	*
	* @since 1.0.0
	* @access private
	*
	* @var array
	*/
	private $usernames = array();
	
	/**
	* Instagram hashtags
	*
	* @since 1.0.0
	* @access private
	*
	* @var array
	*/
	private $hashtags  = array();
	
	/**
	* Instagram count
	*
	* @since 1.0.0
	* @access private
	*
	* @var integer
	*/
	private $count;
	
	/**
	* Instagram media items
	*
	* @since 1.0.0
	* @access private
	*
	* @var array
	*/
	private $media = array();
	
	/**
	* Instagram last media
	*
	* @since 1.0.0
	* @access private
	*
	* @var array
	*/
	private $last_media = array();
	
	
	/**
	* Initialize the class and set its properties.
	* @since 1.0.0
	*/
	public function __construct($grid_data = '') {
		
		$this->get_transient_expiration();
		$this->grid_data = $grid_data;
		
	}
	
	/**
	* Get Instagram transient expiration
	* @since: 1.0.0
	*/
	public function get_transient_expiration(){
		
		$this->transient_sec = apply_filters('tg_transient_instagram', 3600);
		
	}
	
	/**
	* Return array of data
	* @since 1.0.0
	*/
	public function get_grid_items() {

		global $tg_is_ajax;

		$this->get_data(
			'media',
			$this->grid_data['instagram_username'],
			$this->grid_data['instagram_hashtag'],
			$this->grid_data['item_number']
		);
		
		$this->grid_data['ajax_data'] = htmlspecialchars(json_encode($this->last_media), ENT_QUOTES, 'UTF-8');
		
		if ( empty( $this->media ) && ! $tg_is_ajax ) {
				
			$error_msg = __( 'No content was found for the current ursername(s) and/or hashtag(s).', 'tg-text-domain' );
			throw new Exception($error_msg);

		}

		return $this->media;

	}
	
	/**
	* Return array of grid data
	* @since: 1.0.0
	*/
	public function get_grid_data(){

		return $this->grid_data;
		
	}
	
	/**
	* Get instagram data
	* @since 1.0.0
	*/
	public function get_data($type, $usernames, $hashtags, $count) {
		
		// store Instagram data
		$this->usernames = preg_replace('/\s+/', '', $usernames);
		$this->hashtags  = preg_replace('/\s+/', '', $hashtags);
		$this->count     = $count <= 0 ? 10 : $count;
		$this->count     = $this->count > 100 ? 100 : $this->count;
		
		// get last media from ajax
		$last_media = isset($_POST['grid_ajax']) && !empty($_POST['grid_ajax']) ? $_POST['grid_ajax'] : array();
		$this->last_media = $last_media;

		// prepare Instagram data
		$this->get_users_id();
		$this->get_hashtags();
		
		// retrieve Instagram data
		if ($type == 'media') {
			$this->get_media();
		} else if ($type == 'user_info') {	
			if ( is_array($this->user_data) ) {
				return reset($this->user_data);
			} else {
				return;
			}
			
		}

		return $this->media;
		
	}
	
	/**
	* Get user ID if necessary
	* @since 1.0.0
	*/
	public function get_users_id() {
		

		$this->usernames = array_filter(explode(',', $this->usernames));

		foreach ($this->usernames as $index => $username) {
			
			if ( is_numeric( $username ) ) {
				continue;	
			}

			$username = str_replace( '@', '', $username );
			$url = 'https://www.instagram.com/'. $username;// . '/?__a=1';
			$response = $this->request_body($url);

			if ( isset( $response->graphql->user->id ) && ! empty( $response->graphql->user->id ) ) {
				//$this->usernames[$index] = $response->graphql->user->id;
				$this->user_data[$response->graphql->user->id] = $response;
			}

		}

	}
	
	/**
	* Get hashtags
	* @since 1.0.0
	*/
	public function get_hashtags() {
		
		$this->hashtags = str_replace( '#', '', $this->hashtags );
		$this->hashtags = array_filter(explode(',', $this->hashtags));
		$this->hashtags = array_map('trim',$this->hashtags);
		
	}	
	
	/**
	* Retrieve media data
	* @since 1.0.0
	*/
	public function get_media() {

		$prev = -1;

		while ( count( $this->media ) < $this->count && count( $this->media ) !== $prev ) {

			// store previous number of media.
			$prev = count( $this->media );

			// retrieve Instagram data
			$this->get_hashtag_media();
			$this->get_user_media();

			// sort all data by date
			usort($this->media, function($a, $b) {
				return str_replace('@', '',$b['date']) - str_replace('@', '',$a['date']);
			});

			// return only the number of element set in grid settings
			$this->media = array_slice($this->media, 0, $this->count);

			// get the last media id (max_id)
			$this->get_last_media();
			
		}
	
	}
	
	/**
	* Retrieve user media
	* @since 1.0.0
	*/
	public function get_user_media() {

		foreach ($this->usernames as $username ) {
			
			if ( isset( $this->last_media[ $username ] ) ) {
				break;
			}

			/*$url = 'https://www.instagram.com/graphql/query/?query_id=17888483320059182&id='.$username.'&first='.$this->count;
			$url .= isset( $this->last_media[ $username ] ) ? '&after=' . $this->last_media[ $username ] : '';*/
			
			/*$query_url  = 'https://www.instagram.com/graphql/query/';
			$query_hash = '42323d64886122307be10013ad2dcc44';
			$variables  = array(
				'id'    => '25025320',
				'first' => $this->count
			);*/

			$response = $this->request_body( 'https://www.instagram.com/'.$username );

			if ( empty( $response->graphql->user ) ) {
				continue;
			}

			$response = $response->graphql->user->edge_owner_to_timeline_media->edges;

			$this->build_media_array($response, $username, true);
			
		}
		
		/*foreach ($this->usernames as $username ) {

			$url = 'https://www.instagram.com/'. $username . '/?__a=1';
			
			if ( isset( $this->last_media[ $username ] ) ) {
				return;
			}
			
			//$url .= isset( $this->last_media[ $username ] ) ? '&max_id=' . $this->last_media[ $username ] : '';
			$response = $this->get_response($url);

			if ( ! isset( $response->graphql ) ) {
				continue;
			}

			//$this->last_media[$username] = $response->graphql->user->edge_owner_to_timeline_media->page_info->end_cursor;

			
			$response = $response->graphql;
			$response = $response->user->edge_owner_to_timeline_media->edges;
			
			$this->build_media_array($response, $username, true);
			
		}*/

	}
	
	/**
	* Retrieve hashtag media
	* @since 1.0.0
	*/
	public function get_hashtag_media() {
	
		/*foreach ( $this->hashtags as $hashtag ) {

			$url = 'https://www.instagram.com/graphql/query/?query_id=17875800862117404&tag_name='.$hashtag.'&first='.$this->count;
			$url .= isset( $this->last_media[ $hashtag ] ) ? '&after=' . $this->last_media[ $hashtag ] : '';
			$response = $this->request_body($url);

			if ( ! isset( $response->data ) ) {
				continue;
			}

			$response = $response->data;
			$response = $response->hashtag->edge_hashtag_to_media->edges;
			
			$this->build_media_array($response, $hashtag);
			
		}*/
		
		foreach ( $this->hashtags as $hashtag ) {

			$url = 'https://www.instagram.com/explore/tags/'.$hashtag.'/?__a=1';
			$url .= isset( $this->last_media[ $hashtag ] ) ? '&max_id=' . $this->last_media[ $hashtag ] : '';
			$response = $this->get_response($url);

			if ( ! isset( $response->graphql ) ) {
				continue;
			}

			$response = $response->graphql;
			$response = $response->hashtag->edge_hashtag_to_media->edges;
			
			$this->build_media_array($response, $hashtag);
			
		}
		
	
	}
	
	
	/**
	* Get url response (transient)
	* @since 1.0.0
	*/
	public function get_response($url) {
		
		global $tg_is_ajax;
		
		$transient_name = 'tg_grid_' . md5($url);
		
		if ($this->transient_sec > 0 && ($transient = get_transient($transient_name)) !== false) {
			$response = json_decode($transient);
		} else {
			$response = wp_remote_fopen($url);

			if ( is_wp_error( $response ) ) {
				$error_msg  = __( 'Sorry, an error occured from Instagram API.', 'tg-text-domain' );
				throw new Exception($error_msg);
			}

			set_transient($transient_name, $response, $this->transient_sec);
			$response = json_decode($response);
		}

		return $response;
		
	}
	
	public function request_body($url/*, $hash, $variables*/) {
		
		$transient_name = 'tg_grid_' . md5($url);
	
		if ($this->transient_sec > 0 && ($transient = get_transient($transient_name)) !== false) {
			$response = $transient;
		} else {

			$response = wp_remote_get($url);

			if ( !preg_match('#window\._sharedData\s*=\s*(.*?)\s*;\s*</script>#', $response['body'], $response) ) {
				return;
			}

			if ( ! isset($response[1]) ) {
				return;
			}

			$response = json_decode($response[1]);

			if ( !$response || !isset( $response->entry_data->ProfilePage[0] ) ) {
				return;
			}

			$response = $response->entry_data->ProfilePage[0];
			set_transient($transient_name, $response, $this->transient_sec);

		}
		
		return $response;
		
		//print_r(json_encode($page_data_matches));
		/*$csrf = uniqid();
		$args = array(
			'status'     => 1,
			'base_url'   => 'https://www.instagram.com/',
			'cookie_jar' => array(
        		'ig_pr' => '1',
			),
			'query' => array(
				'query_hash' => $hash,
				'variables' => json_encode($variables)
			),
			'headers' => array(
				'User-Agent' => 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.87 Safari/537.36',
				'Accept-Encoding' => 'gzip, deflate',
				'Accept-Language' => 'en-US,en;q=0.8',
				'Origin' => 'https://www.instagram.com/',
				'Referer' => 'https://www.instagram.com/',
				'Connection' => 'close',
				'X-CSRFToken' => $csrf,
				'X-Requested-With' => 'XMLHttpRequest',
				'X-Instagram-AJAX' => '1',
				'X-Instagram-Gis' => 'rrrr',
				'Cookie' => 'ig_pr=1;csrftoken=' . $csrf,
				'Content-Length' => 0,
			),
		);

		$transient_name = 'tg_grid_' . md5($url);
	
		if ($this->transient_sec > 0 && ($transient = get_transient($transient_name)) !== false) {
			$response = json_decode($transient);
		} else {
			$response = wp_remote_get($url, $args);
		preg_match('#window\._sharedData\s*=\s*(.*?)\s*;\s*</script>#', $response['body'], $page_data_matches);

			if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
				$error_msg  = __( 'Sorry, an error occured from Instagram API.', 'tg-text-domain' );
				throw new Exception($error_msg);
			}

			set_transient($transient_name, $response['body'], $this->transient_sec);
			$response = json_decode($response['body']);
		}

		return $response;*/

	}
		

	/**
	* Store last media media
	* @since 1.0.0
	*/
	public function get_last_media() {
		
		// assign max id
		foreach ($this->media as $media => $data) {
			$this->last_media[$data['type']] = $data['ID'];
		}
		
	}
	
	/**
	* Get video
	* @since 1.0.0
	*/
	public function get_video( $node ) {
		
		if( ! $node->is_video ){
			return;
		}

		$url = 'https://www.instagram.com/p/'.$node->shortcode.'/?__a=1';
		$response = $this->get_response($url);
		
		if ( ! isset( $response->graphql->shortcode_media->video_url ) ) {
			return;
		}

		return array(
			'mp4' => $response->graphql->shortcode_media->video_url,
		);

	}

	/**
	* Get excerpt
	* @since 2.1.0
	*/
	public function get_excerpt($data) {
	
		if ( ! isset( $data->edge_media_to_caption->edges[0]->node->text ) ){
			return;
		}
			
		$excerpt = $data->edge_media_to_caption->edges[0]->node->text;
		$excerpt = $excerpt ? preg_replace('~(\#)([^\s!,. /()"\'?]+)~', '<a href="https://www.instagram.com/explore/tags/$2/" target="_blank" class="tg-item-social-link">#$2</a>', $excerpt) : null;
		$excerpt = $excerpt ? preg_replace('~(\@)([^\s!,. /()"\'?]+)~', '<a href="https://www.instagram.com/$2/" target="_blank" class="tg-item-social-link">@$2</a>', $excerpt) : null;
		return $excerpt;
		
	}
	
	/**
	* Build data array for the grid
	* @since 1.0.0
	*/
	public function build_media_array($response, $type, $user = false) {

		foreach( $response as $node ) {
			
			$node = $node->node;

			if ( $user && isset( $this->user_data[$node->owner->id] ) ) {
				$user = $this->user_data[$node->owner->id];
			} else if ( $user ) {

				$user = 'https://www.instagram.com/'. $node->owner->id . '/?__a=1';
				$user = $this->get_response($user);
				$this->user_data[$node->owner->id] = $user;

			}

			$display_url = isset( $node->display_url ) ? $node->display_url : null;
			$display_url = ! $display_url && isset( $node->display_src ) ? $node->display_src : $display_url;
			
			$thumbnail_src = isset( $node->thumbnail_resources[4]->src ) ? $node->thumbnail_resources[4]->src : null;
			$thumbnail_src = ! $thumbnail_src && isset( $node->thumbnail_src ) ? $node->thumbnail_src : $thumbnail_src;
			
			$video = $this->get_video($node);
			
			$this->media[ $node->taken_at_timestamp ] = array(
				'ID'              => $node->id,
				'type'            => $type,
				'date'            => $node->taken_at_timestamp,
				'post_type'       => null,
				'format'          => $video ? 'video' : null,
				'url'             => 'https://www.instagram.com/p/' . $node->shortcode,
				'url_target'      => '_blank',
				'title'           => null,
				'excerpt'         => $this->get_excerpt($node),
				'terms'           => null,
				'author'          => array(
					'ID'     => isset( $node->owner->id ) ? $node->owner->id : null,
					'name'   => isset( $user->graphql->user->username ) ? $user->graphql->user->username : '',
					'url'    => isset( $node->owner->id ) ? 'https://www.instagram.com/'.$node->owner->id.'/' : null,
					'avatar' => isset( $user->graphql->user->profile_pic_url ) ? $user->graphql->user->profile_pic_url : null,
				),
				'likes_number'    => isset( $node->edge_media_preview_like->count ) ? $node->edge_media_preview_like->count : null,
				'likes_title'     =>  __( 'Like on Instagram', 'tg-text-domain' ),
				'comments_number' => isset( $node->edge_media_to_comment->count ) ? $node->edge_media_to_comment->count : null,
				'views_number'    => null,
				'image'           => array(
					'alt'    => null,
					'url'    => $thumbnail_src,
					'lb_url' => $display_url ? $display_url : $thumbnail_src,
					'width'  => isset( $node->thumbnail_resources[4]->config_width ) ? $node->thumbnail_resources[4]->config_width : null,
					'height' => isset( $node->thumbnail_resources[4]->config_height ) ? $node->thumbnail_resources[4]->config_height : null,
				),
				'gallery'         => null,
				'video'           => array(
					'type'   => 'video',
					'source' => $video,
				),
				'audio'           => null,
				'quote'           => null,
				'link'            => null,
				'meta_data'       => null
			);
			
		}
		
	}
	
}
