<?php
namespace Grav\Plugin;

use Composer\Autoload\ClassLoader;
use Grav\Common\Page\Media;
use Grav\Common\Plugin;
use Grav\Common\Yaml;
use RocketTheme\Toolbox\File\File;

/**
 * Class AdminAddonMediaMetadataPlugin
 * some of the code is based on the Admin Addon Media Rename by Dávid Szabó
 *     see https://github.com/david-szabo97/grav-plugin-admin-addon-media-rename
 * @package Grav\Plugin
 */
class AdminAddonMediaMetadataPlugin extends Plugin
{
    const ROUTE = '/admin-addon-media-metadata';
    const TASK_METADATA = 'AdminAddonMediaMetadataEdit';

    /**
     * @return array
     *
     * The getSubscribedEvents() gives the core a list of events
     * that the plugin wants to listen to. The key of each
     * array section is the event that the plugin listens to
     * and the value (in the form of an array) contains the
     * callable (or function) as well as the priority. The
     * higher the number the higher the priority.
     */
    public static function getSubscribedEvents()
    {
        return [
            ['autoload', 100000], // TODO: Remove when plugin requires Grav >=1.7
            'onPluginsInitialized' => ['onPluginsInitialized', 0]
        ];
    }

    /**
     * Composer autoload.
     * is
     * @return ClassLoader
     */
    public function autoload(): ClassLoader
    {
        return require __DIR__ . '/vendor/autoload.php';
    }

    public function getPath()
    {
        return '/' . trim($this->grav['admin']->base, '/') . '/' . trim(self::ROUTE, '/');
    }

    public function buildBaseUrl()
    {
        return rtrim($this->grav['uri']->rootUrl(true), '/') . '/' . trim($this->getPath(), '/');
    }

    /**
     * Initialize the plugin
     */
    public function onPluginsInitialized()
    {
        if (!$this->isAdmin() || !$this->grav['user']->authenticated) {
            return;
        }

        if ($this->grav['uri']->path() == $this->getPath()) {
            $this->enable([
                'onPagesInitialized' => ['processRenameRequest', 0]
            ]);
            return;
        }

        $this->enable([
            'onTwigTemplatePaths' => ['onTwigTemplatePaths', 0],
            'onPagesInitialized' => ['onTwigExtensions', 0],
            //'onAdminAfterAddMedia' => ['createMetaYaml', 0], // removing the call of this method: see method below
            'onAdminTaskExecute' => ['editMetaDataFile', 0],
        ]);
    }

    public function onTwigTemplatePaths()
    {
        $this->grav['twig']->twig_paths[] = __DIR__ . '/templates';
    }

    public function onTwigExtensions()
    {
        $page = $this->grav['admin']->page(true);
        if (!$page) {
            return;
        }

        /**
         * get metadata form field config from plugin admin-addon-media-metadata.yaml file
         * or local override in user/config/plugins/admin-addon-media-metadata.yaml
         * or from page frontmatter
         */
        //$formFields = $this->config->get('plugins.admin-addon-media-metadata.metadata_form');
        $config = $this->mergeConfig($page, true);
        $formFields = $config->get('metadata_form');

        /**
         * list all needed data keys from the fields configuration
         */
        $arrMetaKeys = $this->editableFields($formFields['fields']);

        $path = $page->path();
        $media = new Media($path);
        $allMedia = $media->all();
        $arrFiles = [];
        $i = 0;
        foreach ($allMedia as $filename => $file) {
            $metadata = $file->meta();
            $arrFiles[$filename] = [
                'filename' => $filename,
            ];
            /**
             * for each file: write stored metadata for each editable field into an array
             * this will be output as inline JS variable adminAddonMediaMetadata
             */
            foreach ($arrMetaKeys as $metaKey => $info) {
                $arrFiles[$filename][$metaKey] = $metadata->$metaKey;
            }
            $i++;
        }

        $jsArrFormFields = '';
        $i = 0;
        foreach ($arrMetaKeys as $metaKey => $info) {
            $jsArrFormFields .= ($i > 0) ? ",'" . $metaKey . "'" : "'" . $metaKey . "'";
            $i++;
        }

        $inlineJs = 'var metadataFormFields = [' . $jsArrFormFields . '];';
        $inlineJs .= PHP_EOL . 'var mediaListOnLoad = ' . json_encode($arrFiles) . ';';
        $modal = $this->grav['twig']->twig()->render('metadata-modal.html.twig', $formFields);
        $jsConfig = [
            'PATH' => $this->buildBaseUrl() . '/' . $page->route() . '/task:' . self::TASK_METADATA,
            'MODAL' => $modal
        ];
        $inlineJs .= PHP_EOL . 'var adminAddonMediaMetadata = ' . json_encode($jsConfig) . ';';
        $inlineJs .= PHP_EOL . $this->grav['twig']->twig()->render('additionalInlineJS.html.twig');
        $this->grav['assets']->addInlineJs($inlineJs, -1000);
        $this->grav['assets']->addCss('plugin://admin-addon-media-metadata/admin-addon-media-metadata.css', -1000);
        $this->grav['assets']->addJs('plugin://admin-addon-media-metadata/admin-addon-media-metadata.js', -1000);
    }

    public function editTest()
    {
        $this->outputError('blob');
    }

