<?php

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


$schematicShortcodes = null;

class SchematicShortcodes {

    private $shortcodes = array();
    private $id = 0;

    public function __construct() {

        global $schematicCompatability;

        if ($schematicCompatability->getMppCompatible()) {
            $this->addShortcodeSpecial('special', 'Magic Page Shortcodes - [location], [cities type="csvt"] and others including spintax expressions');
        }

        $this->addShortcode('title', '[title]', 'Retrieve current page title');
        $this->addShortcode('url', '[url]', 'Retrieve current page url');
        $this->addShortcode('bloginfo', '[bloginfo("name")]', 'Retrieve bloginfo such as name, description, bloginfo, url, admin_email, language - read more <a target="_blank" href="https://developer.wordpress.org/reference/functions/bloginfo/#more-information">more info on bloginfo</a>');
        $this->addShortcode('breadcrumbs', '[breadcrumbs]', 'Creates Breadcrumbs (BreadcrumbList) schema. Normally should be used with id: itemListElement. Optional parameters: include_home = true|false (default - true) - displays homepage item; full_tree = true|false (default - true) - displays full category hierarchy. Example: [breadcrumbs("include_home=false", "full_tree=false")]');


        // post related stuff
        $this->addShortcode('post_meta', '[post_meta]', 'Retrieve custom fields (meta fields). Example: [post_meta("price")] ');
        $this->addShortcode('post_id', '[post_id]', 'Retrieve post ID');
        $this->addShortcode('post_title', '[post_title]', 'Retrieve post title by ID or Name (slug). Examples: [post_title("5")], [post_title("contact-us")]');
        $this->addShortcode('post_date_published', '[post_date_published]', 'Retrieve post date published');
        $this->addShortcode('post_date_modified', '[post_date_modified]', 'Retrieve post date modified');
        $this->addShortcode('post_copyright_year', '[post_copyright_year]', 'Retrieve post copyright year');
        $this->addShortcode('post_comments_count', '[post_comments_count]', 'Retrieve post comments count');
        $this->addShortcode('post_author_meta', '[post_author_meta("user_firstname")]', 'Retrieve post author information such as user_firstname, user_lastname, user_url - see more <a target="_blank" href="https://developer.wordpress.org/reference/functions/get_the_author_meta/#description">get_the_author_meta fields</a>');
        $this->addShortcode('post_comments_page_end', '[post_comments_page_end]', 'Retrieve post comments last page number, for use with "pageEnd" parameter for example');
        $this->addShortcode('post_comments', '[post_comments]', 'Retrieve post comments');
        $this->addShortcode('post_thumbnail', '[post_thumbnail]', 'Retrieve post thumbnail. The optional parameter is registered image size to retrieve the source - [post_thumbnail("thumbnail")]');
        $this->addShortcode('post_url', '[post_url]', 'Retrieve Post URL By ID or Name (slug) - Examples [post_url("5")], [post_url("contact-us")]');
        $this->addShortcode('post_category_url', '[post_category_url]', 'Retrieve post category Url');
        $this->addShortcode('post_category_title', '[post_category_title]', 'Retrieve post category Title');
        $this->addShortcode('post_categories', '[post_categories]', 'Retrieve post categories');
        $this->addShortcode('post_tags', '[post_tags]', 'Retrieve post tags');
        $this->addShortcode('post_excerpt', '[post_excerpt]', 'Retrieve current page excerpt');
        $this->addShortcode('post_content', '[post_content]', 'Retrieve current page content');

        // post related stuff
    }

    public function post_categories($content) {
        if (strpos($content, '[post_categories]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $categories = wp_get_post_categories($post->ID, array('fields' => 'names'));
                if ($categories) {
                    $postCategories = serialize($categories);
                }
            }
        }

        if (!isset($postCategories)) {
            $postCategories = '';
        }

        return str_replace('[post_categories]', $postCategories, $content);
    }

    public function post_id($content) {
        if (strpos($content, '[post_id]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $postId = $post->ID;
            }
        }

        if (!isset($postId)) {
            $postId = '';
        }

        return str_replace('[post_id]', $postId, $content);
    }

