<?php
/*
    dynamic template module
    integrated in core theme
*/
if (!defined('ABSPATH')) exit;

new dynamic($config);
class dynamic {

    private $config;

    public $types;
    private $template_log;

    /* 
        Constructor 
    */
    public function __construct($config) {
        // set config
        $this->config = (object)$config;

        // init template types
        add_action( 'init', array($this, 'init') );
        
        // define and add custom post type
        add_action( 'init', array($this, 'add_custom_post') );
        add_filter( 'manage_edit-dynamic_template_columns', array($this, 'add_column_heading') );
        add_action( "manage_dynamic_template_posts_custom_column", array($this, 'add_column_value'), 10, 2 );
        add_filter( 'pre_get_posts', array($this, 'set_default_sorting') );
        add_action( 'restrict_manage_posts' , array($this, 'add_category_dropdown'), 20, 2 );
        add_action( 'admin_notices', array($this, 'add_backend_header'), 999 );
        add_action( 'admin_enqueue_scripts', array($this, 'load_admin_scripts'), 90 );

        // add wizard
        add_action( 'admin_enqueue_scripts', array($this, 'add_wizard_script'), 99 );
        add_action( 'admin_notices', array($this, 'add_wizard'), 999 );

        // vc
        if (is_callable('vc_map') && is_callable('vc_add_param')) {
            add_action( 'vc_before_init', array($this, 'vc_map_dynamic') );
        }

        // frontend
        add_shortcode( 'dynamic', array($this, 'add_dynamic') );
        add_action( 'template_include', array($this, 'handle_dynamic') );
        add_action( 'dynamic_hook', array($this, 'build_dynamic') );

        // add vc roles
        add_filter( 'greyd_add_vc_posttypes', [$this, 'add_vc_roles'] );
    }
    
    public function init() {
        $this->types = array(
            array(
                'title' => __('Dynamic Templates', 'core'),
                'slug' => 'dynamic',
                'description' => '',
                'color' => 'greyd',
            ),
            array(
                'title' => __('WooCommerce', 'core'),
                'slug' => 'woo',
                'description' => '',
                'color' => 'purple',
            ),
            array(
                'title' => __('Navigation', 'core'),
                'slug' => 'navigation',
                'description' => '',
                'color' => 'blue',
            ),
            array(
                'title' => __('Beitrag', 'core'),
                'slug' => 'single',
                'description' => '',
                'color' => 'blue',
            ),
            array(
                'title' => __('Archiv', 'core'),
                'slug' => 'archives',
                'description' => '',
                'color' => 'blue',
            ),
            array(
                'title' => __('Suche', 'core'),
                'slug' => 'search',
                'description' => '',
                'color' => 'blue',
            ),
            array(
                'title' => __('Weitere', 'core'),
                'slug' => 'more',
                'description' => '',
                'color' => 'blue',
            )
        );
    }

    public function add_vc_roles($rules=[]) {
        return array_merge( $rules, ['dynamic_template'] );
    }
    
    /*
    ====================================================================================
        Register Dynamic Template Posttype
    ====================================================================================
    */
    // register Posttype
    public function add_custom_post() {
        $name       = __('Template', 'core' );
        $names      = __('Templates', 'core' );
        $post_type  = "dynamic_template";
        
        $post_type_labels = array(
            'name'               => $names,
            'singular_name'      => $name,
            'menu_name'          => $names,
            'name_admin_bar'     => $name,
            'add_new'            => sprintf(__( 'Neues %s', 'core' ), $name),
            'add_new_item'       => sprintf(__( '%s erstellen', 'core' ), $name),
            'new_item'           => sprintf(__( 'Neues %s', 'core' ), $name),
            'edit_item'          => sprintf(__( '%s bearbeiten', 'core' ), $name),
            'view_item'          => sprintf(__( 'Zeige %s', 'core'), $name),
            'all_items'          => sprintf(__( 'Alle %s', 'core'), $names),
            'search_items'       => sprintf(__( 'Suche %s', 'core'), $name),
            'parent_item_colon'  => sprintf(__( 'Übergeordnetes %s', 'core'), $name),
            'not_found'          => sprintf(__( 'Keine %s gefunden', 'core'), $names),
            'not_found_in_trash' => sprintf(__( 'Keine %s im Papierkorb gefunden', 'core'), $names),
        );
        $post_type_arguments = [
            'labels'             => $post_type_labels,
            'description'        => __( 'Beschreibung', 'core' ),
            'public'             => false, // doesn't have a permalink etc.
            'exclude_from_search'=> true,
            'publicly_queryable' => false,
            'show_ui'            => true,
            'show_in_menu'       => true,
            'show_in_nav_menus'  => false,
            'show_in_admin_bar'  => false,
            'query_var'          => true,
            'rewrite'            => array( 'slug' => $post_type ),
            'capability_type'    => 'page',
            'taxonomies'          => array( $post_type.'_categories', 'template_type' ),
            'has_archive'        => true,
            'hierarchical'       => true,
            'menu_position'      => 20,
            'menu_icon'          => get_template_directory_uri()."/assets/icon/menuitem_template.svg",
            // 'menu_position'      => 3,
            // 'menu_icon'          => 'dashicons-layout',
            'supports'           => array( 'title' , 'editor', 'custom-fields' ),
            'show_in_rest'          => true,
            'rest_base'             => $post_type,
            'rest_controller_class' => 'WP_REST_Posts_Controller',
        ];
        register_post_type($post_type, $post_type_arguments);

        $name       = __('Kategorie', 'core' );
        $names      = __('Kategorien', 'core' );
        $category_arguments = array(
            'labels'                => array(
                'name'                       => $names,
                'singular_name'              => $name,
                'menu_name'                  => $names,
                'search_items'               => sprintf(__( 'Suche %s', 'core' ), $names),
                'popular_items'              => sprintf(__( 'Beliebte %s', 'core' ), $names),
                'all_items'                  => sprintf(__( 'Alle %s', 'core' ), $names),
                'edit_item'                  => sprintf(__( '%s bearbeiten', 'core' ), $name),
                'update_item'                => sprintf(__( 'Update %s', 'core' ), $name),
                'add_new_item'               => sprintf(__( 'Neue %s', 'core' ), $name),
                'new_item_name'              => sprintf(__( 'Neuer %s Name', 'core' ), $name),
                'separate_items_with_commas' => sprintf(__( '%s durch Kommas trennen', 'core' ), $names),
                'add_or_remove_items'        => sprintf(__( '%s hinzufügen oder entfernen', 'core' ), $name),
                'choose_from_most_used'      => sprintf(__( '%s aus meistgenutzten wählen', 'core' ), $name),
                'not_found'                  => sprintf(__( 'keine %s gefunden.', 'core' ), $names),
            ),
            'hierarchical'          => true,
            'publicly_queryable'    => true,
            'show_ui'               => true,
            'show_in_menu'          => true,
            'show_in_nav_menus'     => false,
            'query_var'             => true,
            'rewrite'               => array( 'slug' => 'template_categories' ),
            'show_in_rest'          => true,
        );
        register_taxonomy( 'template_categories', $post_type, $category_arguments );
        
        $name       = __('Typ', 'core' );
        $names      = __('Typen', 'core' );
        $taxonomy_arguments = array(
            'labels'                => array(
                'name'                       => $names,
                'singular_name'              => $name,
                'menu_name'                  => $names,
                'search_items'               => sprintf(__( 'Suche %s', 'core' ), $names),
                'popular_items'              => sprintf(__( 'Beliebte %s', 'core' ), $names),
                'all_items'                  => sprintf(__( 'Alle %s', 'core' ), $names),
                'edit_item'                  => sprintf(__( '%s bearbeiten', 'core' ), $name),
                'update_item'                => sprintf(__( 'Update %s', 'core' ), $name),
                'add_new_item'               => sprintf(__( 'Neuer %s', 'core' ), $name),
                'new_item_name'              => sprintf(__( 'Neuer %s Name', 'core' ), $name),
                'separate_items_with_commas' => sprintf(__( '%s durch Kommas trennen', 'core' ), $names),
                'add_or_remove_items'        => sprintf(__( '%s hinzufügen oder entfernen', 'core' ), $name),
                'choose_from_most_used'      => sprintf(__( '%s aus meistgenutzten wählen', 'core' ), $name),
                'not_found'                  => sprintf(__( 'keine %s gefunden.', 'core' ), $names),
            ),
            'hierarchical'          => false,
            'publicly_queryable'    => false,
            'show_ui'               => false,
            'show_in_menu'          => false,
            'show_in_nav_menus'     => false,
            'query_var'             => true,
            'rewrite'               => array( 'slug' => 'template_type' ),
        );
        register_taxonomy( 'template_type', $post_type, $taxonomy_arguments );

        // delete wrong template types
        $terms = get_terms(array(
            'taxonomy' => 'template_type',
            'hide_empty' => false,
        ));
        $slugs = array('dynamic', 'navigation', 'single', 'archives', 'search', 'more', 'woo');
        foreach ($terms as $term) {
            if (!in_array($term->slug, $slugs)) {
                wp_delete_term($term->term_id, 'template_type');
            }
        }
        // insert template types if not exist
        foreach ($this->types as $type) {
            if (is_null(term_exists($type['slug'], 'template_type'))) {
                wp_insert_term( $type['title'], 'template_type', array( 'slug' => $type['slug'], 'description'=> $type['description'] ) );
            }
        }
        // add type to template if not exist
        $posts = get_posts( array(
            'posts_per_page'   => -1,
            'post_type'        => $post_type,
            'post_status'      => 'publish',
            'tax_query' => array(
                array(
                    'taxonomy' => 'template_type',
                    'field' => 'slug',
                    // 'terms' => 'dynamic',
                    'operator' => 'NOT EXISTS',
                )
            )
        ) );
        if ($posts) {
            foreach($posts as $post) {
                $slug = $post->post_name;
                $slgs = explode('-', $slug);
                if ($slgs[0] == 'single' || $slgs[0] == 'archives' || $slgs[0] == 'search' || $slgs[0] == 'woo')
                    $ttype = $slgs[0];
                else {
                    if ($slug == 'footer' || strpos($slug, '-menu-offcanvas') > 0) $ttype = 'navigation';
                    else if ($slug == '404' || $slgs[0] == '404' || $slug == 'cookiebar' || $slug == 'compatibility') $ttype = 'more';
                    else $ttype = 'dynamic';
                }
                wp_set_object_terms( $post->ID, $ttype, 'template_type' );
            }
        }
    }

