<?php
/**
 * @package     FILEman
 * @copyright   Copyright (C) 2011 Timble CVBA. (http://www.timble.net)
 * @license     GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
 * @link        http://www.joomlatools.com
 */

class ComFilemanDispatcherBehaviorConnectable extends KControllerBehaviorAbstract
{
    private static $_editable_image_containers = [
        'fileman-files', 'fileman-attachments', 'fileman-user-files',
    ];

    private static $_editable_image_extensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp'];

    protected function _initialize(KObjectConfig $config)
    {
        $config->append(array('priority' => self::PRIORITY_HIGH));
        parent::_initialize($config);
    }

    public function isSupported()
    {
        return $this->getObject('com://admin/fileman.job.scans')->isSupported();
    }

    protected function _beforeDispatch(KControllerContextInterface $context)
    {
        /** @var KDispatcherRequest $request */
        $request = $this->getObject('request');
        $query   = $request->getQuery();

        if ($query->has('connect'))
        {
            if ($query->has('token'))
            {
                if (!PlgKoowaConnect::verifyToken($query->token)) {
                    throw new RuntimeException('Invalid JWT token');
                }

                if ($query->has('image')) {
                    $request->isGet() ? $this->_serveFile() : $this->_saveFile();
                }

                if ($request->isGet() && $query->has('serve')) {
                    $this->_serveEntity($this->_getEntity($request));
                }
                elseif ($request->isPost())
                {
                    $result = array(
                        'result' => $this->_processPayload()
                    );

                    $this->getObject('response')
                        ->setContent(json_encode($result), 'application/json')
                        ->send();
                }

                return false;
            }
        }

        return true;
    }

    protected function _parseFile($path)
    {
        $parts = explode('://', $path, 2);

        if (count($parts) !== 2) {
            throw new \UnexpectedValueException('Invalid path: '.$path);
        }

        list($container_slug, $filepath) = $parts;

        if (!$this->getObject('com:files.model.containers')->slug($container_slug)->count()) {
            throw new \UnexpectedValueException('Container not found: '.$container_slug);
        }

        if (!in_array($container_slug, static::$_editable_image_containers)) {
            throw new \UnexpectedValueException('Invalid container: '.$container_slug);
        }

        $extension = strtolower(pathinfo($filepath, PATHINFO_EXTENSION));

        if (!in_array($extension, static::$_editable_image_extensions)) {
            throw new \UnexpectedValueException('Invalid file extension: '.$extension);
        }

        if (strpos($filepath, '/') === false) {
            $folder   = '';
            $filename = $filepath;
        } else {
            $folder    = substr($filepath, 0, strrpos($filepath, '/'));
            $filename  =  \Koowa\basename($path);
        }

        return [
            'container' => $container_slug,
            'folder' => urldecode($folder),
            'name'   => urldecode($filename)
        ];
    }

    protected function _saveFile()
    {
        $request = $this->getObject('request');
        $data    = $request->getData();

        if (!$request->files->has('file')) {
            throw new \UnexpectedValueException('File is missing');
        }

        if (!$data->has('path')) {
            throw new \UnexpectedValueException('Destination path is missing');
        }

        $path = $data->get('path', 'url');
        $data = array_merge($this->_parseFile($path), [
            'file'      => $request->files->file['tmp_name'],
            'overwrite' => true
        ]);

        $c = $this->getObject('com:files.controller.file', ['behaviors' => [
            'permissible' => [
                'permission' => 'controller.permission.default'
            ]
        ]]);
        $c->canAdd();

        $entity = $this->getObject('com:files.controller.file', ['behaviors' => [
            'permissible' => [
                'permission' => 'com://admin/fileman.controller.permission.yesman'
            ]
        ]])->container($data['container'])->add($data);

        $result = [
            'entity' => $entity->toArray()
        ];

        $this->getObject('response')
            ->setContent(json_encode($result), 'application/json')
            ->send();

    }

