<?php

class WPMSEO_Meta {

    public static $meta_prefix = '_metaseo_meta';
    public static $form_prefix = 'metaseo_wpmseo_';
    public static $meta_length = 156;
    public static $meta_title_length = 69;
    public static $meta_keywords_length = 256;
    public static $meta_length_reason = '';
    
    public static $meta_fields = array(
        'general' => array(
            'snippetpreview' => array(
                'type' => 'snippetpreview',
                'title' => '', // Translation added later.
                'help' => '', // Translation added later.
            ),
            'title' => array(
                'type' => 'textarea',
                'title' => '', // Translation added later.
                'default_value' => '',
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
                'rows' => 2
            ),
            'keywords' => array(
                'type' => 'textarea',
                'title' => '', // Translation added later.
                'default_value' => '',
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
                'rows' => 2
            ),
            'desc' => array(
                'type' => 'textarea',
                'title' => '', // Translation added later.
                'default_value' => '',
                'class' => 'desc',
                'rows' => 3,
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
            ),
            'metaseo_chart' => array(
                'type' => 'metaseo_chart',
                'title' => '', // Translation added later.
                'default_value' => '',
                'class' => 'metaseo_chart',
                'rows' => 2,
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
            ),
        ),
        'social' => array(
            'opengraph-title' => array(
                'type' => 'text',
                'title' => '', // Translation added later.
                'default_value' => '',
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
            ),
            'opengraph-desc' => array(
                'type' => 'textarea',
                'title' => '', // Translation added later.
                'default_value' => '',
                'class' => 'desc',
                'rows' => 3,
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
            ),
            'opengraph-image' => array(
                'type' => 'upload',
                'title' => '', // Translation added later.
                'default_value' => '',
                'class' => 'desc',
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
            ),
            
            'twitter-title' => array(
                'type' => 'text',
                'title' => '', // Translation added later.
                'default_value' => '',
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
            ),
            'twitter-desc' => array(
                'type' => 'textarea',
                'title' => '', // Translation added later.
                'default_value' => '',
                'class' => 'desc',
                'rows' => 3,
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
            ),
            'twitter-image' => array(
                'type' => 'upload',
                'title' => '', // Translation added later.
                'default_value' => '',
                'class' => 'desc',
                'description' => '', // Translation added later.
                'help' => '', // Translation added later.
            ),
            
        ),
        /* Fields we should validate & save, but not show on any form */
        'non_form' => array(
            'linkdex' => array(
                'type' => null,
                'default_value' => '0',
            ),
        ),
    );
    public static $fields_index = array();
    public static $defaults = array();
    private static $social_networks = array(
        'opengraph' => 'opengraph',
        'twitter' => 'twitter',
        'googleplus' => 'google-plus',
    );
    private static $social_fields = array(
        'title' => 'text',
        'description' => 'textarea',
        'image' => 'upload',
    );

    public static function init() {
        add_filter('update_post_metadata', array(__CLASS__, 'remove_meta_if_default'), 10, 5);
        add_filter('add_post_metadata', array(__CLASS__, 'dont_save_meta_if_default'), 10, 4);
    }
    