    // Add template slug column after template name
    public function add_column_heading( $columns ) {
        $slug_column = array(
            'dynamic_template_slug' => __( 'Name', 'core' ),
            'dynamic_template_type' => __( 'Typ', 'core' ),
            'dynamic_template_category' => __( 'Kategorien', 'core' ),
            'dynamic_template_usage' => __( 'Verwendung', 'core' )
        );
        $columns = array_slice( $columns, 0, 2, true ) + $slug_column + array_slice( $columns, 2, count( $columns ) - 1, true );

        return $columns;
    }
    
    // Display template slug
    public function add_column_value( $column_name, $post_id ) {
        
        $ttype  = get_the_terms($post_id, 'template_type');
        $slug   = get_post_field( 'post_name', $post_id, 'raw' );
        
        switch ($column_name) {
            case 'dynamic_template_slug':
                // $msg = "edit: ".$slug;
                $span  = "<span>".$slug."</span>";
                $edit  = "<a href='#'  style='display:none' class='dashicons dashicons-edit' onclick='dynamic.editTitle(".$post_id.")'></a>";
                $input = "<input style='width:100%' value=".$slug.">";
                $reset = "<a href='#' class='dashicons dashicons-no' style='color:darkred' onclick='dynamic.resetTitle(".$post_id.", \"".$slug."\")'></a>";
                $set   = "<a href='#' class='dashicons dashicons-saved' onclick='dynamic.setTitle(".$post_id.", \"".$slug."\")'></a>";
                echo "<div id='title_".$post_id."' onmouseenter='dynamic.enterTitle(".$post_id.")' onmouseleave='dynamic.leaveTitle(".$post_id.")'>".$span." ".$edit."</div>
                    <div id='title_edit_".$post_id."' style='display:none;text-align:right'>".$input." ".$reset." ".$set."</div>";
                break;
                
            case 'dynamic_template_type':
                if ($ttype) {
                    $color = '';
                    foreach ($this->types as $type) {
                        if ($ttype[0]->slug == $type['slug']) {
                            $color = $type['color'];
                            break;
                        }
                    }
                    $span = '<span class="greyd_info_box '.$color.'">'.__($ttype[0]->name, 'core').'</span>';
                }
                else $span = '--';
                $edit  = "<a href='#'  style='display:none' class='dashicons dashicons-edit' onclick='dynamic.editType(".$post_id.")'></a>";
                $input = "<select style='width:100%' value=".$ttype[0]->slug.">";
                foreach ($this->types as $type) {
                    if ( $type['slug'] == 'woo' && !class_exists('woocommerce') ) continue;
                    $sel = $type['slug'] == $ttype[0]->slug ? "selected='selected'" : "";
                    $input .= "<option value='".$type['slug']."' ".$sel.">".$type['title']."</option>";
                }
                $input .= "</select>";
                $reset = "<a href='#' class='dashicons dashicons-no' style='color:darkred' onclick='dynamic.resetType(".$post_id.", \"".$ttype[0]->slug."\")'></a>";
                $set   = "<a href='#' class='dashicons dashicons-saved' onclick='dynamic.setType(".$post_id.", \"".$ttype[0]->slug."\")'></a>";
                echo "<div id='type_".$post_id."' onmouseenter='dynamic.enterType(".$post_id.")' onmouseleave='dynamic.leaveType(".$post_id.")'>".$span." ".$edit."</div>
                    <div id='type_edit_".$post_id."' style='display:none;text-align:right'>".$input." ".$reset." ".$set."</div>";
                break;

            case 'dynamic_template_category':
                $cats = get_the_terms($post_id, 'template_categories');
                if ($cats) {
                    $first = "";
                    foreach ($cats as $cat) {
                        echo $first.$cat->name;
                        $first = ", ";
                    }
                }
                else echo '--';
                break;

            case 'dynamic_template_usage':
                if ( !$ttype ) break;
                
                // fallback
                $render     = "--";
                
                // dynamic templates
                if ( $ttype[0]->slug === 'dynamic' ) {
                    
                    $template_log = self::get_template_log($post_id, $slug);
                    $total = self::calc_log_total($template_log);
                    $render = $total."x ".\vc\helper::render_info_popup( self::get_template_log_html($template_log) );
                    
                }
                // system templates
                else {
                    $render = __("System", 'core');
                }
                echo $render;
                break;
        }
    }

    // default post sorting
    public function set_default_sorting($query) {
        global $pagenow;
        if( is_admin() && 'edit.php' == $pagenow && !isset($_GET['orderby']) && isset($_GET['post_type']) && $_GET['post_type'] === 'dynamic_template' ){
            if ( $query->is_main_query() ) {
                // debug($query);
                $query->set( 'orderby', array( 'date' => 'DESC'/*, 'title' => 'ASC'*/ ) );
            }
        }
    }

    // add category filter
    public function add_category_dropdown($post_type){
        
        if ($post_type != "dynamic_template") return;
        
        $tax = 'template_categories';
        $args = array(
               'taxonomy' => $tax,
               'orderby' => 'name',
               'order'   => 'ASC'
           );
        $cats = get_categories($args);
        //debug($cats);
        $current_cat = isset($_GET[$tax]) ? esc_attr($_GET[$tax]) : '';
        
        // render
        echo "<select name='$tax'>";
        echo "<option value=''>"._x( 'Alle Kategorien', 'small', 'core')."</option>";
        if (is_array($cats)) {
            foreach($cats as $cat) {
                echo sprintf(
                    '<option value="%1$s" %2$s >%3$s (%4$s)</option>',
                    $cat->slug,
                    ($current_cat === $cat->slug ? 'selected' : ''),
                    $cat->name,
                    $cat->count
                );
            } 
        }
        echo "</select>";   
    }

