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

# ----- EXPORTED FUNCTIONS ---------------------------------------------------

/**
* Get the list of allowed values for a field.
* @param MetadataField $Field Field for which values are desired
* @return array of possible values (Id => Name)
*/
function DeterminePossibleValues($Field)
{
    # for User fields, we want to list those with a specific set of privs
    if ($Field->Type() == MetadataSchema::MDFTYPE_USER)
    {
        $UserFactory = new CWUserFactory();
        $PossibleValues = $UserFactory->GetUsersWithPrivileges(
            PRIV_RESOURCEADMIN, PRIV_MYRESOURCEADMIN, PRIV_RELEASEADMIN);
    }
    elseif ($Field->Type() == MetadataSchema::MDFTYPE_TREE)
    {
        $MaxDepth = $Field->MaxDepthForAdvancedSearch();
        $AllValues = $Field->GetPossibleValues();

        $PossibleValues = array();
        foreach ($AllValues as $ClassId => $ClassName)
        {
            if (count(explode(" -- ", $ClassName)) <= $MaxDepth)
            {
                $PossibleValues[$ClassId] = $ClassName;
            }
        }
    }
    else
    {
        # otherwise GetPossibleValues is our friend
        $PossibleValues = $Field->GetPossibleValues();
    }

    return $PossibleValues;
}

/**
* Determine which values on a list of candidates should be disabled
* @param MetadataField $Field Metadata field to use
* @param array $PossibleValues Values to consider
* @return array with keys giving disabled value ids.
*/
function DetermineDisabledValues($Field, $PossibleValues)
{
    static $Factories;

    # user fields and trees don't support disabled values
    if ($Field->Type() == MetadataSchema::MDFTYPE_USER ||
        $Field->Type() == MetadataSchema::MDFTYPE_TREE)
    {
        return array();
    }

    $SchemaId = $Field->SchemaId();

    if (!isset($Factories[$SchemaId]))
    {
        $Factories[$SchemaId] = new ResourceFactory($SchemaId);
    }

    $DisabledValues = array();

    if ($Field->Type() == MetadataSchema::MDFTYPE_FLAG)
    {
        foreach ($PossibleValues as $ValueId => $Value)
        {
            $ResourceIds = $Factories[$SchemaId]->GetMatchingResources(
                array($Field->Id() => $ValueId), TRUE, FALSE);
            $ResourceIds = $Factories[$SchemaId]->FilterNonViewableResources(
                $ResourceIds, $GLOBALS["G_User"]);
            if (count($ResourceIds)==0)
            {
                $DislabedValues[$ValueId] = 1;
            }
        }
    }
    else
    {
        foreach ($PossibleValues as $ValueId => $Value)
        {
            if ($Factories[$SchemaId]->AssociatedVisibleResourceCount(
                $ValueId, $GLOBALS["G_User"]) == 0)
            {
                $DisabledValues[$ValueId] = 1;
            }
        }
    }

    return $DisabledValues;
}

/**
* Convert value names to identifiers
* @param MetadataField $Field Field to use
* @param array $Values Values to convert
* @return array of converted values
*/
function ConvertValueNamesToIds($Field, $Values)
{
    switch ($Field->Type())
    {
        case MetadataSchema::MDFTYPE_USER:
            $RetVal = array();

            foreach ($Values as $UserName)
            {
                # if we have a leading equals sign
                if (strlen($UserName) && $UserName[0] == "=")
                {
                    # try to look up this user, add them if found
                    $UserName = substr($UserName, 1);
                    $User = new CWUser($UserName);
                    if ($User->Status() == U_OKAY)
                    {
                        $RetVal[]= $User->Id();
                    }
                }
            }
            break;

        case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
        case MetadataSchema::MDFTYPE_OPTION:
        case MetadataSchema::MDFTYPE_TREE:
            $RetVal = array();

            $Factory = $Field->GetFactory();
            foreach ($Values as $Value)
            {
                # if we have a leading equals sign
                if (strlen($Value) && $Value[0] == "=")
                {
                    # try to look up this value, add it if found
                    $Value = substr($Value, 1);
                    $Id = $Factory->GetItemIdByName($Value);
                    if ($Id !== FALSE)
                    {
                        $RetVal[]= $Id;
                    }
                }
            }
            break;

        case MetadataSchema::MDFTYPE_FLAG:
            $RetVal = array();

            foreach ($Values as $Value)
            {
                if (strlen($Value) && $Value[0] == "=")
                {
                    $Value = substr($Value, 1);
                    $RetVal[]= $Value;
                }
            }
            break;

        default:
            $RetVal = $Values;
            break;
    }

    return $RetVal;
}

