<?PHP
#
#   FILE:  FolderFactory.php
#
#   Part of the Collection Workflow Information System (CWIS)
#   Copyright 2012-2013 Edward Almasy and Internet Scout Research Group
#   http://scout.wisc.edu/cwis/
#

/**
* Factory object for Folder class, used to retrieve and manage Folders
* and groups of Folders.
*/
class FolderFactory extends ItemFactory
{

    # ---- PUBLIC INTERFACE --------------------------------------------------

    /**
    * Constructor for FolderFactory.
    * @param int $OwnerId ID of owner of folders to be manipulated by factory.
    *       If specified then all operations pertain only to folders with
    *       the specified owner.  (OPTIONAL)
    */
    public function __construct($OwnerId = NULL)
    {
        # set up item factory base class
        parent::__construct("Folder", "Folders", "FolderId", "FolderName", TRUE);

        # set up filtering to only folders by specified owner (if given)
        if ($OwnerId !== NULL)
        {
            $this->OwnerId = intval($OwnerId);
            $this->SetOrderOpsCondition("OwnerId = ".$this->OwnerId);
        }
    }

    /**
    * Create new folder that will contain only one type of item.
    * @param mixed $ItemType Type of item that folder will contain.
    * @param string $FolderName String containing name of folder.  (OPTIONAL)
    * @param int $OwnerId Numerical ID of folder owner.  (OPTIONAL)
    * @return New folder (object).
    */
    public function CreateFolder($ItemType, $FolderName = NULL, $OwnerId = NULL)
    {
        # retrieve numerical item type
        $ItemTypeId = ($ItemType === Folder::MIXEDCONTENT)
                ? $ItemType : Folder::GetItemTypeId($ItemType);

        # use default owner if available and none specified
        if (($OwnerId === NULL) & ($this->OwnerId !== NULL))
                {  $OwnerId = $this->OwnerId;  }

        # add new folder to database
        $this->DB->Query("INSERT INTO Folders SET"
                ." ContentType = ".$ItemTypeId
                .($FolderName ? ", FolderName = '".addslashes($FolderName)."'" : "")
                .(($OwnerId !== NULL) ? ", OwnerId = ".intval($OwnerId) : ""));

        # retrieve ID of new folder
        $Id = $this->DB->LastInsertId();

        # create new folder object and return it to caller
        return new Folder($Id);
    }

    /**
    * Create new folder that can contain multiple types of items.  (This is a
    * separate operation because mixed item types incurs execution overhead.)
    * @param string $FolderName String containing name of folder.  (OPTIONAL)
    * @param int $OwnerId Numerical ID of folder owner.  (OPTIONAL)
    * @return New folder (object).
    */
    public function CreateMixedFolder($FolderName = NULL, $OwnerId = NULL)
    {
        # create new mixed-content folder and return it to caller
        return $this->CreateFolder(Folder::MIXEDCONTENT, $FolderName, $OwnerId);
    }

    /**
    * Get total number of folders currently existing.
    * @return Number of folders.
    */
    public function GetFolderCount()
    {
        return $this->GetItemCount(isset($this->OwnerId)
                ? "OwnerId = ".intval($this->OwnerId) : NULL);
    }

    /**
    * Retrieve folder with specified normalized name (as generated by
    * Folder::NormalizeFolderName() method).
    * @param string $NormalizedName Normalized folder name.
    * @param int $OwnerId ID of folder owner.  (OPTIONAL)
    * @return Folder object or NULL if no folder found.  If multiple folders
    *       with the specified name are found, the one with the lowest folder
    *       ID is returned.
    */
    public function GetFolderByNormalizedName($NormalizedName, $OwnerId = NULL)
    {
        # use default owner if available and none specified
        if (($OwnerId === NULL) & ($this->OwnerId !== NULL))
                {  $OwnerId = $this->OwnerId;  }

        # query database for folder ID
        $FolderId = $this->DB->Query("SELECT FolderId FROM Folders"
                    ." WHERE NormalizedName = '".addslashes($NormalizedName)."'"
                    .(($OwnerId !== NULL) ? " AND OwnerId = ".$this->OwnerId : "")
                    ." ORDER BY FolderId ASC",
                "FolderId");

        # if folder found with specified name and owner
        if ($FolderId !== FALSE && !is_null($FolderId))
        {
            # create folder object and return it to caller
            return new Folder($FolderId);
        }
        else
        {
            # return NULL to caller to indicate folder not found
            return NULL;
        }
    }