    // add Custom Taxonomy Tabs
    public function add_backend_header() {
        $screen = get_current_screen();
        // debug($screen);
        
        // only in template edit screen
        if ( $screen->post_type !== "dynamic_template" ) return false;
        
        // on overview screens
        if ( $screen->base === 'edit' ) {
            /*
                Template Log
            */
            global $wpdb;
            $this->template_log = self::get_all_posts_with_templates();
            // debug($this->template_log);

            /*
                Tabs
            */
            $filter_type = isset($_GET['template_type']) ? $_GET['template_type'] : "";
            $url = get_admin_url(null, '/'.$screen->parent_file);
            echo '<div id="templates_tabs" class="greyd_tabs">
                    <a class="tab '.($filter_type == "" ? 'active' : '').' blue" href="'.$url.'">'.__('Alle', 'core').'</a>';
            foreach ($this->types as $type) {
                if ( $type['slug'] == 'woo' && !class_exists('woocommerce') ) continue;
                echo '<a class="tab '.($filter_type == $type['slug'] ? 'active' : '').' '.$type['color'].'" href="'.$url.'&template_type='.$type['slug'].'">'.$type['title'].'</a>';
            }
            echo '</div>';
            
            /*
                Wizard
            */
            // $this->add_wizzard();
        }
        
        // on edit screens
        if ($screen->base === 'post' && $screen->action !== 'add') {
            
            $post_id    = $_GET['post'];
            $ttype      = get_the_terms($post_id, 'template_type');
            
            if ( !$ttype ) return false;
            
            $ttype = $ttype[0]->slug;
            
            // template type
            $color = '';
            foreach ($this->types as $type) {
                if ($ttype == $type['slug']) {
                    $color = $type['color'];
                    break;
                }
            }
            echo '<span id="template_type" class="greyd_info_box '.$color.'" style="display:none">'.get_the_terms($post_id, 'template_type')[0]->name.'</span>';

            // logging
            if ($ttype === "dynamic") {
                $slug = get_post_field( 'post_name', $post_id, 'raw' );
                $template_log = self::get_template_log($post_id, $slug);
                $render = self::get_template_log_html($template_log);
                echo "<div id='template_log'><script>var template_log=".self::calc_log_total($template_log)."</script>".self::get_template_log_html($template_log)."</div>";
            }
        }
    }
    
    // enqueue scripts
    public function load_admin_scripts() {
        global $config;

        // admin styles
        wp_register_style( 'dynamic_admin_css', get_template_directory_uri().'/assets/css/deprecated/dynamic_admin.css', null, $config->theme_version, 'all' );
        wp_enqueue_style('dynamic_admin_css');

        // register & enqueue admin scripts
        wp_register_script('dynamic_admin_js', get_template_directory_uri().'/assets/js/deprecated/dynamic_admin.js', array('jquery'), $config->theme_version, false);
        wp_enqueue_script('dynamic_admin_js');

        // additional scripts and vars
        $screen = get_current_screen();
        // debug($screen);
        if ($screen->base == 'post') {
            // debug($screen);
            $all_dtags = array(
                'product' => array( 
                    array( 'label' => 'Warenkorb Auszug', 'name'=> 'cart-excerpt', 'type' => 'text' ),
                    array( 'label' => 'Preis', 'name'=> 'price', 'type' => 'text' ),
                    array( 'label' => 'Mehrwertsteuer', 'name'=> 'vat', 'type' => 'text' ),
                    array( 'label' => 'Warenkorb', 'name' => 'cart', 'type' => 'url' ),
                    array( 'label' => 'Woo Ajax Button', 'name' => 'woo_ajax', 'type' => 'url' ),
                ),
            );
            $all_vtags = array(
                'post' => array( 'content', 'thumbnail', 'excerpt', 'categories', 'tags' ),
                'product' => array( 'content', 'thumbnail', 'excerpt', 'categories', 'tags' ),
                'page' => array( 'content', 'thumbnail' )
            );
            $posttypes = \vc\helper::get_dynamic_posttypes();
            if (count($posttypes) > 0) {
                foreach ($posttypes as $posttype) {
                    // debug($posttype);
                    $fields = isset($posttype["fields"]) ? (array)$posttype["fields"] : array();
                    if (count($fields) > 0) {
                        // debug($fields);
                        $dtags = array();
                        foreach ($fields as $field) {
                            if (is_array($field) && $field['type'] != 'headline' && $field['type'] != 'descr' && $field['type'] != 'hr') {
                                $dtags[] = array(
                                    'label' => $field['label'],
                                    'name' => $field['name'],
                                    'type' => $field['type'],
                                );
                            }
                        }
                        // debug($dtags);
                        $all_dtags[$posttype["slug"]] = $dtags;
                    }
                    $supports = isset($posttype["supports"]) ? (array) $posttype["supports"] : array();
                    if (isset($posttype["categories"])) $supports["categories"] = $posttype["categories"];
                    if (isset($posttype["tags"])) $supports["tags"] = $posttype["tags"];
                    if (count($supports) > 0) {
                        // debug($supports);
                        $vtags = array();
                        foreach ($supports as $support) {
                            if ( $support === "custom_taxonomies" ) {
                                foreach ($posttype["custom_taxonomies"] as $taxonomy) {
                                    if ( is_object($taxonomy) ) $taxonomy = (array) $taxonomy;
                                    $vtags[] = "customtax-".$taxonomy['slug'];
                                }
                            }
                            else if ($support === "editor") $vtags[] = 'content';
                            else $vtags[] = $support;
                        }
                        // debug($vtags);
                        $all_vtags[$posttype["slug"]] = $vtags;
                    }
                }
            }
            if (isset($_GET['post'])) {
                $post = get_post($_GET['post']);
                // debug($post);
                if (get_the_terms($post, 'template_type') && count(get_the_terms($post, 'template_type')) > 0 && 
                    (get_the_terms($post, 'template_type')[0]->slug == 'single' || 
                    get_the_terms($post, 'template_type')[0]->slug == 'archives' || 
                    get_the_terms($post, 'template_type')[0]->slug == 'search')) {
                    // debug($post->post_name);
                    $default_dtags = $default_vtags = array();
                    $tmp = explode('-', $post->post_name);
                    if (count($tmp) > 1) {
                        $dpt = "";
                        $found = false;
                        for ($i=1; $i<count($tmp); $i++) {
                            $dpt = ($dpt == "") ? $tmp[$i] : $dpt."-".$tmp[$i];
                            if (isset($all_dtags[$dpt])) {
                                $default_dtags = $all_dtags[$dpt];
                                $found = true;
                            }
                            if (isset($all_vtags[$dpt])) {
                                $default_vtags = $all_vtags[$dpt];
                                $found = true;
                            }
                            if ($found) break;
                        }
                        if (!$found) wp_add_inline_script( 'dynamic_admin_js', 'var default_variable_tags = '.json_encode($all_vtags['post']).';' );
                        else {
                            if (count($default_dtags) > 0) wp_add_inline_script( 'dynamic_admin_js', 'var default_dynamic_tags = '.json_encode($default_dtags).';' );
                            if (count($default_vtags) > 0) wp_add_inline_script( 'dynamic_admin_js', 'var default_variable_tags = '.json_encode($default_vtags).';' );
                        }
                    }
                    else {
                        wp_add_inline_script( 'dynamic_admin_js', 'var default_variable_tags = '.json_encode($all_vtags['post']).';' );
                    }
                }
            }
            wp_add_inline_script( 'dynamic_admin_js', 'var all_dynamic_tags = '.json_encode($all_dtags).', dynamic_tags = [];' );
            wp_add_inline_script( 'dynamic_admin_js', 'var all_variable_tags = '.json_encode($all_vtags).', variable_tags = [];' );

            /**
             * Add default dynamic tags via the filter:
             * 
             * @filter 'greyd_default_dynamic_tags'
             */
            $default_dynamic_tags = apply_filters("greyd_default_dynamic_tags", array('post-type', 'title', 'author', 'date') );
            wp_add_inline_script( 'dynamic_admin_js', 'var all_default_dynamic_tags = '.json_encode($default_dynamic_tags).';' );
        }
        
    }
    
