<?PHP

class Resource_Test extends PHPUnit_Framework_TestCase
{
    protected static $TestFieldIds;
    protected static $TestFields;

    /**
    * Prior to running any of the tests, this function is
    * run. It creates all of the test Metadata fields and adds
    * them to class variables $TestFieldIds and $TestFields
    * so each function may use them.
    */
    public static function setUpBeforeClass()
    {
        # construct the schema object
        $Schema = new MetadataSchema(MetadataSchema::SCHEMAID_DEFAULT);

        self::$TestFieldIds = array();

        # outline fields to be created
        self::$TestFields = array(
          "TestTextField" => MetadataSchema::MDFTYPE_TEXT,
          "TestTimestampField" => MetadataSchema::MDFTYPE_TIMESTAMP,
          "TestParagraphField" => MetadataSchema::MDFTYPE_PARAGRAPH,
          "TestUrlField" => MetadataSchema::MDFTYPE_URL,
          "TestReferenceField" => MetadataSchema::MDFTYPE_REFERENCE,
          "TestUserField" => MetadataSchema::MDFTYPE_USER,
          "TestOptionField" => MetadataSchema::MDFTYPE_OPTION,
          "TestCNameField" => MetadataSchema::MDFTYPE_CONTROLLEDNAME,
          "TestTreeField" => MetadataSchema::MDFTYPE_TREE,
          "TestDateField" => MetadataSchema::MDFTYPE_DATE,
          "TestFlagField" => MetadataSchema::MDFTYPE_FLAG,
          "TestNumberField" => MetadataSchema::MDFTYPE_NUMBER);

        # create the fields
        foreach (self::$TestFields as $FieldName => $FieldType)
        {
            $TmpField = $Schema->GetItemByName($FieldName);
            if ($TmpField === NULL)
            {
                $TmpField = $Schema->AddField($FieldName, $FieldType);
            }
            $TmpField->IsTempItem(FALSE);
            self::$TestFieldIds[$FieldName] = $TmpField->Id();
        }

        # Resource::Create() expects a user to be logged in,
        # so log in an admin user
        $UFactory = new CWUserFactory();
        $Users = $UFactory->GetUsersWithPrivileges(
                PRIV_RESOURCEADMIN, PRIV_COLLECTIONADMIN);
        $UserIds = array_keys($Users);
        $AdminUserId = array_pop($UserIds);
        $AdminUser = new CWUser($AdminUserId);
        $GLOBALS["G_User"]->Login($AdminUser->Name(), "", TRUE);
    }

