<?php

namespace WPUM\Carbon_Fields\Helper;

use WPUM\Carbon_Fields\Datastore\Datastore;
use WPUM\Carbon_Fields\Exception\Incorrect_Syntax_Exception;
use \WP_Query;
/**
 * Helper functions and main initialization class.
 */
class Helper
{
    /**
     * Get a field from a specific container type or id
     *
     * @param  string  $container_type Container type to search in. Optional if $container_id is supplied
     * @param  string  $container_id   Container id to search in. Optional if $container_type is supplied
     * @param  string  $field_name     Field name to search for
     * @return boolean
     */
    public static function get_field($container_type, $container_id, $field_name)
    {
        \WPUM\Carbon_Fields\Carbon_Fields::verify_fields_registered();
        $repository = \WPUM\Carbon_Fields\Carbon_Fields::resolve('container_repository');
        if ($container_id) {
            return $repository->get_field_in_container($field_name, $container_id);
        }
        return $repository->get_field_in_containers($field_name, $container_type);
    }
    /**
     * Get a clone of a field with a value loaded.
     * WARNING: The datastore is cloned!
     *
     * @param  int    $object_id      Object id to get value for (e.g. post_id, term_id etc.)
     * @param  string $container_type Container type to search in. Optional if $container_id is supplied
     * @param  string $container_id   Container id to search in. Optional if $container_type is supplied
     * @param  string $field_name     Field name to search for
     * @return mixed
     */
    public static function get_field_clone($object_id, $container_type, $container_id, $field_name)
    {
        $field = static::get_field($container_type, $container_id, $field_name);
        if (!$field) {
            return null;
        }
        $clone = clone $field;
        if ($object_id !== null) {
            $clone->set_datastore(clone $clone->get_datastore(), $clone->has_default_datastore());
            $clone->get_datastore()->set_object_id($object_id);
        }
        return $clone;
    }
    /**
     * Execute an action with a clone of a field with a value loaded.
     * WARNING: The datastore reference is kept!
     *
     * @param  int      $object_id      Object id to get value for (e.g. post_id, term_id etc.)
     * @param  string   $container_type Container type to search in. Optional if $container_id is supplied
     * @param  string   $container_id   Container id to search in. Optional if $container_type is supplied
     * @param  string   $field_name     Field name to search for
     * @param  \Closure $action         Action to execute
     * @return void
     */
    public static function with_field_clone($object_id, $container_type, $container_id, $field_name, $action)
    {
        $field = static::get_field($container_type, $container_id, $field_name);
        if (!$field) {
            return;
        }
        $clone = clone $field;
        $datastore = $clone->get_datastore();
        $datastore_object_id = $datastore->get_object_id();
        if ($object_id !== null) {
            $datastore->set_object_id($object_id);
        }
        $result = $action($clone);
        if ($object_id !== null) {
            $datastore->set_object_id($datastore_object_id);
        }
        return $result;
    }
    /**
     * Get a value formatted for end-users
     *
     * @param  int    $object_id      Object id to get value for (e.g. post_id, term_id etc.)
     * @param  string $container_type Container type to search in
     * @param  string $container_id
     * @param  string $field_name     Field name
     * @return mixed
     */
    public static function get_value($object_id, $container_type, $container_id, $field_name)
    {
        return static::with_field_clone($object_id, $container_type, $container_id, $field_name, function ($field) {
            if (!$field) {
                return '';
            }
            $field->load();
            return $field->get_formatted_value();
        });
    }
    /**
     * Set value for a field
     *
     * @param  int    $object_id      Object id to get value for (e.g. post_id, term_id etc.)
     * @param  string $container_type Container type to search in
     * @param  string $container_id
     * @param  string $field_name     Field name
     * @param  array  $value          Field expects a `value_set`. Complex_Field expects a `value_tree` - refer to DEVELOPMENT.md
     * @return void
     */
    public static function set_value($object_id, $container_type, $container_id, $field_name, $value)
    {
        static::with_field_clone($object_id, $container_type, $container_id, $field_name, function ($field) use($container_type, $container_id, $field_name, $value) {
            if (!$field) {
                $container_message = $container_id ? 'in container with id "' . $container_id . '"' : 'in containers of type "' . $container_type . '"';
                Incorrect_Syntax_Exception::raise('Could not find a field which satisfies the supplied pattern ' . $container_message . ': ' . $field_name);
                return;
            }
            $field->set_value($value);
            $field->save();
        });
    }
    /**
     * Shorthand for get_post_meta().
     * Uses the ID of the current post in the loop.
     *
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function get_the_post_meta($name, $container_id = '')
    {
        return static::get_post_meta(get_the_ID(), $name, $container_id);
    }
    /**
     * Get post meta field for a post.
     *
     * @param  int    $id           Post ID
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function get_post_meta($id, $name, $container_id = '')
    {
        return static::get_value($id, 'post_meta', $container_id, $name);
    }
    /**
     * Set post meta field for a post.
     *
     * @param  int    $id           Post ID
     * @param  string $name         Field name
     * @param  array  $value
     * @param  string $container_id
     */
    public static function set_post_meta($id, $name, $value, $container_id = '')
    {
        return static::set_value($id, 'post_meta', $container_id, $name, $value);
    }
    /**
     * Get theme option field value.
     *
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function get_theme_option($name, $container_id = '')
    {
        return static::get_value(null, 'theme_options', $container_id, $name);
    }
    /**
     * Set theme option field value.
     *
     * @param  string $name         Field name
     * @param  array  $value
     * @param  string $container_id
     */
    public static function set_theme_option($name, $value, $container_id = '')
    {
        return static::set_value(null, 'theme_options', $container_id, $name, $value);
    }
    /**
     * Get network option field value for the main site.
     *
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function get_the_network_option($name, $container_id = '')
    {
        $id = \defined('SITE_ID_CURRENT_SITE') ? SITE_ID_CURRENT_SITE : 1;
        return static::get_network_option($id, $name, $container_id);
    }
    /**
     * Get network option field value for a site.
     *
     * @param  string $id           Site ID
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function get_network_option($id, $name, $container_id = '')
    {
        return static::get_value($id, 'network', $container_id, $name);
    }
    /**
     * Set network option field value for a site.
     *
     * @param  string $id           Site ID
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function set_network_option($id, $name, $value, $container_id = '')
    {
        return static::set_value($id, 'network', $container_id, $name, $value);
    }
    /**
     * Get term meta field for a term.
     *
     * @param  int    $id           Term ID
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function get_term_meta($id, $name, $container_id = '')
    {
        return static::get_value($id, 'term_meta', $container_id, $name);
    }
    /**
     * Set term meta field for a term.
     *
     * @param  int    $id           Term ID
     * @param  string $name         Field name
     * @param  array  $value
     * @param  string $container_id
     */
    public static function set_term_meta($id, $name, $value, $container_id = '')
    {
        return static::set_value($id, 'term_meta', $container_id, $name, $value);
    }
    /**
     * Get user meta field for a user.
     *
     * @param  int    $id           User ID
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function get_user_meta($id, $name, $container_id = '')
    {
        return static::get_value($id, 'user_meta', $container_id, $name);
    }
    /**
     * Set user meta field for a user.
     *
     * @param  int    $id           User ID
     * @param  string $name         Field name
     * @param  array  $value
     * @param  string $container_id
     */
    public static function set_user_meta($id, $name, $value, $container_id = '')
    {
        return static::set_value($id, 'user_meta', $container_id, $name, $value);
    }
    /**
     * Get comment meta field for a comment.
     *
     * @param  int    $id           Comment ID
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function get_comment_meta($id, $name, $container_id = '')
    {
        return static::get_value($id, 'comment_meta', $container_id, $name);
    }
    /**
     * Set comment meta field for a comment.
     *
     * @param  int    $id           Comment ID
     * @param  string $name         Field name
     * @param  array  $value
     * @param  string $container_id
     */
    public static function set_comment_meta($id, $name, $value, $container_id = '')
    {
        return static::set_value($id, 'comment_meta', $container_id, $name, $value);
    }
    /**
     * Get nav menu item meta field for a nav menu item.
     *
     * @param  int    $id           Nav menu item ID
     * @param  string $name         Field name
     * @param  string $container_id
     * @return mixed
     */
    public static function get_nav_menu_item_meta($id, $name, $container_id = '')
    {
        return static::get_value($id, 'nav_menu_item', $container_id, $name);
    }
    /**
     * Set nav menu item meta field for a nav menu item.
     *
     * @param  int    $id           Nav menu item ID
     * @param  string $name         Field name
     * @param  array  $value
     * @param  string $container_id
     */
    public static function set_nav_menu_item_meta($id, $name, $value, $container_id = '')
    {
        return static::set_value($id, 'nav_menu_item', $container_id, $name, $value);
    }
    /**
     * Recursive sorting function by array key.
     *
     * @param  array   &$array     The input array.
     * @param  int     $sort_flags Flags for controlling sorting behavior.
     * @return boolean
     */
    public static function ksort_recursive(&$array, $sort_flags = \SORT_REGULAR)
    {
        if (!\is_array($array)) {
            return \false;
        }
        \ksort($array, $sort_flags);
        foreach ($array as $key => $value) {
            self::ksort_recursive($array[$key], $sort_flags);
        }
        return \true;
    }
    /**
     * Get the relation type from an array similar to how meta_query works in WP_Query
     *
     * @param  array         $array
     * @param  array<string> $allowed_relations
     * @param  string        $relation_key
     * @return string
     */
    public static function get_relation_type_from_array($array, $allowed_relations = array('AND', 'OR'), $relation_key = 'relation')
    {
        $allowed_relations = \array_values($allowed_relations);
        $allowed_relations = \array_map('strtoupper', $allowed_relations);
        $relation = isset($allowed_relations[0]) ? $allowed_relations[0] : '';
        if (isset($array[$relation_key])) {
            $relation = \strtoupper($array[$relation_key]);
        }
        if (!\in_array($relation, $allowed_relations)) {
            Incorrect_Syntax_Exception::raise('Invalid relation type ' . $relation . '. ' . 'The rule should be one of the following: "' . \implode('", "', $allowed_relations) . '"');
        }
        return $relation;
    }
    /**
     * Normalize a label by updating case, stripping common prefixes etc.
     *
     * @param  string $label
     * @return string
     */
    public static function normalize_label($label)
    {
        // remove the leading underscore(if it's there)
        $label = \preg_replace('~^_~', '', $label);
        // remove the leading "crb_"(if it's there)
        $label = \preg_replace('~^crb_~', '', $label);
        // split the name into words and make them capitalized
        $label = \mb_convert_case(\str_replace('_', ' ', $label), \MB_CASE_TITLE);
        return $label;
    }
    /**
     * Normalize a type string representing an object type
     *
     * @param  string $type
     * @return string
     */
    public static function normalize_type($type)
    {
        $normalized_type = \str_replace(' ', '_', $type);
        $normalized_type = \preg_replace('/[_\\s]+/', '_', $normalized_type);
        $normalized_type = \preg_replace('/^_|_$/', '', $normalized_type);
        $normalized_type = \strtolower($normalized_type);
        return $normalized_type;
    }
    /**
     * Convert a string representing an object type to a fully qualified class name
     *
     * @param  string $type
     * @param  string $namespace
     * @param  string $class_suffix
     * @return string
     */
    public static function type_to_class($type, $namespace = '', $class_suffix = '')
    {
        $classlike_type = static::normalize_type($type);
        $classlike_type = \str_replace('_', ' ', $classlike_type);
        $classlike_type = \ucwords($classlike_type);
        $classlike_type = \str_replace(' ', '_', $classlike_type);
        $class = $classlike_type . $class_suffix;
        if ($namespace) {
            $class = $namespace . '\\' . $class;
        }
        return $class;
    }
    /**
     * Convert a string representing an object type to a fully qualified class name
     *
     * @param  string $class
     * @param  string $class_suffix
     * @return string
     */
    public static function class_to_type($class, $class_suffix = '')
    {
        $reflection = new \ReflectionClass($class);
        $type = $reflection->getShortName();
        if ($class_suffix) {
            $type = \preg_replace('/(' . \preg_quote($class_suffix, '/') . ')$/i', '', $type);
        }
        $type = static::normalize_type($type);
        return $type;
    }
    /**
     * Get an array of sanitized html classes
     *
     * @param  string|array<string> $classes
     * @return array<string>
     */
    public static function sanitize_classes($classes)
    {
        if (!\is_array($classes)) {
            $classes = \array_values(\array_filter(\explode(' ', $classes)));
        }
        $classes = \array_map('sanitize_html_class', $classes);
        return $classes;
    }
    /**
     * Check if an id or name for containers and fields is valid
     *
     * @param  string  $id
     * @return boolean
     */
    public static function is_valid_entity_id($id)
    {
        return !empty($id) && \preg_match('/\\A[a-z0-9_\\-]+\\z/', $id);
    }
    /**
     * Return a partial regex pettern matching allowed field name characters
     *
     * @return string
     */
    public static function get_field_name_characters_pattern()
    {
        return 'a-z0-9_\\-';
    }
    /**
     * Get an attachment ID given a file URL
     * Modified version of https://wpscholar.com/blog/get-attachment-id-from-wp-image-url/
     *
     * @param  string  $url
     * @return integet
     */
    public static function get_attachment_id($url)
    {
        $dir = wp_upload_dir();
        $filename = \basename($url);
        if (\strpos($url, $dir['baseurl'] . '/') === \false) {
            return 0;
        }
        $query_args = array('post_type' => 'attachment', 'post_status' => 'inherit', 'fields' => 'ids', 'meta_query' => array(array('value' => $filename, 'compare' => 'LIKE', 'key' => '_wp_attachment_metadata')));
        $query = new WP_Query($query_args);
        if ($query->have_posts()) {
            foreach ($query->posts as $post_id) {
                $meta = wp_get_attachment_metadata($post_id);
                $original_file = \basename($meta['file']);
                $cropped_image_files = wp_list_pluck($meta['sizes'], 'file');
                if ($original_file === $filename || \in_array($filename, $cropped_image_files)) {
                    return \intval($post_id);
                }
            }
        }
        return 0;
    }
    /**
     * Returns attachment metadata from an ID.
     *
     * @param  string  $id
     * @param  string  $type Value Type. Can be either id or url
     * @return boolean
     */
    public static function get_attachment_metadata($id, $type)
    {
        $attachment_meta = array('thumb_url' => '', 'default_thumb_url' => '', 'file_ext' => '', 'file_type' => '', 'file_name' => '', 'file_url' => '', 'edit_nonce' => '', 'title' => '', 'caption' => '', 'description' => '', 'alt' => '', 'date' => '', 'filesize' => '', 'width' => '', 'height' => '');
        // when value_type is set to "url" the $id will hold the url, not the id
        if ($type === 'url') {
            $attachment_id = static::get_attachment_id($id);
            if ($attachment_id === 0) {
                $attachment_meta['thumb_url'] = $id;
                $attachment_meta['default_thumb_url'] = $id;
                $attachment_meta['file_url'] = $id;
                return $attachment_meta;
            }
            $id = $attachment_id;
        }
        $attachment = get_post($id);
        if (!$attachment) {
            return $attachment_meta;
        }
        $meta = wp_get_attachment_metadata($attachment->ID);
        list($src, $width, $height) = wp_get_attachment_image_src($attachment->ID, 'full');
        $attachment_meta['edit_nonce'] = wp_create_nonce('update-post_' . $id);
        $attachment_meta['title'] = get_the_title($id);
        $attachment_meta['caption'] = get_post_field('post_excerpt', $id);
        $attachment_meta['description'] = get_post_field('post_content', $id);
        $attachment_meta['alt'] = get_post_meta($id, '_wp_attachment_image_alt', \true);
        $attachment_meta['date'] = mysql2date(__('F j, Y'), $attachment->post_date);
        $attachment_meta['width'] = $width;
        $attachment_meta['height'] = $height;
        $attachment_meta['file_url'] = \is_numeric($id) ? wp_get_attachment_url($id) : $id;
        $attachment_meta['file_name'] = \basename($attachment_meta['file_url']);
        $attachment_meta['filetype'] = wp_check_filetype($attachment_meta['file_url']);
        $attachment_meta['file_ext'] = $attachment_meta['filetype']['ext'];
        // png, mp3, etc..
        $attachment_meta['file_type'] = \preg_replace('~\\/.+$~', '', $attachment_meta['filetype']['type']);
        // image, video, etc..
        if ($attachment_meta['file_type'] === 'audio') {
            $attachment_meta['artist'] = $meta['artist'];
            $attachment_meta['album'] = $meta['album'];
            $attachment_meta['length'] = $meta['length_formatted'];
        }
        $attachment_meta['default_thumb_url'] = wp_mime_type_icon($id);
        if ($attachment_meta['file_type'] == 'image') {
            $attachment_meta['thumb_url'] = $attachment_meta['file_url'];
            if ($type == 'id') {
                $thumb_src = wp_get_attachment_image_src($id, 'thumbnail');
                $attachment_meta['thumb_url'] = $thumb_src[0];
            }
        } else {
            $attachment_meta['thumb_url'] = $attachment_meta['default_thumb_url'];
        }
        $attached_file = get_attached_file($attachment->ID);
        if (\file_exists($attached_file)) {
            $attachment_meta['filesize'] = size_format(\filesize($attached_file));
        }
        return $attachment_meta;
    }
    /**
     * Get the current $_POST or $_GET input array with compacted input values merged in
     *
     * @return array
     */
    public static function input()
    {
        $input = isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST' ? $_POST : $_GET;
        $input = stripslashes_deep($input);
        if (\WPUM\Carbon_Fields\COMPACT_INPUT) {
            $input = static::expand_compacted_input($input);
        }
        return $input;
    }
    /**
     * Get a copy of the passed array with compacted input values merged in
     *
     * @param  array $input
     * @return array
     */
    public static function expand_compacted_input($input)
    {
        if (isset($input[\WPUM\Carbon_Fields\COMPACT_INPUT_KEY])) {
            $json = \json_decode($input[\WPUM\Carbon_Fields\COMPACT_INPUT_KEY], \true);
            $input = \array_merge($input, $json);
        }
        return $input;
    }
    /**
     * Get valid input from an input array compared to predefined options
     *
     * @param  array $input
     * @param  array $options
     * @return array
     */
    public static function get_valid_options($input, $options)
    {
        // enfore comparison to be string so we do not get unexpected matches
        // for cases such as "string without any numbers" == 0
        // in array_search()
        $search_options = \array_map('strval', $options);
        $valid_input = array();
        foreach ($input as $raw_value) {
            $index = \array_search(\strval($raw_value), $search_options, \true);
            if ($index === \false) {
                continue;
            }
            $valid_input[] = $options[$index];
        }
        return $valid_input;
    }
    /**
     * Get an array of active sidebars
     *
     * @return array
     */
    public static function get_active_sidebars()
    {
        global $wp_registered_sidebars;
        $sidebars = array();
        foreach ($wp_registered_sidebars as $sidebar) {
            // Check if we have inactive sidebars
            if (isset($sidebar['class']) && \strpos($sidebar['class'], 'inactive-sidebar') !== \false) {
                continue;
            }
            $sidebars[] = array('id' => $sidebar['id'], 'name' => $sidebar['name']);
        }
        return $sidebars;
    }
}
