<?php
/**
 * This code is licensed under AGPLv3 license or Afterlogic Software License
 * if commercial version of the product was purchased.
 * For full statements of the licenses see LICENSE-AFTERLOGIC and LICENSE-AGPL3 files.
 */

namespace Afterlogic\DAV\FS;

use Afterlogic\DAV\Server;

/**
 * @license https://www.gnu.org/licenses/agpl-3.0.html AGPL-3.0
 * @license https://afterlogic.com/products/common-licensing Afterlogic Software License
 * @copyright Copyright (c) 2019, Afterlogic Corp.
 */
class Directory extends \Sabre\DAV\FSExt\Directory implements \Sabre\DAVACL\IACL, \Afterlogic\DAV\FS\INode, \Sabre\DAV\IProperties
{
    use NodeTrait;
    use PropertyStorageTrait;

    /**
     * @var object $UserObject
     */
    protected $UserObject = null;

    /**
     * @var \Aurora\Modules\Core\Models\Tenant
     */
    protected $oTenant = null;

    public function __construct($storage, $path)
    {
        $this->storage = $storage;
        parent::__construct($path);
    }

    public function getUserObject()
    {
        if ($this->UserObject === null) {
            $sUserPublicId = $this->getUser();
            if ($sUserPublicId !== null) {
                $this->UserObject = \Aurora\Modules\Core\Module::getInstance()->GetUserByPublicId($sUserPublicId);
            }
        }
        return $this->UserObject;
    }

    public function getTenant()
    {
        if ($this->oTenant === null) {
            $oUser = $this->getUserObject();
            if ($oUser) {
                $this->oTenant = \Aurora\Api::getTenantById($oUser->IdTenant);
            }
        }

        return $this->oTenant;
    }

    public function childExists($name)
    {
        $mResult = false;
        try {
            $mResult = !!$this->getChild($name);
        } catch (\Exception $oEx) {
        }

        return $mResult;
    }

    public function createDirectory($name)
    {
        if ($this->childExists($name)) {
            throw new \Sabre\DAV\Exception\Conflict('Can\'t create a directory');
        }

        // Check for long names and truncate if necessary
        $maxLength = 255; // Maximum filename length in most filesystems
        if (strlen($name) > $maxLength) {
            throw new \Sabre\DAV\Exception\Forbidden('Name is too long');
        }

        parent::createDirectory($name);
    }

    public function createFile($name, $data = null, $rangeType = 0, $offset = 0, $extendedProps = [])
    {
        $result = null;
        if (!$this->childExists($name)) {
            $result = parent::createFile($name);
        }
        $oFile = $this->getChild($name);

        if ($oFile instanceof File) {
            $result = $oFile->patch($data, $rangeType, $offset);

            $aProps = $oFile->getProperties(['Owner', 'ExtendedProps']);

            if (!isset($aProps['Owner'])) {
                $aProps['Owner'] = $this->getUser();
            }
            $aCurrentExtendedProps = [];
            if (isset($aProps['ExtendedProps'])) {
                $aCurrentExtendedProps = $aProps['ExtendedProps'];
            }
            foreach ($extendedProps as $sPropName => $propValue) {
                if ($propValue === null) {
                    if (isset($aCurrentExtendedProps[$sPropName])) {
                        unset($aCurrentExtendedProps[$sPropName]);
                    }
                } else {
                    $aCurrentExtendedProps[$sPropName] = $propValue;
                }
            }
            $aProps['ExtendedProps'] = $aCurrentExtendedProps;

            $oFile->updateProperties($aProps);

            if (!$this->updateQuota()) {
                $oFile->delete();
                throw new \Sabre\DAV\Exception\InsufficientStorage();
            }
        }

        return $result;
    }

    /**
     * @return \Afterlogic\DAV\FS\INode
     */
    public function getChild($name)
    {
        if (strlen(trim($name)) === 0) {
            throw new \Sabre\DAV\Exception\Forbidden('Permission denied to emty item');
        }

        $path = $this->path . '/' . trim($name, '/');

        if (!file_exists($path)) {
            throw new \Sabre\DAV\Exception\NotFound('File could not be located');
        }
        if ($name == '.' || $name == '..') {
            throw new \Sabre\DAV\Exception\Forbidden('Permission denied to . and ..');
        }

        return is_dir($path) ? new self($this->getStorage(), $path) : new File($this->getStorage(), $path);
    }

    /**
     * @return \Afterlogic\DAV\FS\INode[]
     */
    public function getChildren()
    {
        $children = [];

        // if (isset(Root::$childrenCache[$this->getStorage()][$this->getPath()])) {
        //     $children = Root::$childrenCache[$this->getStorage()][$this->getPath()];
        // } else {
            $children = parent::getChildren();

            foreach ($children as $iKey => $oChild) {
                if ($oChild->getName() === '.sabredav' || ($oChild instanceof HistoryDirectory)) {
                    unset($children[$iKey]);
                }
            }
        // }

        return $children;
    }