    /**
     * writes metadata into the [mediafile].meta.yaml file
     * creates the .meta.yaml file if it does not yet exist
     */
    public function editMetaDataFile($e)
    {
        $method = $e['method'];
        if ($method === 'task' . self::TASK_METADATA) {
            $fileName = $_POST['filename'];

            $pageObj = $this->grav['admin']->page();
            $basePath = $pageObj->path() . DS;

            $filePath = $basePath . $fileName;

/**
 * temporarily removing the condition that checks for the media file to exist
 * as in Admin plugin 1.10 a media file will be uploaded to a tmp folder first
 * and moved to the page folder on saving the page
 *
 * there needs to be a better solution for this
 *
 * also: take care of system.yaml → media.auto_metadata_exif
 *     if set to TRUE, a meta.yaml with EXIF data will be written, but only if the meta.yaml does not yet exist
 *     in Admin 1.10 you need to save a page in order to have the meta.yaml file with EXIF data created
 *     in Admin 1.9 this is not a problem since this plugin now (>=1.1.0) writes metadata only when needed
 */
//          if (!file_exists($filePath)) {
//              $this->outputError($this->grav['language']->translate(['PLUGIN_ADMIN_ADDON_MEDIA_METADATA.ERRORS.MEDIA_FILE_NOT_FOUND', $filePath]));
//          } else {
                $metaDataFilePath = $filePath . '.meta.yaml';

                /**
                 * get the list of form data from the fields configuration
                 */
                $arrMetaKeys = $this->editableFields();

                if (file_exists($metaDataFilePath)) {
                    /**
                     * get array of all current metadata for the file
                     * this is to avoid overwriting data that has been added to the meta.yaml file in the file browser
                     */
                    $storedMetaData = Yaml::parse(file_get_contents($metaDataFilePath));

                    /**
                     * overwrite the currently stored data for each field in the form
                     */
                    foreach ($arrMetaKeys as $metaKey => $info) {
                        if (isset($_POST[$metaKey])) {
                            $storedMetaData[$metaKey] = $_POST[$metaKey];
                        }
                    }

                } else {
                    /**
                     * create metaData in case the meta data file does not yet exist
                     */
                    $storedMetaData = [];
                    foreach ($arrMetaKeys as $metaKey => $info) {
                        if (isset($_POST[$metaKey])) {
                            $storedMetaData[$metaKey] = $_POST[$metaKey];
                        } else {
                            $storedMetaData[$metaKey] = '';
                        }
                    }
                }

                /**
                 * Get an instance of the meta file and write the data to it
                 * @see \Grav\Common\Page\Media
                 */
                $metaDataFile = File::instance($metaDataFilePath);
                $metaDataFile->save(Yaml::dump($storedMetaData));

                //$this->outputError($newYamlText);
//          }
        }
    }

/**
 * this method will not be called in Admin Plugin v1.10 (up until rc.11 anyway)
 * additionally, writing the YAML file on upload will meddle with the system functionality
 *     that writes exif data
 *     in Admin Plugin v1.9 this functionality writes the meta.yaml file
 *         on upload, this plugin will then just add the other metadata
 *         when writing the meta.yaml file upon upload the system functionality will be overwritten
 *     in Admin Plugin v1.10 you need to save the page before using this plugin on a file
 *         if you want the exif data to be stored
 */
    /**
     * creates an image.meta.yaml file
     * this file will be deleted by the core when deleting an image
     * will be called after a media file has been added (see onPluginsInitialized())
     */
//    public function createMetaYaml()
//    {
//        $fileName = $_FILES['file']['name'];
//
//        $pageObj = $this->grav['admin']->page();
//        $basePath = $pageObj->path() . DS;
//
//        $filePath = $basePath . $fileName;
//        if (!file_exists($filePath)) {
//            $this->outputError($this->grav['language']->translate(['PLUGIN_ADMIN_ADDON_MEDIA_METADATA.ERRORS.MEDIA_FILE_NOT_FOUND', $filePath]));
//        } else {
//            // TODO: do that only for image files?
//            $metaDataFileName = $fileName . '.meta.yaml';
//            $metaDataFilePath = $basePath . $metaDataFileName;
//            if (!file_exists($metaDataFilePath)) {
//                /**
//                 * get the list of form data from the fields configuration
//                 */
//                $arrMetaKeys = $this->editableFields();
//
//                $newMetaData = [];
//                foreach ($arrMetaKeys as $metaKey => $info) {
//                    $newMetaData[$metaKey] = '';
//                }
//
//                /**
//                 * Get an instance of the meta file and write the data to it
//                 * @see \Grav\Common\Page\Media
//                 */
//                $metaDataFile = File::instance($metaDataFilePath);
//                $metaDataFile->save(Yaml::dump($newMetaData));
//            }
//        }
//    }

    /**
     * return all editable fields from form configuration
     */
    private function editableFields($fieldsConf = null)
    {
        if ($fieldsConf === null) {
            $page = $this->grav['admin']->page(true);
            if (!$page) {
                return;
            }
            /**
             * get metadata form field config from plugin admin-addon-media-metadata.yaml file
             * or local override in user/config/plugins/admin-addon-media-metadata.yaml
             * or from page frontmatter
             */
            //$formFields = $this->config->get('plugins.admin-addon-media-metadata.metadata_form');
            $config = $this->mergeConfig($page, true);
            $formFields = $config->get('metadata_form');
            $fieldsConf = $formFields['fields'];
        }
        $arrMetaKeys = [];
        foreach ($fieldsConf as $singleFieldConf) {
            if (isset($singleFieldConf['name'], $singleFieldConf['type']) && $singleFieldConf['name'] !== 'filename') {
                $arrMetaKeys[$singleFieldConf['name']] = [
                    'name' => $singleFieldConf['name'],
                    'type' => $singleFieldConf['type']
                ];
            }
        }
        return $arrMetaKeys;
    }

    public function outputError($msg)
    {
        header('HTTP/1.1 400 Bad Request');
        die(json_encode(['error' => ['msg' => $msg]]));
    }
}