    /**
    * After to running the tests, this function is
    * run. It deletes all of the test Metadata fields.
    */
    public static function tearDownAfterClass()
    {
        # construct the schema object
        $Schema = new MetadataSchema();
        $Database = new Database();

        # drop all of the test fields
        foreach (self::$TestFieldIds as $FieldName => $FieldId)
        {
            $Schema->DropField($FieldId);

            # remove from OAIFieldMappings too
            $Database->Query("
                DELETE FROM OAIFieldMappings
                WHERE SPTFieldId = " . addslashes($FieldId));
        }
    }

    /**
    * This function exercises the Resource get and set methods for
    * each Metadata types using the fields created in setUpBeforeClass().
    */
    public function testGetandSet()
    {
        # Create test-specific objects
        $TestResource = Resource::Create(MetadataSchema::SCHEMAID_DEFAULT);
        $TestResource->IsTempResource(FALSE);
        $TestReferenceResource = Resource::Create(MetadataSchema::SCHEMAID_DEFAULT);
        $TestReferenceResource->IsTempResource(FALSE);
        $TestClassification = Classification::Create("TestClassification",
                              self::$TestFieldIds['TestTreeField']);
        $TestControlledName = new ControlledName(NULL, "TestControlledName",
                              self::$TestFieldIds['TestCNameField']);
        $TestOptionCName = new ControlledName(NULL, "TestOptionCName",
                           self::$TestFieldIds['TestOptionField']);

        # Test get and set for each field
        foreach (self::$TestFields as $FieldName => $FieldType)
        {
            # whether, before testing equivalence, we need to pop the
            # returned value out of an array
            $BeforeTestArrayShift = FALSE;

            # if we're testing the object return, this is the object we'll compare it to.
            unset($TestObject);

            switch ($FieldType)
            {
                case MetadataSchema::MDFTYPE_TEXT:
                    $TgtVal = "A test title";
                    break;

                case MetadataSchema::MDFTYPE_URL:
                    $TgtVal = "http://testtesttest.com";
                    break;

                case MetadataSchema::MDFTYPE_PARAGRAPH:
                    $TgtVal = "I am a test paragraph.";
                    break;

                case MetadataSchema::MDFTYPE_NUMBER:
                    $TgtVal = "0";
                    break;

                case MetadataSchema::MDFTYPE_FLAG:
                    $TgtVal = "1";
                    break;

                case MetadataSchema::MDFTYPE_DATE:
                    $TgtVal = date("Y-m-d");
                    $TestObject = new Date(strval($TgtVal));
                    $TestObjectType = 'Date';
                    $TestFunctionName = 'BeginDate';
                    $TestFunctionArguments = NULL;
                    break;

                case MetadataSchema::MDFTYPE_TIMESTAMP:
                    $TgtVal = date("Y-m-d H:i:s", strtotime($TgtVal));
                    break;

                case MetadataSchema::MDFTYPE_TREE:
                    $TgtVal = array();
                    $TgtVal[$TestClassification->Id()] = "TestClassification";
                    $TestObject = $TestClassification;
                    $TestObjectType = 'Classification';
                    $TestFunctionName = 'FullName';
                    $TestFunctionArguments = NULL;
                    $BeforeTestArrayShift = TRUE;
                    break;

                case MetadataSchema::MDFTYPE_CONTROLLEDNAME:
                    $TgtVal = array();
                    $TgtVal[$TestControlledName->Id()] = "TestControlledName";
                    $TestObject = $TestControlledName;
                    $TestObjectType = 'ControlledName';
                    $TestFunctionName = 'Name';
                    $TestFunctionArguments = NULL;
                    $BeforeTestArrayShift = TRUE;
                    break;

                case MetadataSchema::MDFTYPE_OPTION:
                    $TgtVal = array();
                    $TgtVal[$TestOptionCName->Id()] = "TestOptionCName";
                    $TestObject = $TestOptionCName;
                    $TestObjectType = 'ControlledName';
                    $TestFunctionName = 'Name';
                    $TestFunctionArguments = NULL;
                    $BeforeTestArrayShift = TRUE;
                    break;

                case MetadataSchema::MDFTYPE_USER:
                    $TestObject = new CWUser(1);
                    $TgtVal = array( 1 => $TestObject->Name() );
                    $TestObjectType = 'CWUser';
                    $TestFunctionName = 'Id';
                    $TestFunctionArguments = NULL;
                    $BeforeTestArrayShift = TRUE;
                    break;

                case MetadataSchema::MDFTYPE_POINT:
                    $TgtVal = array();
                    $TgtVal['X'] = 5;
                    $TgtVal['Y'] = 7;
                    break;

                case MetadataSchema::MDFTYPE_REFERENCE:
                    $TestObject = $TestReferenceResource;
                    $TgtVal = array();
                    $TgtVal[$TestReferenceResource->Id()] = $TestReferenceResource->Id();
                    $TestFunctionName = 'Id';
                    $TestObjectType = 'Resource';
                    $TestFunctionArguments = NULL;
                    $BeforeTestArrayShift = TRUE;
                    break;

                default:
                    throw new Exception("Data type not handled.");
                    break;

            }

            # set the value on the test resource
            $TestResource->Set($FieldName, $TgtVal);

            # assert the default get returns the expected value
            $this->assertEquals($TgtVal, $TestResource->Get($FieldName));

            if (isset($TestObject))
            {
                $ReturnedObject = $TestResource->Get($FieldName, TRUE);

                if ($BeforeTestArrayShift)
                {
                    $ReturnedObject = array_shift($ReturnedObject);
                }

                $array_for_test_object = array($TestObject, $TestFunctionName);
                $array_for_returned_object = array($ReturnedObject, $TestFunctionName);

                $this->assertEquals(call_user_func(
                        $array_for_returned_object, $TestFunctionArguments),
                        call_user_func($array_for_test_object, $TestFunctionArguments));

                $this->assertInstanceOf($TestObjectType, $ReturnedObject);
            }

        }

        # Clean up function-specific objects.
        $TestResource->Delete();
        $TestReferenceResource->Delete();
        $TestClassification->Delete();
        $TestControlledName->Delete();
        $TestOptionCName->Delete();
    }
}