    public static function get_meta_field_defs($tab, $post_type = 'post') {
        if (!isset(self::$meta_fields[$tab])) {
            return array();
        }

        $field_defs = self::$meta_fields[$tab];

        switch ($tab) {
            case 'non-form':
                // Prevent non-form fields from being passed to forms.
                $field_defs = array();
                break;


            case 'general':
                $options = get_option('wpmseo_titles');
                if ($options['usemetakeywords'] === true) {
                    /* Adjust the link in the keywords description text string based on the post type */
                    $field_defs['metakeywords']['description'] = sprintf($field_defs['metakeywords']['description'], '<a target="_blank" href="' . esc_url(admin_url('admin.php?page=wpmseo_titles#top#post_types')) . '">', '</a>');
                } else {
                    /* Don't show the keywords field if keywords aren't enabled */
                    unset($field_defs['metakeywords']);
                }
                /**
                 * Filter the WPSEO metabox form field definitions for the general tab, backward compatibility
                 *
                 * @deprecated 1.5.0
                 * @deprecated use the 'wpmseo_metabox_entries_general' filter instead
                 * @see        WPMSEO_Meta::get_meta_field_defs()
                 *
                 * @param      array $field_defs Metabox orm definitions.
                 *
                 * @return     array
                 */
                $field_defs = apply_filters('wpmseo_metabox_entries', $field_defs);
                break;


            case 'advanced':
//				global $post;
//
//				$options = WPSEO_Options::get_all();
//
//				if ( ! current_user_can( 'manage_options' ) && $options['disableadvanced_meta'] ) {
//					return array();
//				}
//
//				$post_type = '';
//				if ( isset( $post->post_type ) ) {
//					$post_type = $post->post_type;
//				}
//				elseif ( ! isset( $post->post_type ) && isset( $_GET['post_type'] ) ) {
//					$post_type = sanitize_text_field( $_GET['post_type'] );
//				}
//
//				/* Adjust the no-index 'default for post type' text string based on the post type */
//				$field_defs['meta-robots-noindex']['options']['0'] = sprintf( $field_defs['meta-robots-noindex']['options']['0'], ( ( isset( $options[ 'noindex-' . $post_type ] ) && $options[ 'noindex-' . $post_type ] === true ) ? 'noindex' : 'index' ) );
//
//				/* Adjust the robots advanced 'site-wide default' text string based on those settings */
//				if ( $options['noodp'] !== false || $options['noydir'] !== false ) {
//					$robots_adv = array();
//					foreach ( array( 'noodp', 'noydir' ) as $robot ) {
//						if ( $options[ $robot ] === true ) {
//							// Use translation from field def options - mind that $options and $field_def['options'] keys should be the same!
//							$robots_adv[] = $field_defs['meta-robots-adv']['options'][ $robot ];
//						}
//					}
//					unset( $robot );
//					$robots_adv = implode( ', ', $robots_adv );
//				}
//				else {
//					$robots_adv = __( 'None', 'wordpress-seo' );
//				}
//				$field_defs['meta-robots-adv']['options']['-'] = sprintf( $field_defs['meta-robots-adv']['options']['-'], $robots_adv );
//				unset( $robots_adv );
//
//
//				/* Don't show the breadcrumb title field if breadcrumbs aren't enabled */
//				if ( $options['breadcrumbs-enable'] !== true ) {
//					unset( $field_defs['bctitle'] );
//				}
//
//				global $post;
//
//				if ( empty( $post->ID ) || ( ! empty( $post->ID ) && self::get_value( 'redirect', $post->ID ) === '' ) ) {
//					unset( $field_defs['redirect'] );
//				}
                break;
        }

        return apply_filters('wpmseo_metabox_entries_' . $tab, $field_defs, $post_type);
    }