    /*
    ====================================================================================
        Template Log Helpers
    ====================================================================================
    */
    public static function get_all_posts_with_templates() {
        global $wpdb;
        return $wpdb->get_results("
            SELECT ID, post_type, post_title, post_content 
            FROM {$wpdb->posts} 
            WHERE post_status = 'publish' 
            AND (post_content LIKE '% template=\"%' OR post_content LIKE '% post_template=\"%' OR post_content LIKE '%\"template\":\"%')
        ", OBJECT );
    }
    
    public static function get_all_posts_using_template($template_id="", $slug="") {
        if ( empty($template_id) ) return false;
        
        global $wpdb;
        if ( empty($slug) ) $slug = get_post_field( 'post_name', $template_id, 'raw' );
        
        $args = "
            SELECT ID, post_type, post_title, post_content 
            FROM {$wpdb->posts} 
            WHERE post_status = 'publish' 
            AND (
                post_content LIKE '% template=\"{$template_id}\"%' OR post_content LIKE '% post_template=\"{$template_id}\"%' OR post_content LIKE '%\"template\":\"{$template_id}\"%'".
                ( !empty($slug) ? "OR post_content LIKE '% template=\"{$slug}\"%' OR post_content LIKE '% post_template=\"{$slug}\"%' OR post_content LIKE '%\"template\":\"{$slug}\"%'" : "" ).
            ")
         ";
                  
        $result = $wpdb->get_results( $args, OBJECT );
        // debug($result);

        $metas = $wpdb->get_results( 
            $wpdb->prepare("SELECT post_id, meta_value FROM $wpdb->postmeta where meta_key = %s AND meta_value LIKE '{$template_id}|%'", 'menu-item-template')
        );
        foreach( $metas as $meta ) {
            array_push($result, (object)array(
                'ID' => $meta->post_id,
                'post_type' => 'nav_menu_item',
                'post_title' => get_post_field( 'post_title', $meta->post_id, 'raw' ),
                'post_content' => $meta->meta_value
            ));
        }
        // debug($result);

        return $result;
    }
    
    public static function calc_log_total($input) {
        $count = 0;
        if ( is_array($input) ) {
            foreach($input as $val) {
                $count += self::calc_log_total($val);
            }
        }
        if ( is_int($input) ) $count += $input;
        return $count;
    }
    
    public static function get_template_log($template_id="", $slug="") {
        if ( empty($template_id) ) return false;
        
        $posts = self::get_all_posts_using_template($template_id, $slug);
        
        $template_log = [];
        foreach( $posts as $post ) {
            if ( !isset($post->post_content) ) continue;
            
            $id         = intval($post->ID);
            $content    = $post->post_content;
            $log        = [
                "type"  => $post->post_type,
                "title" => $post->post_title,
                "as"    => []
            ];
            
            // as template
            if ( strpos($content, ' template="'.$template_id.'"') !== false || strpos($content, ' template="'.$slug.'"') !== false ) {
                $tmp = explode(' template="'.$template_id.'"', $content);
                $tmp2 = explode(' template="'.$slug.'"', $content);
                $count = count($tmp)-1 + count($tmp2)-1;
                if ( $count > 0 ) $log["as"]["template"] = intval($count);
            }
            // as blocks template
            if ( strpos($content, '"template":"'.$template_id.'"') !== false || strpos($content, '"template":"'.$slug.'"') !== false) {
                $tmp = explode('"template":"'.$template_id.'"', $content);
                $tmp2 = explode('"template":"'.$slug.'"', $content);
                $count = count($tmp)-1 + count($tmp2)-1;
                if ( $count > 0 ) $log["as"]["block_template"] = intval($count);
            }
            // as post overview
            if ( strpos($content, ' post_template="'.$template_id.'"') !== false || strpos($content, ' post_template="'.$slug.'"') !== false ) {
                $tmp = explode(' post_template="'.$template_id.'"', $content);
                $tmp2 = explode(' post_template="'.$slug.'"', $content);
                $count = count($tmp)-1 + count($tmp2)-1;
                if ( $count > 0 ) $log["as"]["post_overview"] = intval($count);
            }
            // as menu template
            if ( strpos($content, $template_id.'|') === 0) {
                $log["as"]["menu_template"] = 1;
            }

            $template_log[$id] = $log;
        }
        
        // form popup
        if ( preg_match( "/form_popup_[\d]+/", $slug ) ) {
            $form_id = intval(preg_replace( "/form_popup_/", "", $slug ));
            $posttype = "tp_forms";
            if ( get_post_type($form_id) === $posttype ) {
                // posts already in log
                if ( isset($template_log[$form_id]) ) {
                    $template_log[$form_id]["as"]["form_popup"] = 1;
                }
                // posts doesn't exist in log
                else {
                    $template_log[$form_id] = [
                        "type"  => $posttype,
                        "title" => get_the_title($form_id),
                        "as"    => [
                            "form_popup" => 1
                        ]
                    ];
                }
            }
        }
        return $template_log;
    }
    
    public static function get_template_log_html($template_log=[]) {
        if ( !is_array($template_log) || count($template_log) == 0 ) return "";
        
        $total = 0;
        $total = self::calc_log_total($template_log);

        ob_start();
        // not used
        if ( $total <= 0 || count($template_log) <= 0 ) {
            echo "<div class='log_title'>".__("Template wird nicht verwendet", 'core')."</div>";
        }
        // used
        else {
            // title
            echo "<div class='log_title ".( $total > 0 ? "toggle" : "")."'>".
                sprintf( __("Template wird %s verwendet", 'core'), "<strong>".sprintf(__("%s mal", 'core'), $total)."</strong>" ).
                "</div>";

            // items
            echo "<div class='log_items'>";
            foreach ($template_log as $id => $log) {
                // debug($log);
                if ($log['type'] == 'nav_menu_item') {
                    $title = get_post_type_object($log['type'])->labels->singular_name;
                    $detail = "";
                    $edit_link = admin_url('nav-menus.php');
                }
                else {
                    $title = $log['title'];
                    $detail = " (".get_post_type_object($log['type'])->labels->singular_name.")";
                    $edit_link = admin_url('post.php?action=edit&post='.$id);
                }
                echo "<div class='log_item'>";
                echo "<a href='$edit_link' target='_blank' title='".__('in neuem Tab öffnen', 'core')."'>".$title."</a>".$detail;
                $modules = [
                    "template"      => __("%s als normales Template", 'core'),
                    "block_template" => __("%s als Blocks Template", 'core'),
                    "menu_template" => __("%s als Dropdown Menü", 'core'),
                    "post_overview" => __("%s in einer Postübersicht", 'core'),
                    "form_popup"    => __("als Formular Pop-up", 'core')
                ];
                foreach( $log["as"] as $module => $count ) {
                    echo "<br>&nbsp;- ".sprintf($modules[$module], sprintf("<strong>%sx</strong>", $count) );
                }
                echo "</div>";
            }
            echo "</div>";
        }
        $render = ob_get_contents();
        ob_end_clean();
        return $render;
    }
    

    /*
    ====================================================================================
        Wizard
    ====================================================================================
    */
    // Add Template Wizard script and vars
    public function add_wizard_script() {
        global $config;
        $screen = get_current_screen();
        if ($screen->base === 'post' || $screen->base === 'edit') {
            $templates = \vc\helper::get_all_posts('dynamic_template');
            $plist = array();
            foreach($templates as $template) {
                $plist[] = array( 'id' => $template->id, 'slug' => $template->slug, 'title' => $template->title );
            }
            wp_add_inline_script( 'dynamic_admin_js', 'var dynamic_templates = '.json_encode($plist).';' );
            wp_enqueue_media();
            wp_enqueue_editor();
        }
    }
    // Add Template Wizard
    public function add_wizard() {
        $screen = get_current_screen();
        if ($screen->post_type == "dynamic_template") {
            if ($screen->base == 'edit') {

                $this->render_wizard();
            }
        }
    }
    // render Templatep Wizard
    public function render_wizard() {
        $asset_url = get_template_directory_uri()."/assets/icon/wizard";

        $pts = \vc\helper::get_all_posttypes();
        $posttypes = "";
        $categories = array();
        foreach ($pts as $posttype) {
            if ($posttype->slug != 'dynamic_template' && 
                $posttype->slug != 'page') {
                if ( $posttype->slug == 'product' && class_exists('woocommerce') ) continue;
                $posttypes .= '<option value="'.$posttype->slug.'">'.$posttype->title.' ('.$posttype->count.')</option>';
                $taxonomies = \vc\helper::get_all_taxonomies($posttype->slug);
                foreach($taxonomies as $taxonomy) {
                    $tax_slug = isset($taxonomy->slug) ? $taxonomy->slug : null;
                    if ( $tax_slug === 'category' || $tax_slug === $posttype->slug.'_category' ) {
                        $categories[$posttype->slug] = "";
                        $terms = \vc\helper::get_all_terms($taxonomy->slug);
                        foreach($terms as $term) {
                            $categories[$posttype->slug] .= '<option value="'.$term->slug.'">'.$term->title.' ('.$term->count.')</option>';
                        }
                        break;
                    }
                }
            }
        }
        $templates = array();
        foreach ($this->types as $type) {
            $posts = \vc\helper::get_all_posts('dynamic_template');
            $temp = "";
            foreach($posts as $post) {
                $ttype = wp_get_post_terms($post->id, 'template_type');
                if ($ttype && $ttype[0]->slug == $type['slug']) {
                    $language = "";
                    if (is_array($post->lang) && $post->lang['display_name'] != "") 
                        $language = ' ('.$post->lang['language_code'].')';
                    $temp .= '<option value="'.$post->id.'">'.$post->title.$language.'</option>';
                }
            }
            if ($temp != "") $templates[$type['slug']] = $temp;
        }
        // debug($posttypes);
        // debug($categories);
        // debug($templates);

        echo '
        <div id="greyd-wizard" class="wizzard template_wizzard">
            <div class="wizzard_box big">
                <div class="wizzard_head">
                    <img class="wizzard_icon" src="'.$asset_url.'/logo_light.svg">
                    <span class="close_wizzard dashicons dashicons-no-alt"></span>
                </div>
                <div class="wizzard_content">
                    <div data-slide="0-error">
                        <h2>'.__('Ooooops!', 'core').'</h2>
                        <div class="greyd_info_box orange" style="width: 100%; box-sizing: border-box;">
                            <span class="dashicons dashicons-warning"></span>
                            <div>
                                <p>'.__('Ein Problem ist aufgetreten:', 'core').'</p><br>
                                <p class="error_msg"></p>
                            </div>
                        </div>
                    </div>
                    <div data-slide="1">
                        <h2>'.__('Neues Template erstellen', 'core').'</h2>
                        <div class="row flex">
                            <div class="element">
                                <div class="label">'.__('Art', 'core').'</div>
                                <select id="type" name="type">
                                    <optgroup label="'.__('Standard', 'core').'">
                                        <option value="dynamic">'.__('Dynamic Template', 'core').'</option>
                                    </optgroup> 
                                    <optgroup label="'.__('Navigation', 'core').'">
                                        <option value="footer">'.__('Footer', 'core').'</option>
                                        <option value="offmenu">'.__('Off-Menü', 'core').'</option>
                                    </optgroup> 
                                    <optgroup label="'.__('Beiträge', 'core').'">
                                        <option value="single">'.__('Beitragseite', 'core').'</option>
                                        <option value="archives">'.__('Beitragsarchiv', 'core').'</option>
                                        <option value="search">'.__('Suchergebnisse', 'core').'</option>
                                    </optgroup> 
                                    <optgroup label="'.__('Weitere', 'core').'">
                                        <option value="404">'.__('404 Seite', 'core').'</option>'.
                                        '<option value="compatibility">'.__('Kompatibilitätsmeldung', 'core').'</option>
                                    </optgroup>';
                        if ( class_exists('woocommerce') ) {
                            echo    '<optgroup label="'.__('WooCommerce', 'core').'">
                                        <option value="woo_product">'.__('Produktseite', 'core').'</option>
                                        <option value="woo_shop">'.__('Shop-Übersicht', 'core').'</option>
                                        <option value="woo_myaccount">'.__('Mein Konto', 'core').'</option>
                                    </optgroup>';
                        }
                        echo    '</select>
                            </div>
                            <div class="element" style="display:none">
                                <div class="label">'.__('Header', 'core').'</div>
                                <select id="header" name="header">
                                    <option value="main">'.__('erster Header', 'core').'</option>
                                    <option value="meta">'.__('zweiter Header', 'core').'</option>
                                </select>
                            </div>
                            <div class="element" style="display:none">
                                <div class="label">'.__('Beitragstyp', 'core').'</div>
                                <select id="post_type" name="post_type">
                                    <option value="0">'._x('alle', 'small', 'core').'</option>
                                    '.$posttypes.'
                                </select>
                            </div>';
                        foreach ($categories as $posttype => $cats) {
                    echo    '<div class="element" style="display:none">
                                <div class="label">'.__('Kategorie', 'core').'</div>
                                <select id="category_'.$posttype.'" class="category" name="category_'.$posttype.'">
                                    <option value="0">'._x('alle Kategorien', 'small', 'core').'</option>
                                    '.$cats.'
                                </select>
                            </div>';
                        }
                        if ( class_exists('woocommerce') ) {
                    echo    '<div class="element" style="display:none">
                                <div class="label">'.__('Art', 'core').'</div>
                                <select id="shop" name="shop">
                                    <option value="category">'._x('nach Kategorie', 'small', 'core').'</option>
                                    <option value="tag">'._x('nach Schlagwort', 'small', 'core').'</option>
                                </select>
                            </div>';
                            $hooks = \vc\helper::get_dynamic_woocommerce_hooks();
                    echo    '<div class="element" style="display:none">
                                <div class="label">'.__('Position', 'core').'</div>
                                <select id="myaccount" name="myaccount">';
                                    foreach($hooks as $hook => $name) {
                                        echo '<option value="'.$hook.'">'.$name.'</option>';
                                    }
                                echo '</select>
                            </div>';
                        }
                    echo' </div>
                        <div class="row flex">
                            <div class="element grow">
                                <div class="label">'.__('Name', 'core').'</div>
                                <input id="name" name="name" type="text" value="" placeholder="'.__('hier eingeben', 'core').'" >
                                <input id="slug" name="slug" type="hidden" value="" >
                                <input id="ttype" name="ttype" type="hidden" value="" >
                            </div>
                        </div>
                        <div class="row flex">
                            <div class="element grow name_double" style="display:none">
                                <div class="greyd_info_box warning">
                                    <span class="dashicons dashicons-warning"></span>
                                    <div>
                                        <div style="margin-bottom:10px"><strong>'.__('Achtung:', 'core').'</strong></div>
                                        <div style="margin-bottom:10px">'.sprintf(__('Das gewählte Template existiert bereits (Name: „%s“). Um das Template neu zu erstellen, musst du erst das vorhandene in den Papierkorb legen. Du kannst es aber auch ganz einfach auf den Standard zurücksetzen oder bearbeiten.', 'core'), '<span class="double_name">Templatename</span>').'</div>
                                        <div style="margin-bottom:10px"><a class="double_reset" style="cursor:pointer;text-decoration:underline">'.__('auf Standard zurücksetzen', 'core').'</a> | <a class="double_edit" href="#">'.__('Template bearbeiten', 'core').'</a></div>
                                    </div>
                                </div>
                            </div>
                            <div class="element grow name_ok">
                                <div class="label">'.__('Inhalt', 'core').'</div>
                                <div class="content_options">
                                    <span id="empty" class="content_option active"><img class="option_icon" src="'.$asset_url.'/new_select.png"><p>'._x('leer', 'small', 'core').'</p></span>
                                    <span id="default" class="content_option"><img class="option_icon" src="'.$asset_url.'/default.png"><p>'._x('GREYD-Vorlage', 'small', 'core').'</p></span>
                                    <span id="copy" class="content_option"><img class="option_icon" src="'.$asset_url.'/copy.png"><p>'._x('Kopie', 'small', 'core').'</p>';
                                foreach ($templates as $ttype => $template) {
                                    echo '<select id="copy_'.$ttype.'" class="copy" name="copy_'.$ttype.'">
                                            <option value="0">'._x('Template wählen', 'small', 'core').'</option>
                                            '.$template.'
                                        </select>';
                                }
                    echo           '</span>
                                </div>
                            </div>
                        </div>

                    </div>
                    <div data-slide="11">
                        <h2>'.__('Glückwunsch!', 'core').'</h2>
                        <div class="install_txt">'.__('Das Template "%s" wurde erfolgreich erstellt.', 'core').'</div>
                        <div class="success_mark"><div class="checkmark"></div></div>
                    </div>
                </div>
                <div class="wizzard_foot">
                    <div data-slide="0-error">
                        <div class="flex">
                            <span class="back button button-secondary">'.__('zurück', 'core').'</span>
                            <span class="close_wizzard reload button button-primary">'.__('schließen', 'core').'</span>
                        </div>
                    </div>
                    <div data-slide="1">
                        <div class="flex">
                            <span class="close_wizzard button button-secondary">'.__('zurück', 'core').'</span>
                            <span class="create button button-primary">'._x('Template erstellen', 'small', 'core').'</span>
                        </div>
                    </div>
                    <div data-slide="11">
                        <div class="flex">
                            <span class="close_wizzard reload button button-secondary">'.__('schließen', 'core').'</span>
                            <a class="finish_wizzard button button-primary" href="" target="_blank">'._x('Template bearbeiten', 'small', 'core').'</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>';
    }
    /*
    ====================================================================================
        Frontend
    ====================================================================================
    */
    // visual composer
    public function vc_map_dynamic() {
        
        vc_map(array(
            "name"          => __('Template', 'core'),
            "description"   => __('Einfügen eines Dynamic Templates', 'core'),
            "base"          => "dynamic",
            "category"      => __('Inhalt', 'core'),
            "icon"          => get_template_directory_uri()."/assets/icon/template.svg",
            'js_view'       => 'VcTemplateView',
            "params"        => array_merge(array(
                array(
                    "type"          => "seperator",
                    "heading"       => __('Template', 'core'),
                    "param_name"    => "seperator_basic",
                ),
                array(
                    "type"          => "select",
                    "heading"       => __('Template', 'core'),
                    "description"   => __('Welches Template soll eingefügt werden? Templates kannst du links unter "Templates" erstellen.', 'core'),
                    "param_name"    => "template",
                    'holder'        => 'div',
                    'options'         => \vc\helper::get_vc_templates((isset($_POST['post_id']) ? $_POST['post_id'] : false)),
                    "edit_field_class" => 'vc_col-xs-6',
                ),
                array(
                    'type'          => 'checkbox',
                    'heading'       => __('Seitliches Gutter', 'core'),
                    "description"   => __('Durch diese Option kannst du das Template seitlich bündig darstellen.', 'core'),
                    'param_name'    => 'options',
                    'value' => array(
                        // _x('erste Zeile auf volle Breite setzen', 'small', 'core') => 'ignore_row',
                        _x('ignorieren (empfohlen)', 'small', 'core') => 'ignore_gutter',
                    ),
                    'edit_field_class' => 'vc_col-xs-6',
                ),
                
                array(
                    "type"          => "seperator",
                    "heading"       => __('Inhalte', 'core'),
                    "param_name"    => "seperator_content",
                ),
                // dynamic dynamic content
                array(
                    'type'          => 'dynamic',
                    'param_name'    => 'dynamic_dynamic_content',
                    'heading'       => __( 'Einbindung', 'core' ),
                    'value'         => 'static',
                    'params'        => array(
                        'type' => 'dynamic_content',
                        'field' => 'dynamic_content',
                        'static' => _x( 'Inhalte eingeben', 'small', 'core'),
                        'dynamic' => _x( 'Inhalte dynamisch lassen', 'small', 'core' ),
                    ),
                ),
                // dynamic content
                array(
                    'type'          => 'dynamic_content',
                    'param_name'    => 'dynamic_content',
                    'params'        => array(
                        'field' => 'template',
                    ),
                    'edit_field_class' => 'vc_col-xs-12',
                ),
                array(
                    'type'          => 'textarea',
                    'heading'       => __('Backend-Beschreibung', 'core'),
                    'description'   => __('Die Beschreibung dient der Übersichtlichkeit im Backend Editor.', 'core'),
                    'param_name'    => 'description',
                    'holder'        => 'div',
                    'group'         => __('Info', 'core'),
                ), 
            ),
            \vc\helper::trigger_action_params_vc()
            )
        ));
    }

    // resolve all content from templates
    public static function get_enqueue_content() {
        $content = "";

        $template = self::get_dynamic_name();
        if ($template != "") {
            $template = self::get_template($template);
            if (count($template) > 0 && isset($template[0]->post_content))
                $content = $template[0]->post_content;
        }
        // single & archive pages
        if (is_page() || is_single()) { 
            global $post;
            if ($post != "" && isset($post->post_content))
                $content .= $post->post_content;
        }
        // footer
        if (get_theme_mod('navi_footer_mode', 'on') != 'none' && get_theme_mod('navi_footer_content', '') != '') {
            $template = self::get_template('footer');
            if (count($template) > 0 && isset($template[0]->post_content))
                $content .= $template[0]->post_content;
        }
        // header
        if (get_theme_mod('navi_header_mode', 'on')  != 'none' && get_theme_mod('navi_header_off_content', '') != '') {
            $template = self::get_template('main-menu-offcanvas');
            if (count($template) > 0 && isset($template[0]->post_content))
                $content .= $template[0]->post_content;
        }
        // header sec
        if (get_theme_mod('navi_header_sec_mode', 'none') != 'none' && get_theme_mod('navi_header_sec_off_content', '') != '') {
            $template = self::get_template('meta-menu-offcanvas');
            if (count($template) > 0 && isset($template[0]->post_content))
                $content .= $template[0]->post_content;
        }

        return $content;
    }

    // check content for enqueueing
    public static function check_enqueue_template($content, $sc) {
        $found = false;
        // check shorcode list
        if (!is_array($sc)) $sc = [$sc];
        foreach ($sc as $s) {
            if (has_shortcode($content, esc_attr($s))) {
                // debug('found '.$s);
                return true;
            }
        }
        // check templates
        if (!$found) {
            $shortcodes = \vc\helper::get_shortcodes($content, array( 'dynamic' ));
            if ($shortcodes) {
                // debug($shortcodes);
                foreach ($shortcodes as $shortcode) {
                    // debug($shortcode);
                    if ($shortcode['atts'] != "") {
                        foreach ($shortcode['atts'] as $att) {
                            // debug($att['key'].' = '.$att['value']);
                            if ($att['key'] == 'template') {
                                $slug = $att['value'];
                                $template = self::get_template($slug);
                                if (count($template) > 0 && isset($template[0]->post_content)) {
                                    $found = self::check_enqueue_template($template[0]->post_content, $sc);
                                    if ($found) return true;
                                }
                            }
                        }
                    }
                }
            }
        }
        return $found;
    }

    // shortcode
    public static function add_dynamic($atts=[], $content='') {
        $template_name  = isset($atts['template']) ? esc_attr($atts['template']) : '';
        
        $has_blocks = false;
        if (!empty($template_name)) {
            // get template
            $template = self::get_template($template_name);
            // debug($template_name);
            // debug($template);
            if ($template && !empty($template[0])) {
                if (!function_exists('register_block_type') || !has_blocks($template[0]->post_content))
                    $content = self::process_shortcode_content($atts, $template[0]);
                else {
                    $has_blocks = true;
                    if (method_exists('\greyd\blocks\render', 'process_blocks_content')) 
                        $content = \greyd\blocks\render::process_blocks_content($atts, $template[0]);
                    else if (method_exists('\greyd\blocks\dynamic\Render', 'process_blocks_content')) 
                        $content = \greyd\blocks\dynamic\Render::process_blocks_content($atts, $template[0]);
                    else if (method_exists('\Greyd\Dynamic\Render_Blocks', 'process_blocks_content'))
                        $content = \Greyd\Dynamic\Render_Blocks::process_blocks_content($atts, $template[0]);
                    else 
                        $content = do_blocks($template[0]->post_content);
                }
            }
            else {
                $content = "<div class='container'><div class='message info'>".
                                sprintf(__("Kein Template mit dem Namen „%s“ gefunden.", 'core'), "<strong>".$template_name."</strong>").
                            "</div></div>";
            }
        }
        if ($has_blocks) {
            return $content;
        }
        else {
            $options        = isset($atts['options']) ? esc_attr($atts['options']) : '';
            $options        = explode( ',', $options );
            $classes        = array();
            $trigger_events = \vc\helper::make_trigger_action($atts);
            // if (in_array('ignore_row', $options)) $classes[] = 'ignore_row';
            if (in_array('ignore_gutter', $options)) $classes[] = 'ignore_gutter';
            return "<div class='dynamic ".implode(' ', $classes)."' $trigger_events>".$content."</div>";
        }
    }
    
    // system templates
    public function handle_dynamic($template) {
        // debug("hooking ...");
        // debug($template);
        // disable for certain custom post types
        $disable = [ 'rbx', 'form_gen' ];
        $disable = apply_filters('tp_dynamic_exclude_post_type', $disable) ;
        if (in_array(get_post_type(), $disable)) return $template;
        // enable dynamic template
        if (is_archive() || is_category() || is_single() || is_search() || is_404()) {
            if (strpos($template, "index.php") || !file_exists($template))
                $template = get_template_directory() . '/inc/deprecated/template.php';
        }
        $template = apply_filters('tp_dynamic_edit_template', $template) ;
        return $template;
    }
    public function build_dynamic() {
        $atts['template'] = self::get_dynamic_name();
        echo self::add_dynamic($atts);
    }

    /*
    ====================================================================================
        Helper functions
    ====================================================================================
    */
    
    public static function get_dynamic_name() {

        $cache = wp_cache_get( 'get_dynamic_name', 'greyd' );
        if ( $cache ) {
            return $cache;
        }

        global $wp_query;
        // debug($wp_query);
        // default template names
        // {404|single|archives|search}-{custom_post_type|''}-{category|''}
        $template_name = $tax = '';
        
        // name base
        if (is_404())           return '404';
        else if (is_search())   $template_name = 'search';
        else if (is_archive())  $template_name = 'archives';
        else if (is_single())   $template_name = 'single';
        
        // get post type
        $post_type = get_post_type();
        if (is_post_type_archive()) $post_type = $wp_query->query['post_type'];
        
        // add first category to template name
        if (is_single()) {
            if ($post_type != '' && $post_type != 'page' && isset($wp_query->queried_object)) {
                $categories = \vc\helper::get_categories($wp_query->queried_object);
                $tax        = isset($categories) && is_array($categories) && count($categories) > 0 ? $categories[0]->slug : '';
            }
        }
        else if (is_archive()) {
            if (isset($wp_query->query[$post_type.'_category'])) {
                $tax        = $wp_query->query[$post_type.'_category'];
            }
            else if ( is_tax() ) {
                $tax        = $wp_query->query_vars['taxonomy'];
                $post_type  = get_taxonomy( $tax )->object_type[0];
                $tax        = $wp_query->query[$tax];
            }
            else {
                $category   = get_category(get_query_var('cat'), false);
                $tax        = ($category && !is_wp_error($category)) ? $category->slug : '';
            }
        }
        
        // if custom post type -> add cpt to default name
        if ( !empty($template_name) && !empty($post_type) && $post_type != 'post' && $post_type != 'page') {
            $template_name = $template_name.'-'.$post_type; // -{custom_post_type}
        }
        // if tax is queried -> add taxonomy to default name
        if ( !empty($template_name) && !empty($tax) ) {
            $template_name = $template_name.'-'.$tax; // -{taxonomy}
        }
        
        $template_name = apply_filters('tp_dynamic_edit_template_name', $template_name) ;
        
        // $archive = is_archive() ? "yes" : "no";
        // $category = is_category() ? "yes" : "no";
        // $single = is_single() ? "yes" : "no";
        // $search = is_search() ? "yes" : "no";
        // $post_type_archive = is_post_type_archive() ? "yes" : "no";
        // debug("is_archive: ".$archive);
        // debug("is_category: ".$category);
        // debug("is_single: ".$single);
        // debug("is_search: ".$search);
        // debug("is_post_type_archive: ".$post_type_archive);
        // debug("post_type: ".$post_type);
        // debug("template_name: ".$template_name);


        wp_cache_set( 'get_dynamic_name', $template_name, 'greyd' );

        return $template_name;
    }

    public static function get_template($template_name) {

        $cache = wp_cache_get( 'dynamic_template_'.$template_name, 'greyd' );
        if ( $cache ) {
            return $cache;
        }

        if ($template_name != "404" && is_numeric($template_name)) {
            // debug("template by id");
            $template = array(get_post($template_name));
        }
        else {
            // debug("template by slug");
            $template = get_posts(array(
                'name'        => $template_name,
                'post_type'   => 'dynamic_template',
                'post_status' => 'publish',
                'numberposts' => 1
            ));
            // if template name is copy (eg. footer-2) search for parent (footer)
            if (!$template && strpos($template_name, '-') > 0) {

                /**
                 * Get alternate template name if the current archive term
                 * has a parent term.
                 * @since 1.4.1
                 */
                if ( is_archive() && strpos( $template_name, 'archives-' ) === 0 ) {

                    $post_type = get_post_type();
                    $term_slug = explode( '-', $template_name, 2 )[1];
                    $tax_name  = $post_type === 'post' ? 'category' : $post_type . '_category';

                    $term = get_term_by( 'slug', $term_slug, $tax_name );
                    if ( $term && isset( $term->parent ) && !empty( $term->parent ) ) {
                        $term = get_term_by( 'term_id', $term->parent, $tax_name );
                        if ( $term && isset( $term->slug ) ) {
                            $template_name = $term_slug = explode( '-', $template_name, 2 )[0] . '-' . $term->slug . '-1';
                        }
                    }
                }
                
                // debug($template_name);
                $alternate_name = explode('-', $template_name);
                array_pop($alternate_name);
                $alternate_name = implode('-', $alternate_name);
                $template = self::get_template($alternate_name);
            }
            // if still no template found, search for copies
            if (!$template) {
                $i = 1;
                while ($i <= 10) {
                    $template = get_posts(array(
                        'name'        => $template_name."-".$i,
                        'post_type'   => 'dynamic_template',
                        'post_status' => 'publish',
                        'numberposts' => 1
                    ));
                    if ($template) {
                        break;
                    }
                    $i++;
                }
            }
        }

        wp_cache_set( 'dynamic_template_'.$template_name, $template, 'greyd' );

        // debug($template);
        return $template;
    }

    public static function process_shortcode_content($atts, $template) {
        // dynamic content
        $content = $template->post_content;
        // debug($atts);
        $dynamic_content = isset($atts['dynamic_content']) ? esc_attr($atts['dynamic_content']) : '';
        $dynamic_values = explode('::', $dynamic_content);
        if ($dynamic_values[0] == $atts['template'] && count($dynamic_values) > 1) {
            $content = self::content_match_dynamic($dynamic_values, $content);
            // debug($content);
        }
        // rows in templates
        $term_obj = $template && is_object($template) ? get_the_terms($template->ID, 'template_type') : null;
        if ($term_obj && isset($term_obj[0]) && $term_obj[0]->slug == 'dynamic') {
            $shortcodes = \vc\helper::get_shortcodes($content, array( 'vc_row' ));
            // debug($shortcodes);
            if ($shortcodes) {
                for ($j=0; $j<count($shortcodes); $j++) {
                    // debug($shortcodes[$j]['atts']);
                    $found = false;
                    if ($shortcodes[$j]['atts'] != "") {
                        foreach ($shortcodes[$j]['atts'] as $att) {
                            if ($att['key'] == 'row_type') {
                                $found = true;
                                break;
                            }
                        }
                    }
                    if (!$found) {
                        // debug($shortcodes[$j]['raw']);
                        $raw = explode('][', $shortcodes[$j]['raw']);
                        $old = $raw[0];
                        $raw[0] = str_replace('[vc_row', '[vc_row row_type="'.get_theme_mod('grid_row_template_default', 'row_xxl').'" ', $old);
                        $raw = implode('][', $raw);
                        $content = str_replace($shortcodes[$j]['raw'], $raw, $content);
                    }
                }
            }
        }
        // dynamic tags
        if (is_archive() || is_search()) {
            global $wp_query;
            // debug($wp_query);
            $tags = \vc\helper::get_dynamic_tags('archive');
            // debug(array_keys($tags));
            $content = self::content_match_tags($wp_query, $tags, $content);
        }
        else if (is_single()) {
            $post = get_post(get_the_ID());
            // debug($post);
            // dynamic posttype tags
            $dtags = \vc\helper::get_dynamic_posttype_tags($post);
            // debug($dtags);
            // standard tags
            $tags = \vc\helper::get_dynamic_tags('single');
            // debug(array_keys($tags));
            $content = self::content_match_tags($post, $dtags, $content);
            $content = self::content_match_tags($post, $tags, $content);
        }

        // fix css
        $styles = "";
        \enqueue::add_custom_style(\vc\helper::get_vc_custom_css($content));
                
        $content = shortcode_unautop(preg_replace('/<\/?p\>/', "\n", $content));
        return do_shortcode($content).$styles;
    }

    public static function content_match_dynamic($dynamic_values, $content) {
        $shortcodes = \vc\helper::get_dynamic_shortcodes($content);
        // debug($shortcodes);
        if ($shortcodes) {

            // set dynamic_content atts for nested templates
            for ($j=0; $j<count($shortcodes); $j++) {
                if ($shortcodes[$j]['shortcode'] == 'dynamic' && is_array($shortcodes[$j]['atts'])) {
                    // debug($shortcodes[$j]);

                    foreach ((array)$shortcodes[$j]['atts'] as $att) {

                        // only handle dynamic content
                        if ($att['key'] != 'dynamic_dynamic_content') continue;

                        // get the arguments
                        $args = explode('|', $att['value']);
                        if ( count($args) < 3 ) break;

                        // get the template
                        list( $field, $type, $slug ) = $args;
                        $template = self::get_template($slug);
                        if ( !$template) break;

                        // vars
                        $value      = $dynamic_values;
                        $value[0]   = $slug;
                        $value      = implode('::', $value);
                        $old        = $shortcodes[$j]['raw'];
                        $found      = false;

                        // replace tags
                        foreach ( (array)$shortcodes[$j]['atts'] as $att) {
                            if ($att['key'] == $field) {
                                $found = true;
                                $new = str_replace($att['value'], $value, $old);
                                break;
                            }
                        }
                        if (!$found) {
                            $new = str_replace('['.$shortcodes[$j]['shortcode'].' ', '['.$shortcodes[$j]['shortcode'].' '.$field.'="'.$value.'" ', $old);
                        }
                        $content = str_replace($old, $new, $content);
                        $shortcodes[$j]['raw'] = $new;

                        break;
                    }
                }
            }

            // set dynamic_content values for this template
            for ($i=1; $i<count($dynamic_values); $i++) {

                $args   = explode('|', $dynamic_values[$i]);
                $field  = $args[0];
                $type   = $args[1];
                $title  = $args[2];
                $value  = rawurldecode($args[3]);
                $key    = $field.'|'.$type.'|'.$title;

                if ($value != "") {
                    for ($j=0; $j<count($shortcodes); $j++) {
                        if ($shortcodes[$j]['shortcode'] == 'dynamic') continue;
                        // debug($shortcodes[$j]);

                        if ($shortcodes[$j]['atts'] != "" && is_array($shortcodes[$j]['atts'])) {
                            foreach ($shortcodes[$j]['atts'] as $att) {
                                if ($att['value'] == $key) {
                                    // debug($shortcodes[$j]);

                                    $raw    = explode('][', $shortcodes[$j]['raw']);
                                    $found  = false;
                                    // $old = $raw[0];

                                    foreach ( (array)$shortcodes[$j]['atts'] as $att) {
                                        if ($att['key'] == $field) {
                                            $found  = true;
                                            $old    = $raw[0];
                                            $new    = str_replace($att['value'], $value, $old);
                                            break;
                                        }
                                    }
                                    if (!$found) {
                                        if ($field == 'content') {
                                            if (isset($shortcodes[$j]['content'])) {
                                                $old = $raw[0];
                                                $new = str_replace($shortcodes[$j]['content'], $value, $old);
                                            }
                                            else {
                                                $raw = array( implode('][', $raw) );
                                                $old = $raw[0];
                                                $new = str_replace('][', ']'.$value.'[', $old);
                                                $shortcodes[$j]['content'] = $value;
                                            }
                                        }
                                        else {
                                            $old = $raw[0];
                                            $val = $value;
                                            if ($type == 'textarea') $val = str_replace(array('"', "''"), '``', $val);
                                            $new = str_replace('['.$shortcodes[$j]['shortcode'].' ', '['.$shortcodes[$j]['shortcode'].' '.$field.'="'.$val.'" ', $old);
                                        }
                                    }
                                    $content    = str_replace($old, $new, $content);
                                    $raw[0]     = $new;
                                    $shortcodes[$j]['raw'] = implode('][', $raw);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        return $content;
    }

    public static function content_match_tags($post, $tags, $content) {
        $shortcodes = \vc\helper::get_dynamic_shortcodes($content);
        // debug($shortcodes);
        if ($shortcodes) {
            for ($i=0; $i<count($shortcodes); $i++) {
                // debug($shortcodes[$i]);
                if ($shortcodes[$i]['shortcode'] == 'dynamic') continue;
                if ($shortcodes[$i]['atts'] != "") {
                    foreach ( (array)$shortcodes[$i]['atts'] as $att) {
                        $oldval = $att['value'];
                        $newval = self::match_tags($post, $tags, $oldval);
                        if ($oldval != $newval) {
                            $raw        = explode('][', $shortcodes[$i]['raw']);
                            $old        = $raw[0];
                            $new        = str_replace($att['key'].'="'.$oldval.'"', $att['key'].'="'.$newval.'"', $old);
                            $content    = str_replace($old, $new, $content);
                            $raw[0]     = $new;
                            $shortcodes[$i]['raw'] = implode('][', $raw);
                            $att['value'] = $newval;
                        }
                    }
                }
                if (isset($shortcodes[$i]['content'])) {
                    $oldval = $shortcodes[$i]['content'];
                    $newval = self::match_tags($post, $tags, $oldval);
                    if ($oldval != $newval) {
                        $raw        = explode('][', $shortcodes[$i]['raw']);
                        $old        = $raw[0];
                        $new        = str_replace(']'.$oldval.'[', ']'.$newval.'[', $old);
                        $content    = str_replace($old, $new, $content);
                        $raw[0]     = $new;
                        $shortcodes[$i]['raw'] = implode('][', $raw);
                        $shortcodes[$i]['content'] = $newval;
                    }
                }
            }
        }
        return $content;
    }

    /**
     * Replace all dynamic tags of a string.
     * Supports simple tags ( eg. _categories_ ) AND advanced tags with parameters ( eg. _categories|vert|1_ )
     * 
     * @param WP_Post $post     Post object.
     * @param array   $tags     Array of dynamic tags to match and replace. @see helper::get_dynamic_tags()
     * @param string  $value    Content that holds the unprocessed dynamic tags (eg. post_content).
     * 
     * @return string $newval   Content with all dynamic tags replaced.
     */
    public static function match_tags($post, $tags, $value) {
        $oldval = $newval = $value;

        $tag_slugs = array_filter(
            array_keys($tags),
            function($k) {
                return !empty($k);
            }
        );
        $regex = '/(?<!vc)(\|title:[^:|"\[\]]*?)?_('.join('|', $tag_slugs) .')(\|.*?)?_(?!box)/';
        
        preg_match_all( $regex, $oldval, $matches );
        
        if ( count($matches[0]) > 0 ) {
            for ( $i = 0; $i < count($matches[0]); $i++ ) {
                
                // get vars
                $whole_tag  = $matches[0][$i];
                $tag        = $matches[2][$i];
                $params     = $matches[3][$i];
                if ( empty($tag) ) continue;
                
                // get value
                if ( !isset($tags[$tag]) ) {
                    $value = "";
                }
                else if ( is_callable($tags[$tag]) ) {
                    $value = call_user_func( $tags[$tag], $post, $params, $tag );
                }
                else {
                    try {
                        $value = strval( $tags[$tag] );
                    } catch (Exception $e) {
                        $value = "";
                    }
                }

                /**
                 * If the first regex group '(\|title:|)' holds a value, the tag is
                 * inside of a 'vc_link'-picker. If this is true, the value needs
                 * to be urlencoded in order to work properly.
                 * 
                 * @since 1.0.5
                 */
                if ( !empty($matches[1][$i]) ) {
                    $value = $matches[1][$i].\vc\helper::vc_encode($value);
                }
                else {
                    $value = wp_specialchars_decode($value, ENT_QUOTES);
					$value = str_replace( [ '[', ']', '(', ')' ], '', $value );
                }
                
                // assign value
                $newval = str_replace( $whole_tag, $value, $newval );
            }
        }
        
        return $newval;
    }


}