    protected function _serveFile()
    {
        $request = $this->getObject('request');
        $query    = $request->getQuery();

        if (!$query->has('path')) {
            throw new \UnexpectedValueException('Destination path is missing');
        }

        $path = $query->get('path', 'url');

        $controller = $this->getObject('com:files.controller.file', ['behaviors' => [
            'permissible' => [
                'permission' => 'com://admin/fileman.controller.permission.yesman'
            ]
        ]]);
        $controller->getRequest()->getQuery()->add($this->_parseFile($path));

        $file = $controller->read($this->_parseFile($path));

        if ($file->isNew() || !is_file($file->fullpath)) {
            throw new KControllerExceptionResourceNotFound('File not found');
        }

        /** @var KDispatcherResponseAbstract $response */
        $response = $this->getObject('response');

        $response->attachTransport('stream')
            ->setContent($file->fullpath, $file->mimetype ?: 'application/octet-stream')
            ->getHeaders()->set('Access-Control-Allow-Origin', '*');

        $response->send();
    }

    protected function _getEntity(KControllerRequestInterface $request)
    {
        $query = $request->getQuery();

        return $this->getObject('com:files.model.files')
                    ->container($query->container)
                    ->folder($query->folder)
                    ->name($query->name)
                    ->fetch();
    }

    /**
     * Serve a document for the consumption of the thumbnail service
     *
     * @param integer $id
     */
    protected function _serveEntity($entity)
    {
        if ($entity->isNew()) {
            throw new KControllerExceptionResourceNotFound('Entity not found');
        }

        /** @var KDispatcherResponseAbstract $response */
        $response = $this->getObject('response');

        $response->attachTransport('stream')
                 ->setContent($entity->fullpath, $entity->mimetype ?: 'application/octet-stream')
                 ->getHeaders()->set('Content-Disposition', ['attachment' => ['filename' => '"file"']]);

        $response->send();
    }

    /**
     * Updates the document thumbnail from the request payload
     *
     * @return boolean
     */
    protected function _processPayload()
    {
        /** @var KDispatcherRequest $request */
        $request = $this->getObject('request');
        $data = $request->getData();

        $user_data = $data->user_data;

        if (!isset($user_data['folder'])) {
            $user_data['folder'] = '';
        }

        if (!isset($user_data['folder']) || !isset($user_data['name']) || !isset($user_data['container'])) {
            throw new RuntimeException('Missing user data');
        }

        $scan = $this->getObject('com://admin/fileman.model.scans')
                     ->container($user_data['container'])
                     ->folder($user_data['folder'])
                     ->name($user_data['name'])->fetch();

        if ($scan->isNew()) {
            throw new RuntimeException('Scan not found');
        }

        $entity = $scan->getEntity();

        if ($entity->isNew()) {
            throw new RuntimeException('Entity not found');
        }

        if ($scan->thumbnail && isset($data->thumbnail_url))
        {
            $model = $this->getObject('com:files.model.files');

            $container = $this->getObject('com:files.model.containers')
                              ->slug($user_data['target']['container'])
                              ->fetch();

            if ($container->isNew()) {
                throw new RuntimeException('Thumbnails container not found');
            }

            $model->container($container->slug)
                  ->name($user_data['target']['name'])
                  ->folder($user_data['target']['folder']);

            $thumbnail = $model->create();

            $file = $this->getObject('com:files.model.entity.url', array('data' => array('file' => $data->thumbnail_url)));

            if ($file->contents)
            {
                $thumbnail->contents = $file->contents;
                $thumbnail->save();
            }
            else throw new RuntimeException('Could not read thumbnail content');
        }

        if ($scan->ocr && isset($data->contents_url))
        {
            try {
                $file = $this->getObject('com:files.model.entity.url', array('data' => array('file' => $data->contents_url)));

                if ($file->contents)
                {
                    $model = $this->getObject('com://admin/fileman.model.contents');

                    $path = sprintf('%s/%s', $user_data['folder'], $user_data['name']);

                    $content = $model->container($user_data['container'])->path($path)->fetch();

                    if ($content->isNew()) {
                        $content = $model->create();
                    }

                    $content->setProperty('contents', $file->contents)->save();
                }
            }
            catch (Exception $e) {}
        }

        if (!empty($data->error)) {
            $scan->status = ComFilemanJobScans::STATUS_FAILED;
            PlgSystemJoomlatoolsscheduler::log(sprintf('Failed to process scan %s', $scan->identifier));
            $scan->save();
        } else {
            PlgSystemJoomlatoolsscheduler::log(sprintf('Processed scan %s', $scan->identifier));
            $scan->delete();
        }

        return true;
    }
}