# ----- MAIN -----------------------------------------------------------------

# check if we have a legacy search URL, redirect
#  to SearchREsults page if we do
if (SearchParameterSet::IsLegacyUrl($_SERVER["QUERY_STRING"]))
{
    $GLOBALS["AF"]->SetJumpToPage(
        "SearchResults&".SearchParameterSet::ConvertLegacyUrl(
            $_SERVER["QUERY_STRING"]));
    return;
}

$TextFieldTypes =
    MetadataSchema::MDFTYPE_TEXT |
    MetadataSchema::MDFTYPE_PARAGRAPH |
    MetadataSchema::MDFTYPE_CONTROLLEDNAME |
    MetadataSchema::MDFTYPE_NUMBER |
    MetadataSchema::MDFTYPE_FILE |
    MetadataSchema::MDFTYPE_TREE |
    MetadataSchema::MDFTYPE_IMAGE |
    MetadataSchema::MDFTYPE_DATE |
    MetadataSchema::MDFTYPE_TIMESTAMP |
    MetadataSchema::MDFTYPE_URL |
    MetadataSchema::MDFTYPE_REFERENCE ;

$SearchLimitTypes = array(
    MetadataSchema::MDFTYPE_OPTION,
    MetadataSchema::MDFTYPE_TREE,
    MetadataSchema::MDFTYPE_FLAG,
    MetadataSchema::MDFTYPE_USER );

$SortFieldTypes =
    MetadataSchema::MDFTYPE_TEXT |
    MetadataSchema::MDFTYPE_NUMBER |
    MetadataSchema::MDFTYPE_DATE |
    MetadataSchema::MDFTYPE_TIMESTAMP |
    MetadataSchema::MDFTYPE_URL ;


# construct a list of fields needing a text form
$H_FieldsHavingTextForms = array(
    "KEYWORD" => "Keyword" );

# construct a list of fields needing a search limit
$H_SearchLimits = array();

$AllSchemas = MetadataSchema::GetAllSchemas();

# extract the names of all schemas
$H_SchemaNames = array();
foreach ($AllSchemas as $SchemaId => $Schema)
{
    $H_SchemaNames[$SchemaId] =
            ($SchemaId == MetadataSchema::SCHEMAID_DEFAULT) ?
            "Resource" : $Schema->Name();
}

# generate a list of all the fields that want to have text searches
# key this by name
$TextFields = array();
foreach ($AllSchemas as $SchemaId => $Schema)
{
    # iterate over candidate fields, including those that belong
    $Fields = $Schema->GetFields(
        $TextFieldTypes, MetadataSchema::MDFORDER_DISPLAY);
    foreach ($Fields as $FieldId => $Field)
    {
        if ($Field->Enabled() &&
            $Field->IncludeInAdvancedSearch() &&
            $Field->UserCanView($GLOBALS["G_User"]) )
        {
            if ($Field->Type() == MetadataSchema::MDFTYPE_TREE &&
                $Field->DisplayAsListForAdvancedSearch() )
            {
                continue;
            }

            $TextFields[$Field->GetDisplayName()][]= $FieldId;
        }
    }
}

# make sure that we display one field for each field name
foreach ($TextFields as $DisplayName => $FieldIds)
{
    $Key = implode("-", $FieldIds);
    $H_FieldsHavingTextForms[$Key] = $DisplayName;
}

# now, on to the search limits
foreach ($AllSchemas as $SchemaId => $Schema)
{
    # iterate over candidate fields, including those that belong
    foreach ($SearchLimitTypes as $LimitType)
    {
        $Fields = $Schema->GetFields(
            $LimitType,
            MetadataSchema::MDFORDER_DISPLAY );

        foreach ($Fields as $FieldId => $Field)
        {
            if ($Field->Enabled() &&
                $Field->IncludeInAdvancedSearch() &&
                $Field->UserCanView($GLOBALS["G_User"]))
            {
                if ($Field->Type() == MetadataSchema::MDFTYPE_TREE &&
                    !$Field->DisplayAsListForAdvancedSearch() )
                {
                    continue;
                }

                $H_SearchLimits[$SchemaId][$FieldId] = $Field->GetDisplayName();
            }
        }
    }
}

# construct a list of sort fields
$H_SortFields = array("R" => "Relevance");

# iterate over candidate fields, including those that belong
$Schema = new MetadataSchema(MetadataSchema::SCHEMAID_DEFAULT);
$Fields = $Schema->GetFields(
    $SortFieldTypes, MetadataSchema::MDFORDER_DISPLAY);