    public static function sanitize_post_meta($meta_value, $meta_key) {
        $field_def = self::$meta_fields[self::$fields_index[$meta_key]['subset']][self::$fields_index[$meta_key]['key']];
        $clean = self::$defaults[$meta_key];

        switch (true) {
            case ( $meta_key === self::$meta_prefix . 'linkdex' ):
                $int = WPMSEO_Utils::validate_int($meta_value);
                if ($int !== false && $int >= 0) {
                    $clean = strval($int); // Convert to string to make sure default check works.
                }
                break;


            case ( $field_def['type'] === 'checkbox' ):
                // Only allow value if it's one of the predefined options.
                if (in_array($meta_value, array('on', 'off'), true)) {
                    $clean = $meta_value;
                }
                break;


            case ( $field_def['type'] === 'select' || $field_def['type'] === 'radio' ):
                // Only allow value if it's one of the predefined options.
                if (isset($field_def['options'][$meta_value])) {
                    $clean = $meta_value;
                }
                break;


            case ( $field_def['type'] === 'multiselect' && $meta_key === self::$meta_prefix . 'meta-robots-adv' ):
                $clean = self::validate_meta_robots_adv($meta_value);
                break;


            case ( $field_def['type'] === 'text' && $meta_key === self::$meta_prefix . 'canonical' ):
            case ( $field_def['type'] === 'text' && $meta_key === self::$meta_prefix . 'redirect' ):
                // Validate as url(-part).
                $url = WPMSEO_Utils::sanitize_url($meta_value);
                if ($url !== '') {
                    $clean = $url;
                }
                break;


            case ( $field_def['type'] === 'upload' && $meta_key === self::$meta_prefix . 'opengraph-image' ):
                // Validate as url.
                $url = WPMSEO_Utils::sanitize_url($meta_value, array('http', 'https', 'ftp', 'ftps'));
                if ($url !== '') {
                    $clean = $url;
                }
                break;


            case ( $field_def['type'] === 'textarea' ):
                if (is_string($meta_value)) {
                    // Remove line breaks and tabs.
                    // @todo [JRF => Yoast] verify that line breaks and the likes aren't allowed/recommended in meta header fields.
                    $meta_value = str_replace(array("\n", "\r", "\t", '  '), ' ', $meta_value);
                    $clean = WPMSEO_Utils::sanitize_text_field(trim($meta_value));
                }
                break;

            case ( 'multiselect' === $field_def['type'] ):
                $clean = $meta_value;
                break;


            case ( $field_def['type'] === 'text' ):
            default:
                if (is_string($meta_value)) {
                    $clean = WPMSEO_Utils::sanitize_text_field(trim($meta_value));
                }
                break;
        }

        $clean = apply_filters('wpmseo_sanitize_post_meta_' . $meta_key, $clean, $meta_value, $field_def, $meta_key);

        return $clean;
    }

    public static function validate_meta_robots_adv($meta_value) {
        $clean = self::$meta_fields['advanced']['meta-robots-adv']['default_value'];
        $options = self::$meta_fields['advanced']['meta-robots-adv']['options'];

        if (is_string($meta_value)) {
            $meta_value = explode(',', $meta_value);
        }

        if (is_array($meta_value) && $meta_value !== array()) {
            $meta_value = array_map('trim', $meta_value);

            if (in_array('none', $meta_value, true)) {
                // None is one of the selected values, takes priority over everything else.
                $clean = 'none';
            } elseif (in_array('-', $meta_value, true)) {
                // Site-wide defaults is one of the selected values, takes priority over individual selected entries.
                $clean = '-';
            } else {
                // Individual selected entries.
                $cleaning = array();
                foreach ($meta_value as $value) {
                    if (isset($options[$value])) {
                        $cleaning[] = $value;
                    }
                }

                if ($cleaning !== array()) {
                    $clean = implode(',', $cleaning);
                }
                unset($cleaning, $value);
            }
        }

        return $clean;
    }

    public static function remove_meta_if_default($null, $object_id, $meta_key, $meta_value, $prev_value = '') {
        /* If it's one of our meta fields, check against default */
        if (isset(self::$fields_index[$meta_key]) && self::meta_value_is_default($meta_key, $meta_value) === true) {
            if ($prev_value !== '') {
                delete_post_meta($object_id, $meta_key, $prev_value);
            } else {
                delete_post_meta($object_id, $meta_key);
            }

            return true; // Stop saving the value.
        }

        return null; // Go on with the normal execution (update) in meta.php.
    }

    public static function dont_save_meta_if_default($null, $object_id, $meta_key, $meta_value) {
        /* If it's one of our meta fields, check against default */
        if (isset(self::$fields_index[$meta_key]) && self::meta_value_is_default($meta_key, $meta_value) === true) {
            return true; // Stop saving the value.
        }

        return null; // Go on with the normal execution (add) in meta.php.
    }