    /**
    * Retrieve folders containing specified item.
    * @param object $Item Object (must have Id() method) or item ID.
    * @param mixed $ItemType The item type.
    * @param int $OwnerId Optional owner ID to restrict folders to.
    * @param bool $SharedFoldersOnly Whether to only return shared folders.
    * @return array Array of Folder objects that contain specified item.
    */
    public function GetFoldersContainingItem($Item, $ItemType, $OwnerId = NULL,
            $SharedFoldersOnly = FALSE)
    {
        # assume we won't find any folders
        $Folders = array();

        # retrieve item ID
        $ItemId = is_object($Item) ? $Item->Id() : $Item;

        # retrieve numerical item type
        $ItemTypeId = ($ItemType === Folder::MIXEDCONTENT)
                ? $ItemType : Folder::GetItemTypeId($ItemType);

        # use default owner if available and none specified
        if (($OwnerId === NULL) & ($this->OwnerId !== NULL))
                {  $OwnerId = $this->OwnerId;  }

        # query database for IDs of all folders that contain item
        $this->DB->Query("
            SELECT DISTINCT FolderItemInts.FolderId
            FROM FolderItemInts
            LEFT JOIN Folders
            ON FolderItemInts.FolderId = Folders.FolderId
            WHERE FolderItemInts.ItemId = '".intval($ItemId)."'
            AND (FolderItemInts.ItemTypeId = '".intval($ItemTypeId)."'
                 OR Folders.ContentType = '".intval($ItemTypeId)."')
            ".(($OwnerId !== NULL) ? " AND Folders.OwnerId = ".intval($OwnerId) : "")."
            ".(($SharedFoldersOnly) ? " AND Folders.IsShared = 1" : ""));
        $FolderIds = $this->DB->FetchColumn("FolderId");

        # create array of folders from folder IDs
        foreach ($FolderIds as $Id)
        {
            $Folders[$Id] = new Folder($Id);
        }

        # return folders (if any) to caller
        return $Folders;
    }

    /**
    * Retrieve folders with specified name, owner, or default content type.  If
    * no parameters are specified, all existing folders are returned.  If no owner
    * ID parameter is supplied and an owner ID was specified for
    * FolderFactory::FolderFactory(), then that owner ID is used.
    * @param mixed $ItemType String containing type of item to search for as default
    *       content type of folder.  To search for only mixed-content-type
    *       folders specify Folder::MIXEDCONTENT.  (OPTIONAL, defaults to NULL)
    * @param int $OwnerId Numerical ID of folder owner.  (OPTIONAL, defaults to NULL)
    * @param string $Name String containing target folder name.  (OPTIONAL,
    *       defaults to NULL)
    * @param int $Offset Zero-based offset into list of folders.  (OPTIONAL)
    * @param int $Count Number of folders to retrieve beginning at specified
    *       offset.  (OPTIONAL)
    * @return Array of Folder objects that match specified parameters.
    */
    public function GetFolders($ItemType = NULL, $OwnerId = NULL, $Name = NULL,
            $Offset = 0, $Count = NULL)
    {
        # retrieve numerical item type
        $ItemTypeId = ($ItemType === Folder::MIXEDCONTENT)
                ? $ItemType : Folder::GetItemTypeId($ItemType);

        # retrieve IDs of all folders that match specified parameters
        $Condition = ($ItemTypeId !== NULL) ? "ContentType = ".intval($ItemTypeId) : NULL;
        if (($OwnerId !== NULL) || ($this->OwnerId !== NULL))
        {
            $Condition .= ($Condition ? " AND " : "")."OwnerId = "
                    .intval(($OwnerId !== NULL) ? $OwnerId : $this->OwnerId);
        }
        if ($Name !== NULL)
        {
            $Condition .= ($Condition ? " AND " : "")."FolderName = '"
                    .addslashes($Name)."'";
        }
        $FolderIds = $this->GetItemIds($Condition, FALSE, "FolderId");

        # pare down list to requested range
        if ($Offset || $Count)
        {
            $FolderIds = $Count ? array_slice($FolderIds, $Offset, $Count)
                    : array_slice($FolderIds, $Offset);
        }

        # create array of folders based on IDs
        $Folders = array();
        foreach ($FolderIds as $FolderId)
        {
            $Folders[$FolderId] = new Folder($FolderId);
        }

        # return folders (if any) to caller
        return $Folders;
    }


    # ---- PRIVATE INTERFACE -------------------------------------------------

    private $OwnerId;
}