    public function post_tags($content) {
        if (strpos($content, '[post_tags]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $tags = wp_get_post_tags($post->ID, array('fields' => 'names'));
                if ($tags) {
                    $postTags = serialize($tags);
                }
            }
        }

        if (!isset($postTags)) {
            $postTags = '';
        }

        return str_replace('[post_tags]', $postTags, $content);
    }

    public function post_comments_page_end($content) {
        if (strpos($content, '[post_comments_page_end]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {

                $comments = get_comments(array('post_id' => $post->ID));

                if ($comments) {
                    if (!get_option('page_comments') || empty($comments)) {
                        $count = 1;
                    }

                    $per_page = (int) get_query_var('comments_per_page');

                    if (0 === $per_page) {
                        $per_page = (int) get_option('comments_per_page');
                    }
                    if (0 === $per_page) {
                        $count = 1;
                    }

                    if (!isset($threaded)) {
                        $threaded = get_option('thread_comments');
                    }

                    if ($threaded) {
                        $walker = new Walker_Comment;
                        $count = ceil($walker->get_number_of_root_elements($comments) / $per_page);
                    } else {
                        $count = ceil(count($comments) / $per_page);
                    }
                }
            }

            if (!isset($count)) {
                $count = '';
            }

            return str_replace('[post_comments_page_end]', $count, $content);
        }
    }

    public function breadcrumbs($content) {
        $out = array();

        foreach ($this->getFunctions($content, 'breadcrumbs') as $function) {
            $includeHome = ($this->getFunctionParameter($function, 'include_home', true));
            $fullTree = ($this->getFunctionParameter($function, 'full_tree', true));

            $this->id = 0;

            if ($includeHome) {
                $out[] = $this->getListItem(get_home_url(), get_bloginfo('name'));
            }

            if (is_single() || is_page()) {
                $categories = getPostTermList($fullTree);
                foreach ($categories as $category) {
                    $out[] = $this->getListItem($category['url'], $category['name']);
                }
            } else if (is_category() or is_tax() or is_tag()) {
                $terms = getTermList($fullTree);
                foreach ($terms as $term) {
                    $out[] = $this->getListItem($term['url'], $term['name']);
                }
            }

            $content = str_replace($function, serialize($out), $content);
        }


        return $content;
    }

    public function post_category_url($content) {
        if (strpos($content, '[post_category_url]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $categories = wp_get_post_categories($post->ID);
                if ($categories) {
                    $link = get_category_link($categories[0]);
                }
            }
        }

        if (!isset($link)) {
            $link = '';
        }

        return str_replace('[post_category_url]', $link, $content);
    }

    public function post_category_title($content) {
        if (strpos($content, '[post_category_title]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $categories = wp_get_post_categories($post->ID, array('fields' => 'names'));
                if ($categories) {
                    $postcategoryTitle = $categories[0];
                }
            }
        }

        if (!isset($postcategoryTitle)) {
            $postcategoryTitle = '';
        }

        return str_replace('[post_category_title]', $postcategoryTitle, $content);
    }

    public function post_thumbnail($content) {
        if (is_single() || is_page()) {
            global $post;

            foreach ($this->getFunctions($content, 'post_thumbnail') as $function) {
                $params = $this->getFunctionParameters($function);

                if ($params && $params[0]) {
                    $size = $params[0];
                }

                $size = $params && $params[0] ? $params[0] : 'post-thumbnail';
                $src = get_the_post_thumbnail_url($post, $size);


                $content = str_replace($function, $src, $content);
            }
        }

        return $content;
    }

    public function post_comments($content) {
        if (is_single() || is_page()) {
            global $post;

            foreach ($this->getFunctions($content, 'post_comments') as $function) {
//                 
//                 $defaultCommentsPage = get_option('default_comments_page');
//                 $paged = ($defaultCommentsPage == 'newest') ? $lastPage : 1;
//                 $lastPage = get_comment_pages_count($comments, $commentsPerPage);
//                 
                // get_comment_pages_count

                $parameters = array(
                    'post_id' => $post->ID,
                    'status' => 'approve',
                    // 'order' => 'ASC',
                    'per_page' => get_option('comments_per_page'),
                        // 'page' => $paged,
                );

                $comments = get_comments($parameters);

                $out = array();

                $postUrl = get_permalink($post);

                foreach ($comments as $comment) {
                    $out[] = $this->getCommentItem($comment, $postUrl);
                }

                $content = str_replace($function, serialize($out), $content);
            }
        }

        return $content;
    }