    public function delete()
    {
        $this->deleteResourceData();

        $files = new \Illuminate\Filesystem\Filesystem();
        $files->deleteDirectory($this->path);
        $this->deleteShares();
        $this->deleteFavorites();
        $this->updateQuota();

        return true;
    }

    public function Search($pattern, $path = null)
    {
        $aResult = [];

        $path = ($path === null) ? $this->getPath() : $path;
        $aItems = \Aurora\System\Utils::SearchFiles($path, $pattern);
        if ($aItems) {
            foreach ($aItems as $sItem) {
                $sItemName = \basename($sItem);
                $ext = strtolower(substr($sItemName, -5));
                if ($sItemName !== '.sabredav' && !(is_dir($sItem) && $ext === '.hist')) {
                    $aResult[] = is_dir($sItem) ? new self($this->getStorage(), $sItem) : new File($this->getStorage(), $sItem);
                }
            }
        }

        return $aResult;
    }

    public function getFullQuotaInfo()
    {
        $iFreeSize = 0;
        $iUsageSize = 0;

        $personalRoot =  Server::getInstance()->tree->getNodeForPath('files/' . $this->storage);
        if ($personalRoot instanceof Directory) {
            $sRootPath = $personalRoot->getRootPath();
            $aSize = \Aurora\System\Utils::GetDirectorySize($sRootPath);
            $iUsageSize += (int) $aSize['size'];
        }

        $UserPublicId = $this->getUser();
        if ($UserPublicId) {
            $oTenant = $this->getTenant();
            if ($oTenant) {
                $iFreeSize = ($oTenant->FilesUsageDynamicQuotaInMB * 1024 * 1024) - $iUsageSize;
            }
        }

        return [$iUsageSize, $iFreeSize];
    }

    public function updateQuota()
    {
        if (isset($GLOBALS['__FILESTORAGE_MOVE_ACTION__']) && $GLOBALS['__FILESTORAGE_MOVE_ACTION__']) {
            return true;
        }

        $iSizeUsage = 0;
        $aQuota = $this->getFullQuotaInfo();
        if (isset($aQuota[0])) {
            $iSizeUsage = $aQuota[0];
        }
        $oTenant = $this->getTenant();
        if (!isset($oTenant)) {
            return true;
        } else {
            return true;
            // TODO:
            /*
                        $oTenantsMan = $this->getTenantsMan();
                        if ($oTenantsMan)
                        {
                            return $oTenantsMan->allocateFileUsage($oTenant, $iSizeUsage);
                        }
             *
             */
        }
    }

    public function getChildrenProperties()
    {
        return $this->getResourceData();
    }

    /**
     * Returns array of SplFileInfo
     *
     * @return array
     */
    public function getFileListRecursive()
    {
        $files = [];
        $items = new \RecursiveIteratorIterator(
            new \RecursiveDirectoryIterator($this->path),
            \RecursiveIteratorIterator::SELF_FIRST
        );
        $excludedFiles = ['.sabredav'];

        foreach ($items as $item) {
            /* @var $item \SplFileInfo */
            if ($item->isFile() && $item->isReadable() && !in_array($item->getFilename(), $excludedFiles)) {
                $files[] = $item;
            }
        }

        return $files;
    }

    /**
     * Returns the last modification time, as a unix timestamp
     *
     * @return int
     */
    public function getLastModified()
    {
        if (!empty($this->path)) {
            return filemtime($this->path);;
        }
        return 0;
    }

    /**
     * Returns available diskspace information.
     *
     * @return array
     */
    public function getQuotaInfo()
    {
        return [
            0,
            0,
        ];
    }

    public function moveInto($targetName, $sourcePath, \Sabre\DAV\INode $sourceNode)
    {
        // We only support moving files and directories
        if (!$sourceNode instanceof self && !$sourceNode instanceof File) {
            return false;
        }

        // Saving properties before move
        $sourceProperties = $sourceNode->getProperties([]);
        // Deleting resource data to avoid conflicts
        $sourceNode->deleteResourceData();

        $result = parent::moveInto($targetName, $sourcePath, $sourceNode);

        if ($result && !empty($sourceProperties)) {
            $targetNode = null;
            try {
                $targetNode = $this->getChild($targetName);
            } catch (\Exception $oEx) {
                // not found
            }

            if ($targetNode instanceof \Afterlogic\DAV\FS\File || $targetNode instanceof \Afterlogic\DAV\FS\Directory) {
                $targetNode->updateProperties($sourceProperties);
            }
        }

        return $result;
    }
}