foreach ($Fields as $FieldId => $Field)
{
    if ($Field->Enabled() &&
        $Field->IncludeInSortOptions() &&
        $Field->UserCanView($GLOBALS["G_User"]))
    {
        $H_SortFields[$FieldId] = $Field->GetDisplayName();
    }
}

# use sort field from URL parameters if available and visible,
#   otherwise default to relevance
$H_SelectedSortField =
    (isset($_GET["SF"]) && array_key_exists($_GET["SF"], $H_SortFields)) ?
    $_GET["SF"] : "R";

# extract records per page setting
$H_RecordsPerPage = isset($_GET["RP"]) ? intval($_GET["RP"]) : NULL;

# determine saved searches to list and RecordsPerPage preference (if any)
if ($GLOBALS["G_User"]->IsLoggedIn())
{
    $SSFactory = new SavedSearchFactory();
    $H_SavedSearches = $SSFactory->GetSearchesForUser(
            $GLOBALS["G_User"]->Id());
    if (is_null($H_RecordsPerPage))
    {
        $H_RecordsPerPage = $GLOBALS["G_User"]->Get("RecordsPerPage");
    }
}
else
{
    $H_SavedSearches = array();
}

# if there was a saved search specified, we'll want to snag that and
#  extract search information from it
if (isset($_GET["ID"]))
{
    $H_SavedSearch = new SavedSearch( intval($_GET["ID"]) );
    $H_SearchParameters = $H_SavedSearch->SearchParameters();
}
else
{
    # otherwise, pull the search information out of the URL
    $H_SearchParameters = new SearchParameterSet();
    $H_SearchParameters->UrlParameters($_GET);
}

# determine which search limits should be open
$OpenLimitSchemas = array();
foreach ($H_SearchParameters->GetFields() as $FieldId)
{
    # check if this field even exists
    if (MetadataSchema::FieldExistsInAnySchema($FieldId))
    {
        # and if so mark it as open
        $Field = new MetadataField($FieldId);
        if (isset($H_SearchLimits[$Field->SchemaId()][$FieldId]))
        {
            $OpenLimitSchemas[$Field->SchemaId()] = 1;
        }
    }
}
$H_OpenSearchLimits = array_keys($OpenLimitSchemas);

$H_OpenByDefault = $GLOBALS["G_SysConfig"]->DisplayLimitsByDefault();

# determine which 'contains' text fields should be selected
$H_SelectedFields = array();

# if a user is logged in, and we're not loading a saved search,
#  and we're not refining an existing search, then we want to
#  load the user's saved search selections
if ($GLOBALS["G_User"]->IsLoggedIn() &&
    !isset($_GET["ID"]) && !isset($_GET["RF"]))
{
    $FieldData = $GLOBALS["G_User"]->Get("SearchSelections");
    if (strlen($FieldData))
    {
        $H_SelectedFields = unserialize($FieldData);
    }
}

# if the user didn't *have* any saved search selections, fall back to defaults
if (!count($H_SelectedFields))
{
    # if a search was specified
    if ($H_SearchParameters->ParameterCount())
    {
        # if this includes keywords, then select Keyword
        if (count($H_SearchParameters->GetKeywordSearchStrings()))
        {
            $H_SelectedFields[]= "KEYWORD";
        }

        # the the list of all other fields in the search
        $FieldsInSearch = $H_SearchParameters->GetFields();

        # iterate over the fields that have text forms, adding those
        # for which we have a value to the selected parameters
        $RemaingFields = array();
        foreach ($H_FieldsHavingTextForms as $FieldIds => $DisplayName)
        {
            # start off assuming we don't have a value
            $FoundInSearch = FALSE;

            # iterate over the fields for this selection, checking if
            # we have a value for any of them
            foreach (explode("-", $FieldIds) as $FieldId)
            {
                if (in_array($FieldId, $FieldsInSearch))
                {
                    $FoundInSearch = TRUE;
                    break;
                }
            }

            # if we did find a value, select this field
            # otherwise, add it to the list of empty fields
            if ($FoundInSearch)
            {
                $H_SelectedFields[]= $FieldIds;
            }
            else
            {
                $RemainingFields[]= $FieldIds;
            }
        }

        # and enough of the other fields to get 4 displayed
        while (count($H_SelectedFields) < 4)
        {
            $H_SelectedFields[]= array_shift($RemainingFields);
        }
    }

    # if no fields are selected, use the first four
    if (count($H_SelectedFields)==0)
    {
        $H_SelectedFields = array_slice(
            array_keys($H_FieldsHavingTextForms),
            0, 4);
    }
}