    public function post_title($content) {
        foreach ($this->getFunctions($content, 'post_title') as $function) {
            $params = $this->getFunctionParameters($function);

            $postId = null;

            if ($params && $params[0]) {
                if (is_numeric($params[0])) {
                    $postId = intval($params[0]);
                } else {
                    global $wpdb;
                    $postId = $wpdb->get_var($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_name = %s", $params[0]));
                }

                $replace = $postId ? get_the_title($postId) : '';

                $content = str_replace($function, $replace, $content);
            }
        }

        return $content;
    }

    public function post_comments_count($content) {
        if (strpos($content, '[post_comments_count]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $comments = get_comments_number($post);
            }
        }

        if (!isset($comments)) {
            $comments = '';
        }

        return str_replace('[post_comments_count]', $comments, $content);
    }

    public function post_date_published($content) {
        if (strpos($content, '[post_date_published]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $post_date = get_the_date('c', $post);
            }
        }

        if (!isset($post_date)) {
            $post_date = '';
        }

        return str_replace('[post_date_published]', $post_date, $content);
    }

    public function post_date_modified($content) {
        if (strpos($content, '[post_date_modified]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {

                $post_date = get_the_modified_date('c', $post);
            }
        }

        if (!isset($post_date)) {
            $post_date = '';
        }

        return str_replace('[post_date_modified]', $post_date, $content);
    }

    public function post_copyright_year($content) {
        if (strpos($content, '[post_copyright_year]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $post_date = get_the_date('Y', $post);
            }
        }

        if (!isset($post_date)) {
            $post_date = '';
        }

        return str_replace('[post_copyright_year]', $post_date, $content);
    }

    public function post_author_meta($content) {
        foreach ($this->getFunctions($content, 'post_author') as $function) {
            $params = $this->getFunctionParameters($function);

            if (is_single() || is_page()) {
                global $post;
                if ($post && $post->ID) {
                    $author_id = $post->post_author;

                    if ($params && $params[0] && $params[0] !== 'user_pass') {
                        $content = str_replace($function, get_the_author_meta($params[0], $author_id), $content);
                    }
                }
            }
        }

        return $content;
    }

    public function post_url($content) {
        foreach ($this->getFunctions($content, 'post_url') as $function) {
            $params = $this->getFunctionParameters($function);

            $postId = null;

            if ($params && $params[0]) {
                if (is_numeric($params[0])) {
                    $postId = intval($params[0]);
                } else {
                    global $wpdb;
                    $postId = $wpdb->get_var($wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_name = %s", $params[0]));
                }

                $replace = $postId ? get_permalink($postId) : '';

                $content = str_replace($function, $replace, $content);
            }
        }

        return $content;
    }

    public function title($content) {
        if (strpos($content, '[title]') === false) {
            return $content;
        }

        if (is_home()) {
            if (($page = get_option('page_on_front'))) {
                $title = get_the_title($page);
            } else {
                $title = get_bloginfo('name');
            }
        } else if (is_single() || is_page()) {
            global $post;
            $title = get_the_title($post);
        } else if (is_category() or is_tax() or is_tag()) {
            $object = get_queried_object();
            if ($object && $object->name) {
                $title = $object->name;
            }
        }

        if (!isset($title)) {
            $title = '';
        }

        return str_replace('[title]', $title, $content);
    }

    public function url($content) {
        if (strpos($content, '[url]') === false) {
            return $content;
        }

        if (is_home()) {
            $link = get_home_url();
        } else if (is_single() || is_page()) {
            global $post;
            $link = get_permalink($post);
        } else if (is_category() or is_tax() or is_tag()) {
            $object = get_queried_object();
            if ($object && $object->name) {
                $link = get_term_link($object);
            }
        }

        if (!isset($link)) {
            $link = '';
        }

        return str_replace('[url]', $link, $content);
    }

    public function post_excerpt($content) {
        if (strpos($content, '[post_excerpt]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $excerpt = get_the_excerpt($post);
            }
        }

        if (!isset($excerpt)) {
            $excerpt = '';
        }

        return str_replace('[post_excerpt]', strip_tags($excerpt), $content);
    }

    public function post_content($content) {
        if (strpos($content, '[post_content]') === false) {
            return $content;
        }

        if (is_single() || is_page()) {
            global $post;
            if ($post && $post->ID) {
                $_content = get_the_content('', false, $post);
                $_content = apply_filters('the_content', $_content);
            }
        }

        if (!isset($_content)) {
            $_content = '';
        }

        return str_replace('[post_content]', strip_tags($_content), $content);
    }

    public function bloginfo($content) {
        foreach ($this->getFunctions($content, 'bloginfo') as $function) {
            $params = $this->getFunctionParameters($function);

            if ($params && $params[0]) {
                $content = str_replace($function, get_bloginfo($params[0]), $content);
            }
        }

        return $content;
    }

    public function post_meta($content) {
        global $post;

        if ($post && $post->ID) {
            // older syntax
            foreach ($this->getFunctions($content, 'postmeta') as $function) {
                $params = $this->getFunctionParameters($function);

                if ($params && $params[0]) {
                    $content = str_replace($function, get_post_meta($post->ID, $params[0], true), $content);
                }
            }
            // new syntax
            foreach ($this->getFunctions($content, 'post_meta') as $function) {
                $params = $this->getFunctionParameters($function);

                if ($params && $params[0]) {


                    $content = str_replace($function, get_post_meta($post->ID, $params[0], true), $content);
                }
            }
        }

        return $content;
    }

    public function getFunctions($content, $functionName) {
        $found = array();

        preg_match_all("/\[[^\]]*\]/", $content, $matches);

        if ($matches && $matches[0]) {
            foreach ($matches[0] as $contentFound) {
                if (strpos($contentFound, $functionName) !== false) {
                    $found[] = $contentFound;
                }
            }
        }

        return $found;
    }

    public function getShortcodes() {
        return $this->shortcodes;
    }

    public function getFunctionParameters($input) {
        preg_match_all('/(?:[^\s(]+\(|\G(?!^),\s*)\K(?:"[^"]*"|[^,()]+)(?=[^()]*\))/', $input, $matches);
        if ($matches && $matches[0]) {
            $params = array_map('trim', $matches[0]);

            return array_map(array($this, 'removeQuotes'), $params);
        }
    }

    public function getFunctionParameter($input, $parameter, $default = true) {
        $parameters = $this->getFunctionParameters($input);

        if ($parameters) {
            foreach ($parameters as $_parameter) {
                $found = explode('=', $_parameter);

                if ($found && $found[0] && trim($found[0]) == $parameter) {
                    $return = trim($found[1]);

                    if ($return == 'true') {
                        return true;
                    }

                    if ($return == 'false') {
                        return false;
                    }

                    return $return;
                }
            }
        }

        return $default;
    }

    public function removeQuotes($string) {
        return preg_replace('/^(\'(.*)\'|"(.*)")$/', '$2$3', $string);
    }

    public function addShortcode($function, $shortcode, $description) {
        add_filter('schematic_shortcodes', array($this, $function));
        $this->shortcodes[] = array($shortcode, $description);
    }

    public function addShortcodeSpecial($shortcode, $description) {
        $this->shortcodes[] = array($shortcode, $description);
    }

    private function getListItem($id, $name) {
        $this->id++;

        return array(
            "@type" => "ListItem",
            'position' => $this->id,
            'item' => array(
                "@id" => $id,
                'name' => $name,
            )
        );
    }

    /**
     * 
     * @param type $date
     * @param type $text
     * @param type $url
     * @param type $avatarUrl
     * @param type $authorName
     * @param type $authorUrl
     * @return type
     */
    private function getCommentItem($comment, $postUrl) {

        return array(
            "@type" => "Comment",
            "dateCreated" => mysql2date('c', $comment->comment_date),
            "text" => get_comment_text($comment),
            "url" => trailingslashit($postUrl) . '#comment-' . $comment->comment_ID,
            'author' => array(
                "@type" => "Person",
                "image" => esc_url(get_avatar_url($comment->author_ID)),
                "name" => get_comment_author($comment),
                "url" => esc_url(get_author_posts_url($comment->author_ID))
            )
        );
    }

}

add_action('init', 'initSchematicShortcodes');

function initSchematicShortcodes() {
    global $schematicShortcodes;
    $schematicShortcodes = new SchematicShortcodes();
}
