<?php

namespace Mnv\Core\Uploads;

use InvalidArgumentException;
use ZipArchive;

class Favicon
{

    private $params = array();

    public function run($json)
    {
        if ($json == NULL) {
            throw new InvalidArgumentException("No response from RealFaviconGenerator");
        }

//        print_r($json);
        $response = json_decode($json, true);

        if ($response == NULL) {
            throw new InvalidArgumentException("JSON could not be parsed");
        }

        $response = $this->getParam($response, 'favicon_generation_result');
        $result = $this->getParam($response, 'result');
        $status = $this->getParam($result, 'status');

        if ($status != 'success') {
            $msg = $this->getParam($result, 'error_message', false);
            $msg = $msg != NULL ? $msg : 'An error occured';
            throw new InvalidArgumentException($msg);
        }

        $favicon = $this->getParam($response, 'favicon');
        $this->params['package_url'] = $this->getParam($favicon, 'package_url');
        $this->params['compression'] = $this->getParam($favicon, 'compression') == 'true';
        $this->params['html_code'] = $this->getParam($favicon, 'html_code');

        $filesLoc = $this->getParam($response, 'files_location');
        $this->params['files_in_root'] = $this->getParam($filesLoc, 'type') == 'root';
        $this->params['files_path'] = $this->params['files_in_root'] ? '/' : $this->getParam($filesLoc, 'path');

        $this->params['preview_picture_url'] = $this->getParam($response, 'preview_picture_url', false);

        $this->params['custom_parameter'] = $this->getParam($response, 'custom_parameter', false);
        $this->params['version'] = $this->getParam($response, 'version', false);

        $this->params['non_interactive_request'] = $this->getParam($response, 'non_interactive_request', false);
    }

    /**
     * For example: <code>"http://realfavicongenerator.net/files/1234f5d2s34f3ds2/package.zip"</code>
     */
    public function getPackageUrl() {
        return $this->params['package_url'];
    }

    /**
     * For example: <code>"&lt;link ..."</code>
     */
    public function getHtmlCode() {
        return $this->params['html_code'];
    }

    /**
     * <code>true</code> if the user chose to compress the pictures, <code>false</code> otherwise.
     */
    public function isCompressed() {
        return $this->params['compression'];
    }

    /**
     * <code>true</code> if the favicon files are to be stored in the root directory of the target web site, <code>false</code> otherwise.
     */
    public function isFilesInRoot() {
        return $this->params['files_in_root'];
    }

    /**
     * Indicate where the favicon files should be stored in the target web site. For example: <code>"/"</code>, <code>"/path/to/icons"</code>.
     */
    public function getFilesLocation() {
        return $this->params['files_path'];
    }

    /**
     * For example: <code>"http://realfavicongenerator.net/files/1234f5d2s34f3ds2/preview.png"</code>
     */
    public function getPreviewUrl() {
        return $this->params['preview_picture_url'];
    }

    /**
     * Return the customer parameter, as it was transmitted during the invocation of the API.
     */
    public function getCustomParameter() {
        return $this->params['custom_parameter'];
    }

    /**
     * Return version of RealFaviconGenerator used to generate the favicon. Save this value to later check for updates.
     */
    public function getVersion() {
        return $this->params['version'];
    }

    private function getParam($params, $paramName, $throwIfNotFound = true) {
        if (isset($params[$paramName])) {
            return $params[$paramName];
        }
        else if ($throwIfNotFound) {
            throw new InvalidArgumentException("Cannot find parameter " . $paramName);
        }
    }

    /**
     * Download and extract the files referenced by the response sent back by RealFaviconGenerator.
     *
     * Warning: as this method does HTTP accesses, calling it can take a few seconds. Better invoke it
     * in an Ajax call, to not slow down the user experience.
     */
    public function downloadAndUnpack($outputDirectory = NULL): void
    {
        if ($outputDirectory == NULL) {
            $outputDirectory = sys_get_temp_dir();
        }


        if ($this->getPackageUrl() != NULL) {
            $packagePath = $outputDirectory . DIRECTORY_SEPARATOR . 'favicon_package.zip';
            $this->downloadFile($packagePath, $this->getPackageUrl());

            $zip = new ZipArchive();
            $r = $zip->open($packagePath);

            if ($r === TRUE) {
                $extractedPath = $outputDirectory  . 'favicon_package';


                if (!file_exists($extractedPath)) {
                    mkdir($extractedPath);
                }

                $zip->extractTo($extractedPath);
                $zip->close();

                $this->params['favicon_production_package_path'] = $extractedPath;
            }
            else {
                throw new InvalidArgumentException('Cannot open package. Invalid Zip file?!');
            }
        }

        if ($this->getPreviewUrl() != NULL) {
            $previewPath = $outputDirectory  . 'favicon_preview.png';
            $this->downloadFile($previewPath, $this->getPreviewUrl());
            $this->params['preview_path'] = $previewPath;
        }
    }

