<?PHP

class POAIItemFactory implements OAIItemFactory {

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

    # object constructor
    function __construct(array $RepDescr, $RetrievalSearchParameters = NULL)
    {
        # save the repository description
        $this->RepDescr = $RepDescr;

        # save any supplied retrieval parameters
        $this->RetrievalSearchParameters = $RetrievalSearchParameters;
    }

    function GetItem($ItemId)
    {
        # add link to full record page for item
        $Protocol = isset($_SERVER["HTTPS"]) ? "https://" : "http://";
        $ServerName = ($_SERVER["SERVER_NAME"] != "127.0.0.1")
                ? $_SERVER["SERVER_NAME"]
                : $_SERVER["HTTP_HOST"];
        $ServerName = str_replace('/','',$ServerName);
        $SearchInfo["fullRecordLink"] =
            $Protocol.$ServerName.dirname($_SERVER["SCRIPT_NAME"])
            ."/index.php?P=FullRecord&ID=".$ItemId;

        # if a search score is available for the item
        if (isset($this->SearchScores) && isset($this->SearchScores[$ItemId]))
        {
            # add search info for item
            $SearchInfo["searchScore"] = $this->SearchScores[$ItemId];
            $SearchInfo["searchScoreScale"] = $this->SearchScoreScale;
        }

        # attempt to create item
        $Item = new POAIItem($ItemId, $this->RepDescr, $SearchInfo);

        # if item creation failed
        if ($Item->Status() == -1)
        {
            # return NULL to indicate that no item was found with that ID
            return NULL;
        }
        else
        {
            # return item to caller
            return $Item;
        }
    }

    function GetItems($StartingDate = NULL, $EndingDate = NULL)
    {
        return $this->GetItemsInSet(NULL, $StartingDate, $EndingDate);
    }

    function GetItemsInSet($Set,
            $StartingDate = NULL, $EndingDate = NULL, $SearchStrings = NULL)
    {
        $SearchParams = new SearchParameterSet();

        if ($SearchStrings !== NULL)
        {
            foreach ($SearchStrings as $Field => $Value)
            {
                $SearchParams->AddParameter($Value, $Field);
            }
        }

        # if begin date was specified
        if ($StartingDate !== NULL)
        {
            $SearchParams->AddParameter(">=".$StartingDate, "Date Of Record Creation");
        }

        if ($EndingDate !== NULL)
        {
            $SearchParams->AddParameter("<=".$EndingDate, "Date Of Record Creation");
        }


        # if set specified
        if ($Set != NULL)
        {
            # load set mappings
            $this->LoadSetNameInfo();

            # if set is valid
            if (isset($this->SetFields[$Set]))
            {
                # add field spec to search strings
                $SearchParams->AddParameter(
                    "= ".$this->SetValues[$Set],
                    $this->SetFields[$Set]);
            }
            else
            {
                # set will not match anything so return empty array to caller
                return array();
            }
        }

        # set up search parameter groups
        if ($this->RetrievalSearchParameters)
        {
            foreach ($this->RetrievalSearchParameters as $Field => $Value)
            {
                $SearchParams->AddParameter($Value, $Field);
            }
        }

        # allow any hooked handlers to modify search parameters if desired
        $SignalResult = $GLOBALS["AF"]->SignalEvent(
                "OAIPMHServer_EVENT_MODIFY_RESOURCE_SEARCH_PARAMETERS",
                array("SearchParameters" => $SearchParams));
        $SearchParams = $SignalResult["SearchParameters"];

        # get a ResourceFactory
        $RFactory = new ResourceFactory();

        # if our search has no conditions, and just wants all the resources
        if ($SearchParams->ParameterCount() == 0)
        {
            # pull them out of the item factory and construct a SearchResult
            $SearchResults = array_fill_keys($RFactory->GetItemIds(), 1);
            $this->SearchScoreScale = 1;
        }
        else
        {
            # otherwise, perform search for desired items
            $Engine = new SPTSearchEngine();
            $SearchResults = $Engine->Search($SearchParams);
            $this->SearchScoreScale = $Engine->FieldedSearchWeightScale($SearchParams);

            if (isset($SearchResults[MetadataSchema::SCHEMAID_DEFAULT]))
            {
                $SearchResults = $SearchResults[MetadataSchema::SCHEMAID_DEFAULT];
            }
            else
            {
                $SearchResults = array();
            }
        }

        # filter out non-viewable resources
        $ViewableIds = $RFactory->FilterNonViewableResources(
            array_keys($SearchResults), $GLOBALS["G_User"]);
        $SearchResults = array_intersect_key(
            $SearchResults, array_flip($ViewableIds) );

        # save search scores for
        $this->SearchScores = $SearchResults;

        # extract resource IDs from search results
        $ItemIds = array_keys($SearchResults);

        # allow any hooked handlers to filter results if desired
        $SignalResult = $GLOBALS["AF"]->SignalEvent(
                "OAIPMHServer_EVENT_FILTER_RESULTS",
                array("ItemIds" => $ItemIds));
        $ItemIds = $SignalResult["ItemIds"];

        # return array of resource IDs to caller
        return $ItemIds;
    }