    public static function meta_value_is_default($meta_key, $meta_value) {
        return ( isset(self::$defaults[$meta_key]) && $meta_value === self::$defaults[$meta_key] );
    }

    public static function get_value($key, $postid = 0) {
        global $post;

        $postid = absint($postid);
        if ($postid === 0) {
            if (( isset($post) && is_object($post) ) && ( isset($post->post_status) && $post->post_status !== 'auto-draft' )) {
                $postid = $post->ID;
            } else {
                return '';
            }
        }

        $custom = get_post_custom($postid); // Array of strings or empty array.

        if (isset($custom[self::$meta_prefix . $key][0])) {
            $unserialized = maybe_unserialize($custom[self::$meta_prefix . $key][0]);
            if ($custom[self::$meta_prefix . $key][0] === $unserialized) {
                return $custom[self::$meta_prefix . $key][0];
            } else {
                return '';
            }
        }

        // Meta was either not found or found, but object/array while not allowed to be.
        if (isset(self::$defaults[self::$meta_prefix . $key])) {
            return self::$defaults[self::$meta_prefix . $key];
        } else {
            return '';
        }
    }

    public static function set_value($key, $meta_value, $post_id) {
        return update_post_meta($post_id, self::$meta_prefix . $key, $meta_value);
    }

    public static function replace_meta($old_metakey, $new_metakey, $delete_old = false) {
        global $wpdb;
        $query = $wpdb->prepare(
                "
				SELECT `a`.*
				FROM {$wpdb->postmeta} AS a
				WHERE `a`.`meta_key` = %s
					AND NOT	EXISTS (
						SELECT DISTINCT `post_id` , count( `meta_id` ) AS count
						FROM {$wpdb->postmeta} AS b
						WHERE `a`.`post_id` = `b`.`post_id`
							AND `meta_key` LIKE %s
							AND `meta_key` <> %s
						GROUP BY `post_id`
					)
				;", $old_metakey, $wpdb->esc_like(self::$meta_prefix . '%'), self::$meta_prefix . 'linkdex'
        );
        $oldies = $wpdb->get_results($query);

        if (is_array($oldies) && $oldies !== array()) {
            foreach ($oldies as $old) {
                update_post_meta($old->post_id, $new_metakey, $old->meta_value);
            }
        }

        // Delete old keys.
        if ($delete_old === true) {
            delete_post_meta_by_key($old_metakey);
        }
    }

