<?php

if (!defined('ABSPATH')) {
die('No direct access.');
}

/**
 * Utility class to handle various image tasks
 */
class MetaSlider_Image
{
    /**
     * Theme instance
     * 
     * @var object
     * @see get_instance()
     */
    protected static $instance = null;

    /**
     * Used to access the instance
     */
    public static function instance()
    {
        if (null === self::$instance) {
self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Method to import an image (or more than one). If nothing passed in it will load in a random one from 
     * the theme directory. A theme can also be defined.
     * 
     * @since 3.98 - Added $extra param
     * 
     * @param array  $images   - Images to upload, if any
     * @param string $theme_id - The folder name of a theme
     * @param array $extra          Extra data such as slug from quickstart option if available. e.g. 'withcaption'
     * 
     * @return WP_Error|array - The array of ids that were uploaded
     */
    public function import($images, $theme_id = null, $extra = array())
    {
        /*
        If we are provided images, they should be formatted already
        It should look like this, possibly without the meta data
        $image[$url] = array(
            'source' => $tmp_name,
            'caption' => '',
            'title' => '',
            'description' => '',
            'alt' => ''
        );
        */
        if (empty($images)) {
            $images = $this->get_theme_images($theme_id, 4, $extra);
        }

        // Get an array or sucessful image ids
        return $this->upload_multiple($images);
    }

    /**
     * Method to import images from a theme
     *
     * @param array $images - The full path to the local image or an array that includes the path
     */
    public function upload_multiple($images)
    {

        $successful_uploads = array();
        foreach ($images as $filename => $image) {
            if ($image_id = $this->upload($filename, $image['source'], $image)) {
                array_push($successful_uploads, $image_id);
            }
        }

        return $successful_uploads;
    }

    /**
     * Method to upload a single image, you should provide a local location on the server
     *
     * @param string $filename  - The preferred name of the file
     * @param string $source    - The current location of the file without the file name
     * @param array  $meta_data - Extra data like caption, description, etc
     * @return int|boolean - returns the ID of the new image, or false
     */
    public function upload($filename, $source, $meta_data = array())
    {
        if (!function_exists('media_handle_upload')) {
            require_once(ABSPATH . 'wp-admin/includes/image.php');
            require_once(ABSPATH . 'wp-admin/includes/file.php');
            require_once(ABSPATH . 'wp-admin/includes/media.php');
        }

        if (file_exists($source)) {
            $wp_upload_dir = wp_upload_dir();

            // Create a new filename if needed
            $filename = wp_unique_filename(trailingslashit($wp_upload_dir['path']), $filename);

            // Get the file path of the target destination
            $destination = trailingslashit($wp_upload_dir['path']) . $filename;

            // We want these both to return true
            if (copy($source, $destination)) {
                if ((bool) $image_id = $this->attach_image_to_media_library($destination, $meta_data)) {
                    return $image_id;
                }
            }

            // TODO: we might want to provide a specific error message if an image 
            // fails to upload.
            return false;
        }

        // If we make it this far then the file doesn't exit
        return false;
    }

    /**
     * Adds the type and image id to an array
     *
     * @param  int $image_id Image ID
     * @return array
     */
    public function make_layer_slide_data($image_id)
    {
        return array(
            'type' => 'layer',
            'id'   => absint($image_id)
        );
    }

    /**
     * Method to import images from a theme
     *
     * @since 3.98 - Added $extra param
     * 
     * @param array|null $theme_id  The name of a theme
     * @param int $count            How many images? (4 for legacy reasons)
     * @param array $extra          Extra data such as slug from quickstart option if available. e.g. 'withcaption'
     * 
     * @return array - a formatted image array
     */
    public function get_theme_images($theme_id, $count = 4, $extra = array())
    {

        // To use local images, the folder must exist
        if (!file_exists($theme_image_directory = METASLIDER_THEMES_PATH . 'images/')) {
            return new WP_Error('images_not_found', __('We could not find any images to import.', 'ml-slider'), array('status' => 404));
        }

        if (is_multisite() && $settings = get_site_option('metaslider_global_settings')) {
            $global_settings = $settings;
        }
        
        if ($settings = get_option('metaslider_global_settings')) {
            $global_settings = $settings;
        }

        $new_install = get_option('metaslider_new_user');
        
        if (
            (isset($global_settings['legacy']) && true == $global_settings['legacy']) ||
            (isset($new_install)  && 'new' == $new_install)
        ) {
            $manifest_file = 'manifest.php';
        } else {
            $manifest_file = 'manifest-legacy.php';
        }

        // Check for the manifest, and load theme specific images for a theme (if a theme is set)
        if (!is_null($theme_id) && file_exists(METASLIDER_THEMES_PATH . $manifest_file)) {
            $themes = (include METASLIDER_THEMES_PATH . $manifest_file);

            // Check if the theme is available and has images set
            foreach ($themes as $theme) {
                if (!empty($theme['images']) && $theme_id === $theme['folder']) {
                    $images = $theme['images'];
                }
            }
        }

        // Get list of images in the folder
        $all_images = array_filter(scandir($theme_image_directory), array($this, 'filter_images'));

        // If images are specified, make sure they exist and use them. if not, use 4 at random
        $images = !empty($images) ? $this->pluck_images($images, $all_images) : array_rand(array_flip($all_images), $count);

        $available_captions = array(
            "Bold ideas. Brighter futures.",
            "Crafted with purpose, built to inspire.",
            "Where ambition finds its home.",
            "Simplicity meets sophistication.",
            "Dream big. Build bigger.",
            "Design the moment. Define the future.",
            "Passion fuels every detail.",
            "Visionary minds, timeless creations.",
            "The future starts with a spark.",
            "Inspired by life. Driven by purpose."
        );
        $used_captions      = array();
        $images_formatted   = array();

        foreach ((array) $images as $filedata) {
            $data = array();

            // Only process strings or arrays
            if (!is_string($filedata) && !is_array($filedata)) {
continue;
            }

            // If a string, convert it to an array with the string as the key (filename)
            if (is_string($filedata)) {
                $data[$filedata] = array();
                $filename = $filedata;
            }

            // If it was an array, the filename needs to become the key
            if (!empty($filedata['filename'])) {
                $filename = $filedata['filename'];
                unset($filedata['filename']);
                $data = $filedata;
            }

            // Set the local images dir as the source
            $data['source'] = trailingslashit($theme_image_directory) . $filename;
            
            // @since 3.98 - Get a random caption that hasn't been used yet 
            // for 'Carousel Slideshow with Captions' quickstart slides
            if ( isset( $extra['slug'] ) && $extra['slug'] == 'withcaption' ) {
                $available_captions = array_diff( $available_captions, $used_captions );
                if ( empty( $available_captions ) ) {
                    // When we run out of captions, set empty
                    $data['caption'] = '';
                } else {
                    $random_caption = $available_captions[array_rand( $available_captions )];
                    $used_captions[] = $random_caption;
                    $data['caption'] = $random_caption;
                }
            }
            
            /*
            It should look like this, possibly without the meta data
            $images_formatted[$filename] = array(
                'source' => $tmp_name,
                'caption' => '',
                'title' => '',
                'description' => '',
                'alt' => ''
            );
            */
            $images_formatted[$filename] = $data;
        }

        return $images_formatted;
    }

    /**
     * Method to use filter out non-images
     * TODO: possible extract this into static method on a utility class
     *
     * @param string $string - a filename scanned from the images dir
     * @return boolean
     */
    private function filter_images($string)
    {

        // TODO: allow all image types (this is currently used in the themes folder only)
        return preg_match('/jpg$/i', $string);
    }

    /**
     * Method to use filter out images that might not exist
     * TODO: possible extract this into static method on a utility class
     *
     * @param array $images_to_use - Images defined in the manifest
     * @param array $images        - Images from the images folder
     * @return array
     */
    private function pluck_images($images_to_use, $images)
    {

        // For the filename/caption scenario
        if (!empty($images_to_use[0]) && is_array($images_to_use[0])) {
            // Just return the multi-dimentional array and handle the filecheck later
            return $images_to_use;
        }

        // Return the images specified by the filename or four random
        $images_that_exist = array_intersect($images_to_use, $images);
        return (!empty($images_that_exist)) ? $images_that_exist : array_rand(array_flip($images), 4);
    }

    /**
     * Method to add a file to the media library
     * TODO: possible extract this into static method on a utility class
     *
     * @param string $filename   - The full path to the image dir in the media library
     * @param array  $image_data - Optional data to attach / override to the image
     * @return int
     */
    private function attach_image_to_media_library($filename, $image_data = array())
    {

        $filetype = wp_check_filetype(basename($filename), null);
        $wp_upload_dir = wp_upload_dir();

        $attachment = array(
            'guid'           => $wp_upload_dir['url'] . '/' . basename($filename),
            'post_mime_type' => $filetype['type'],
            'post_title'     => preg_replace('/\.[^.]+$/', '', basename($filename)),
            'post_content'   => '',
            'post_excerpt'   => '',
            'post_status'    => 'publish'
        );

        // Add the caption, title and description if set. This used human-friendly words
        // instead of WP specific to make it more simple for theme developers
        if (!empty($image_data['caption'])) {
            $image_data['post_excerpt'] = $image_data['caption'];
            unset($image_data['caption']);
        }
        if (!empty($image_data['title'])) {
            $image_data['post_title'] = $image_data['title'];
            unset($image_data['title']);
        }
        if (!empty($image_data['description'])) {
            $image_data['post_content'] = $image_data['description'];
            unset($image_data['description']);
        }

        // Merge the theme data with the defaults
        $data = array_merge($attachment, $image_data);

        // Insert the attachment
        $attach_id = wp_insert_attachment($data, $filename);

        // Double check it was an image, delete if not.
        if (!wp_attachment_is_image($attach_id)) {
            wp_delete_post($attach_id, true);
            return false;
        }

        // Generate the metadata for the attachment, and update the database record
        $attach_data = wp_generate_attachment_metadata($attach_id, $filename);
        wp_update_attachment_metadata($attach_id, $attach_data);

        // The theme can set the alt tag too if needed
        if ($attach_id && !empty($image_data['alt'])) {
            update_post_meta($attach_id, '_wp_attachment_image_alt', $image_data['alt']);
        }

        return $attach_id;
    }

    /**
     * Method to get image id from the filename
     *
     * @param array $filenames - It should be the the post_name
     * @return array
     */
    public static function get_image_ids_from_file_name($filenames)
    {
        $images = array();
        foreach ($filenames as $filename) {
            $image = get_posts(array(
                'post_type' => 'attachment',
                'name' => $filename,
                'posts_per_page' => 1,
                'post_status' => 'inherit',
            ));
            $image = $image ? array_pop($image) : null;

            if (is_null($image)) {
                $images[$filename] = null;
                continue;
            }

            $images[$filename] = array(
                'url' => wp_get_attachment_url($image->ID),
                'thumbnail' => wp_get_attachment_thumb_url($image->ID),
                'id' => $image->ID
            );
        }
        return $images;
    }
}