    # retrieve IDs of items that match search parameters (only needed if OAI-SQ supported)
    function SearchForItems($SearchParams, $StartingDate = NULL, $EndingDate = NULL)
    {
        # translate field IDs into field names for search parameters
        $Schema = new MetadataSchema;
        foreach ($SearchParams as $FieldId => $Value)
        {
            if ($FieldId == "X-KEYWORD-X")
            {
                $SearchStrings["XXXKeywordXXX"] = $Value;
            }
            else
            {
                $Field = $Schema->GetField($FieldId);
                $SearchStrings[$Field->Name()] = $Value;
            }
        }

        # perform search and return results to caller
        return $this->GetItemsInSet(NULL, $StartingDate, $EndingDate, $SearchStrings);
    }

    # return array containing all set specs (with human-readable set names as keys)
    function GetListOfSets()
    {
        # make sure set name info is loaded
        $this->LoadSetNameInfo();

        # return list of sets to caller
        return $this->SetSpecs;
    }


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

    private $SetSpecs;
    private $SetFields;
    private $SetValues;
    private $RepDescr;
    private $RetrievalSearchParameters;
    private $SearchScores;
    private $SearchScoreScale;

    # normalize value for use as an OAI set spec
    private function NormalizeForSetSpec($Name)
    {
        return preg_replace("/[^a-zA-Z0-9\-_.!~*'()]/", "", $Name);
    }

    # load normalized set names and name mappings
    private function LoadSetNameInfo()
    {
        # if set names have not already been loaded
        if (!isset($this->SetSpecs))
        {
            # start with empty list of sets
            $this->SetSpecs = array();
            $this->SetFields = array();
            $this->SetValues = array();

            # for each metadata field that is a type that can be used for sets
            $Schema = new MetadataSchema();
            $Fields = $Schema->GetFields(MetadataSchema::MDFTYPE_TREE
                    |MetadataSchema::MDFTYPE_CONTROLLEDNAME
                    |MetadataSchema::MDFTYPE_OPTION);
            foreach ($Fields as $Field)
            {
                # if field is flagged as being used for OAI sets
                if ($Field->UseForOaiSets())
                {
                    # retrieve all possible values for field
                    $FieldValues = $Field->GetPossibleValues();

                    # prepend field name to each value and add to list of sets
                    $FieldName = $Field->Name();
                    $NormalizedFieldName = $this->NormalizeForSetSpec($FieldName);
                    foreach ($FieldValues as $Value)
                    {
                        $SetSpec = $NormalizedFieldName.":"
                                .$this->NormalizeForSetSpec($Value);
                        $this->SetSpecs[$FieldName.": ".$Value] = $SetSpec;
                        $this->SetFields[$SetSpec] = $FieldName;
                        $this->SetValues[$SetSpec] = $Value;
                    }
                }
            }
        }
    }
}

?>