    /**
     * Directory where the production favicon files are stored.
     * These are the files to deployed to the targeted web site.
     * Method <code>downloadAndUnpack</code> must be called first in order to populate this field.
     */
    public function getPackagePath()
    {
        return $this->params['favicon_production_package_path'];
    }

    /**
     * Path to the preview picture.
     */
    public function getPreviewPath()
    {
        return $this->params['preview_path'];
    }

    /**
     * Non-interative request.
     */
    public function getNonInteractiveRequest() {
        return $this->params['non_interactive_request'];
    }


    private function downloadFile($localPath, $url): void
    {
        $content = file_get_contents($url);
        if ($content === FALSE) {
            throw new InvalidArgumentException("Cannot download file at " . $url);
        }
        $ret = file_put_contents($localPath, $content);
        if ($ret === FALSE) {
            throw new InvalidArgumentException("Cannot store content of " . $url . " to " . $localPath);
        }
    }

    public function storePictures( $rfg_response ): void
    {
        $working_dir = $this->getFilesDir();

        $files = glob( $working_dir . '*' );
        foreach ($files as $file) {
            if (is_file($file)) {
                unlink($file);
            }
        }

        $files = glob( $rfg_response->getPackagePath() . '/*' );
        foreach ($files as $file) {
            if (is_file($file)) {
                $this->portableRename($file, $working_dir . basename($file));
            }
        }
    }

    public function storePreview($preview_path)
    {
        $previous_preview = $this->previewFileName();
        if ($previous_preview != null && (file_exists( $this->previewPath($previous_preview)))) {
            unlink( $this->previewPath($previous_preview));
        }

        if ($preview_path == null) {
            return null;
        } else {
            $preview_file_name = 'preview_' . hash( 'sha256', 'RFB stuff here ' . rand() . microtime() ) . '.png';
        }

        if (!file_exists(dirname($this->previewPath($preview_file_name)))) {
            mkdir(dirname($this->previewPath($preview_file_name)), 0755 );
        }

        $this->portableRename($preview_path, $this->previewPath($preview_file_name));
    }

    public static function getFilesDir(): string
    {
        return GLOBAL_ROOT. '/themes/assets/favicon/';
    }

    public static function getFilesUrl(): string
    {
        return GLOBAL_URL. '/themes/assets/favicon/';
    }

    public function portableFilename($filename): string
    {
        return str_replace( '/', DIRECTORY_SEPARATOR, $filename);
    }

    public function portableRename($from, $to): void
    {
        $from = $this->portableFilename($from);
        $to   = $this->portableFilename($to);
        rename($from, $to);
    }

    public function previewPath($preview_file_name = null): string
    {
        if ( ! $preview_file_name ) {
            $preview_file_name = $this->previewFileName();
        }

        return $this->getFilesDir() . 'preview' . DIRECTORY_SEPARATOR . $preview_file_name;
    }

    public function is_preview_available(): bool
    {
        $preview_file_name = $this->previewFileName();
        return is_file($this->getFilesDir() . 'preview' . DIRECTORY_SEPARATOR . $preview_file_name);
    }

    public function previewUrl($preview_file_name = null): string
    {
        if (!$preview_file_name) {
            $preview_file_name = $this->previewFileName();
        }

        return $this->getFilesUrl() . 'preview' . DIRECTORY_SEPARATOR . $preview_file_name;
    }


    public function previewFileName()
    {
        $working_dir = $this->getFilesDir();

        $filenames = [];
        if (is_dir($working_dir)) {
            $files = glob($working_dir . 'preview/preview*.*', GLOB_BRACE);
            foreach ($files as $file) {
                $filenames[] = basename($file);
            }
        }

        return !empty($filenames) ? $filenames[0] : null;
    }


}