<?PHP
#
#   FILE:  Tags_TagReference.php
#
#   Part of the Collection Workflow Integration System (CWIS)
#   Copyright 2011 Internet Scout Project
#   http://scout.wisc.edu/
#

class Tags_TagReference extends Tags_ImmutableStruct
{

    /**
     * @const TABLE_NAME SQL table where the tag references are stored
     */
    const TABLE_NAME = "Tags_TagReferences";

    /**
     * Create the tag reference storage table
     * @return TRUE if successful or FALSE otherwise
     */
    public static function CreateStorageTable()
    {
        global $DB;

        $Result = $DB->Query("
            CREATE TABLE IF NOT EXISTS ".self::TABLE_NAME." (
                TagReferenceId    INT,
                UserId            INT,
                ResourceId        INT,
                ControlledNameId  INT,
                IsPrivileged      INT,
                PRIMARY KEY       (TagReferenceId),
                INDEX             (UserId, ResourceId, ControlledNameId)
            );");

        return $Result !== FALSE;
    }

    /**
     * Add a tag reference for the given resource, user, and controlled name.
     * @param $User User object
     * @param $Resource Resource object
     * @param $ControlledName ControlledName object
     * @param $IsPrivileged TRUE if the tag is privileged or FALSE otherwise
     * @return a TagReference object
     */
    public static function AddTagReference(
        User $User,
        Resource $Resource,
        ControlledName $ControlledName,
        $IsPrivileged)
    {
        global $DB;

        $DB->Query("
            SELECT * FROM ".self::TABLE_NAME."
            WHERE UserId = '".addslashes($User->Id())."'
            AND ResourceId = '".addslashes($Resource->Id())."'
            AND ControlledNameId = '".addslashes($ControlledName->Id())."'");

        # tag reference already exists
        if ($DB->NumRowsSelected())
        {
            $TagReference = new Tags_TagReference($DB->FetchRow());

            # IsPrivileged value is different, so update the database
            if ($TagReference->IsPrivileged != $IsPrivileged)
            {
                $TagReference = self::UpdateIsPrivileged(
                    $TagReference,
                    $IsPrivileged);
            }

            return $TagReference;
        }

        # lock the table to avoid tag reference ID collisions
        $DB->Query("LOCK TABLES ".self::TABLE_NAME." WRITE");

        # get the next available ID
        $NextId = $DB->Query("
            SELECT MAX(TagReferenceId) AS NextId
            FROM ".self::TABLE_NAME,
            "NextId");
        $NextId = $NextId ? $NextId+1 : 1;

        # insert a skeleton tag reference record to secure a place in the table
        $DB->Query("
            INSERT INTO ".self::TABLE_NAME." (TagReferenceId)
            VALUES ('".addslashes($NextId)."')");

        # finally unlock the table
        $DB->Query("UNLOCK TABLES");

        # update the record based on the parameters to this method
        $DB->Query("
            UPDATE ".self::TABLE_NAME." SET
            ResourceId = '".addslashes($Resource->Id())."',
            UserId = '".addslashes($User->Id())."',
            ControlledNameId = '".$ControlledName->Id()."',
            IsPrivileged = '".addslashes($IsPrivileged)."'
            WHERE TagReferenceId = '".addslashes($NextId)."'");

        # create properties array to pass to a new tag reference object
        $Properties = array(
            "TagReferenceId" => $NextId,
            "UserId" => $User->Id(),
            "ResourceId" => $Resource->Id(),
            "ControlledNameId" => $ControlledName->Id(),
            "IsPrivileged" => $IsPrivileged);
        $TagReference = new Tags_TagReference($Properties);

        return $TagReference;
    }

    /**
     * Update the user ID value for the given tag reference.
     * @param $TagReference TagReference object
     * @param $User User object
     * @return updated TagReference object
     */
    public static function UpdateUserId($TagReference, User $User)
    {
        global $DB;

        $Id = $TagReference->TagReferenceId;

        # update the record
        $DB->Query("
            UPDATE ".self::TABLE_NAME." SET
            UserId = '".addslashes($User->Id())."'
            WHERE TagReferenceId = '".addslashes($Id)."'");

        # refresh database cache and get new reference data
        $References = self::GetTagReferences(array("TagReferenceId" => $Id));
        $TagReference = array_shift($References);

        return $TagReference;
    }

    /**
     * Update the controlled name ID value for the given tag reference.
     * @param $TagReference TagReference object
     * @param $ControlledNameId new controlled name ID
     * @return updated TagReference object
     */
    public static function UpdateControlledNameId(
        $TagReference,
        $ControlledNameId)
    {
        global $DB;

        $Id = $TagReference->TagReferenceId;

        # update the record
        $DB->Query("
            UPDATE ".self::TABLE_NAME." SET
            ControlledNameId = '".addslashes($ControlledNameId)."'
            WHERE TagReferenceId = '".addslashes($Id)."'");

        # refresh database cache and get new reference data
        $References = self::GetTagReferences(array("TagReferenceId" => $Id));
        $TagReference = array_shift($References);

        return $TagReference;
    }

    /**
     * Update the IsPrivileged value for the given tag reference.
     * @param $TagReference TagReference object
     * @param $IsPrivileged TRUE if the tag is privileged or FALSE otherwise
     * @return updated TagReference object
     */
    public static function UpdateIsPrivileged($TagReference, $IsPrivileged)
    {
        global $DB;

        $Id = $TagReference->TagReferenceId;

        # update the record
        $DB->Query("
            UPDATE ".self::TABLE_NAME." SET
            IsPrivileged = '".addslashes($IsPrivileged)."'
            WHERE TagReferenceId = '".addslashes($Id)."'");

        # refresh database cache and get new reference data
        $References = self::GetTagReferences(array("TagReferenceId" => $Id));
        $TagReference = array_shift($References);

        return $TagReference;
    }

    /**
     * Fetch the tag references that meet the given constraints.
     * @param $Constraints array of constraints
     * @return an array of TagReference objects
     */
    public static function GetTagReferences(array $Constraints)
    {
        global $DB;

        $Where = self::ConstructWhereClause($Constraints);
        $DB->Query("SELECT * FROM ".self::TABLE_NAME." ".$Where);

        $TagReferences = array();

        while (FALSE !== ($Row = $DB->FetchRow()))
        {
            $TagReferenceId = $Row["TagReferenceId"];
            $TagReferences[$TagReferenceId] = new Tags_TagReference($Row);
        }

        return $TagReferences;
    }

    /**
     * Delete all tag references that match the given constraints.
     * @param $Constraints array of constraints
     */
    public static function DeleteTagReferences(array $Constraints)
    {
        global $DB;

        $Where = self::ConstructWhereClause($Constraints);
        $DB->Query("DELETE FROM ".self::TABLE_NAME." ".$Where);
    }

    /**
     * Construct a SQL WHERE clause from the given constraints.
     * @param $Constraints an array of constraints
     * @return a SQL WHERE clause as a string
     */
    protected static function ConstructWhereClause(array $Constraints)
    {
        $Where = NULL;

        foreach ($Constraints as $Constraint => $Value)
        {
            # make sure the constraint is valid
            if (self::ClassHasProperty($Constraint))
            {
                if (!is_null($Where))
                {
                    $Where .= " AND ";
                }

                $Where .= $Constraint . " = '" . addslashes($Value) . "'";
            }
        }

        # add WHERE specifier if there are constraints
        if (!is_null($Where))
        {
            $Where = "WHERE " . $Where;
        }

        return $Where;
    }

    /**
     * Determine if the class has the given property.
     * @param $Property property name
     * @return TRUE if the class has the given property or FALSE otherwise
     */
    protected static function ClassHasProperty($Property)
    {
        return in_array($Property, array(
            "TagReferenceId", "UserId", "ResourceId", "ControlledNameId",
            "IsPrivileged"));
    }

    protected $TagReferenceId;
    protected $UserId;
    protected $ResourceId;
    protected $ControlledNameId;
    protected $IsPrivileged;

}