    public static function clean_up() {
        global $wpdb;
        $query = $wpdb->prepare(
                "
				SELECT `a`.*
				FROM {$wpdb->postmeta} AS a
				WHERE `a`.`meta_key` = %s
					AND NOT	EXISTS (
						SELECT DISTINCT `post_id` , count( `meta_id` ) AS count
						FROM {$wpdb->postmeta} AS b
						WHERE `a`.`post_id` = `b`.`post_id`
							AND ( `meta_key` = %s
							OR `meta_key` = %s )
						GROUP BY `post_id`
					)
				;", self::$meta_prefix . 'meta-robots', self::$meta_prefix . 'meta-robots-noindex', self::$meta_prefix . 'meta-robots-nofollow'
        );
        $oldies = $wpdb->get_results($query);

        if (is_array($oldies) && $oldies !== array()) {
            foreach ($oldies as $old) {
                $old_values = explode(',', $old->meta_value);
                foreach ($old_values as $value) {
                    if ($value === 'noindex') {
                        update_post_meta($old->post_id, self::$meta_prefix . 'meta-robots-noindex', 1);
                    } elseif ($value === 'nofollow') {
                        update_post_meta($old->post_id, self::$meta_prefix . 'meta-robots-nofollow', 1);
                    }
                }
            }
        }
        unset($query, $oldies, $old, $old_values, $value);

        // Delete old keys.
        delete_post_meta_by_key(self::$meta_prefix . 'meta-robots');

        $query = array();

        foreach (self::$meta_fields as $subset => $field_group) {
            foreach ($field_group as $key => $field_def) {
                if ($field_def['type'] === 'snippetpreview' || !isset($field_def['default_value'])) {
                    continue;
                }

                if ($key === 'meta-robots-adv') {
                    $query[] = $wpdb->prepare(
                            "( meta_key = %s AND ( meta_value = 'none' OR meta_value = '-' ) )", self::$meta_prefix . $key
                    );
                } elseif (isset($field_def['options']) && is_array($field_def['options']) && $field_def['options'] !== array()) {
                    $valid = $field_def['options'];
                    // Remove the default value from the valid options.
                    unset($valid[$field_def['default_value']]);
                    $valid = array_keys($valid);

                    $query[] = $wpdb->prepare(
                            "( meta_key = %s AND meta_value NOT IN ( '" . implode("','", esc_sql($valid)) . "' ) )", self::$meta_prefix . $key
                    );
                    unset($valid);
                } elseif (is_string($field_def['default_value']) && $field_def['default_value'] !== '') {
                    $query[] = $wpdb->prepare(
                            '( meta_key = %s AND meta_value = %s )', self::$meta_prefix . $key, $field_def['default_value']
                    );
                } else {
                    $query[] = $wpdb->prepare(
                            "( meta_key = %s AND meta_value = '' )", self::$meta_prefix . $key
                    );
                }
            }
        }
        unset($subset, $field_group, $key, $field_def);

        $query = "SELECT meta_id FROM {$wpdb->postmeta} WHERE " . implode(' OR ', $query) . ';';
        $meta_ids = $wpdb->get_col($query);

        if (is_array($meta_ids) && $meta_ids !== array()) {
            // WP native action.
            do_action('delete_post_meta', $meta_ids, null, null, null);

            $query = "DELETE FROM {$wpdb->postmeta} WHERE meta_id IN( " . implode(',', $meta_ids) . ' )';
            $count = $wpdb->query($query);

            if ($count) {
                foreach ($meta_ids as $object_id) {
                    wp_cache_delete($object_id, 'post_meta');
                }

                // WP native action.
                do_action('deleted_post_meta', $meta_ids, null, null, null);
            }
        }
        unset($query, $meta_ids, $count, $object_id);

        $query = $wpdb->prepare(
                "SELECT meta_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = %s", self::$meta_prefix . 'meta-robots-adv'
        );
        $oldies = $wpdb->get_results($query);

        if (is_array($oldies) && $oldies !== array()) {
            foreach ($oldies as $old) {
                $clean = self::validate_meta_robots_adv($old->meta_value);

                if ($clean !== $old->meta_value) {
                    if ($clean !== self::$meta_fields['advanced']['meta-robots-adv']['default_value']) {
                        update_metadata_by_mid('post', $old->meta_id, $clean);
                    } else {
                        delete_metadata_by_mid('post', $old->meta_id);
                    }
                }
            }
        }
        unset($query, $oldies, $old, $clean);

        do_action('wpmseo_meta_clean_up');
    }

    public static function array_merge_recursive_distinct() {

        $arrays = func_get_args();
        if (count($arrays) < 2) {
            if ($arrays === array()) {
                return array();
            } else {
                return $arrays[0];
            }
        }

        $merged = array_shift($arrays);

        foreach ($arrays as $array) {
            foreach ($array as $key => $value) {
                if (is_array($value) && ( isset($merged[$key]) && is_array($merged[$key]) )) {
                    $merged[$key] = self::array_merge_recursive_distinct($merged[$key], $value);
                } else {
                    $merged[$key] = $value;
                }
            }
            unset($key, $value);
        }

        return $merged;
    }

    public static function get_post_value($key) {
        return ( array_key_exists($key, $_POST) ) ? $_POST[$key] : '';
    }

}

/* End of class */
