<?PHP
class Folder_Test extends PHPUnit_Framework_TestCase
{
    const TYPE_NAME = "XX-Test-XX";

    /**
    * Delete XX-Test-XX folder conte type that was created for
    * testing.
    */
    static function tearDownAfterClass()
    {
        $DB = new Database();
        $DB->Query("DELETE FROM FolderContentTypes WHERE "
                   ."TypeName='".self::TYPE_NAME."'");
    }



    /**
    * Verify that items can be appened and appear in the corect order.
    * covers AppendItem()
    */
    function testAppendItem()
    {
        $Factory = new FolderFactory();
        $TestFolder = $Factory->CreateFolder("Resource");

        $TestFolder->AppendItem(1);
        $this->CheckContents( $TestFolder, array(1) );

        $TestFolder->AppendItem(2);
        $this->CheckContents( $TestFolder, array(1,2) );

        $TestFolder->Delete();
    }

    /**
    * Verify that items can be prepended and appear in the correct order.
    * covers PrependItem()
    */
    function testPrependItem()
    {
        $Factory = new FolderFactory();
        $TestFolder = $Factory->CreateFolder("Resource");

        $TestFolder->PrependItem(1);
        $this->CheckContents( $TestFolder, array(1) );

        $TestFolder->PrependItem(2);
        $this->CheckContents( $TestFolder, array(2,1) );

        $TestFolder->Delete();
    }

    /**
    * Verify that InsertItemBefore() works correctly on empty lists,
    * when the tgt is nonexistent, and at the beginning, middle, and
    * end of a list.
    * covers InsertItemBefore()
    */
    function testInsertItemBefore()
    {
        $Factory = new FolderFactory();
        $TestFolder = $Factory->CreateFolder("Resource");

        # insert to empty list
        $TestFolder->InsertItemBefore(100, 1);
        $this->CheckContents($TestFolder, array(1) );

        # insert before non-existent item
        $TestFolder->InsertItemBefore(100, 2);
        $this->CheckContents($TestFolder, array(2,1) );

        # insert before beginning
        $TestFolder->InsertItemBefore(2, 3);
        $this->CheckContents($TestFolder, array(3,2,1) );

        # insert before end
        $TestFolder->InsertItemBefore(1, 4);
        $this->CheckContents($TestFolder, array(3,2,4,1) );

        # insert in the middle
        $TestFolder->InsertItemBefore(4, 5);
        $this->CheckContents($TestFolder, array(3,2,5,4,1) );

        $TestFolder->Delete();
    }

    /**
    * Verify that InsertItemAfter() works correctly on empty lists,
    * when the tgt is nonexistent, and at the beginning, middle, and
    * end of a list.
    * covers InsertItemAfter()
    */
    function testInsertItemAfter()
    {
        $Factory = new FolderFactory();
        $TestFolder = $Factory->CreateFolder("Resource");

        # insert to empty list
        $TestFolder->InsertItemAfter(100, 1);
        $this->CheckContents( $TestFolder, array(1) );

        # insert before non-existent item
        $TestFolder->InsertItemAfter(100, 2);
        $this->CheckContents( $TestFolder, array(1,2) );

        # insert after beginning
        $TestFolder->InsertItemAfter(1, 3);
        $this->CheckContents( $TestFolder, array(1,3,2) );

        # insert after end
        $TestFolder->InsertItemAfter(2, 4);
        $this->CheckContents( $TestFolder, array(1,3,2,4) );

        # insert in the middle
        $TestFolder->InsertItemAfter(3, 5);
        $this->CheckContents( $TestFolder, array(1,3,5,2,4) );

        $TestFolder->Delete();
    }

    /**
    * Test creation of a mixed content folder with AppendItems()
    * inserting some data into it.  Note that with mixed content
    * folders, we're only exercising the code paths in Folder that
    * aren't otherwise used.  This presumes that the unit tests for
    * PersistentDoublyLinkedList will find any issues with typed
    * lists.
    * covers FolderFactory::CreateMixedFolder(), AppendItems()
    */
    function testMixedContent()
    {
        $Factory = new FolderFactory();
        $TestFolder = $Factory->CreateMixedFolder();

        $TestFolder->AppendItems( array(1,2,3), self::TYPE_NAME);
        $this->CheckContentsMixed($TestFolder, array(1,2,3) );

        $TestFolder->Delete();
    }


    /**
    * Test all remaining misc functions.
    * covsers AppendItems(), GetItemIds() when Offset and Limit are
    * specified, Remove Item(), Id(), Name(), NormalizedName(),
    * OwnerId(), Note(), IsShared(), NoteForItem(), and untested paths
    * in the constructor.
    */
    function testMiscRemaining()
    {
        $Factory = new FolderFactory();
        $TestFolder = $Factory->CreateFolder("Resource");

        # preload the folder wtih 1:5
        $TestFolder->AppendItems( array(1,2,3,4,5) );
        $this->CheckContents( $TestFolder, array(1,2,3,4,5) );

        # test the offset and limit of GetItemIds()
        $this->assertSame(
            array(3,4,5), $TestFolder->GetItemIds(2) );
        $this->assertSame(
            array(3,4), $TestFolder->GetItemIds(2,2) );

        # test removing items from the beginning, end, and middle of the folder
        $TestFolder->RemoveItem(1);
        $this->CheckContents( $TestFolder, array(2,3,4,5) );

        $TestFolder->RemoveItem(5);
        $this->CheckContents( $TestFolder, array(2,3,4) );

        $TestFolder->RemoveItem(3);
        $this->CheckContents( $TestFolder, array(2,4) );

        # make sure that the FolderId is an int
        $this->assertTrue(
            is_int($TestFolder->Id()) );

        # test the setter/getter methods for this folder
        $TestFolder->Name("My Test Folder");
        $this->assertSame(
            "My Test Folder", $TestFolder->Name() );

        # check the autogenerated normalized name
        $this->assertSame(
            "mytestfolder", $TestFolder->Normalizedname());

        $TestFolder->NormalizedName("mynewname");
        $this->assertSame(
            "mynewname", $TestFolder->NormalizedName());

        # blank the normalized name so it will reset
        $TestFolder->NormalizedName('');
        $this->assertSame(
            "mytestfolder", $TestFolder->NormalizedName() );

        $TestFolder->OwnerId(5);
        $this->assertSame(
            5, $TestFolder->OwnerId() );

        $TestFolder->Note("Test folder note");
        $this->assertSame(
            "Test folder note", $TestFolder->Note() );

        $TestFolder->IsShared(1);
        $this->assertSame(
            1, $TestFolder->IsShared() );

        # and test folder item notes
        $TestFolder->NoteForItem( 4, "Test Item Note");
        $this->assertSame(
            "Test Item Note", $TestFolder->NoteForItem( 4 ) );

        # test getting an existing folder
        $TestFolderCopy = new Folder( $TestFolder->Id() );

        # verify that TestFolder and TestFolderCopy refer to the same FolderId
        $this->assertSame(
            $TestFolder->Id(),
            $TestFolderCopy->Id() );

        # test getting a nonexistent folder
        try {
            $TestFolderCopy = new Folder(PHP_INT_MAX);
            $this->assertTrue(FALSE);
        }
        catch (Exception $e)
        {
            ; // empty on purpose
        }

        $TestFolder->Delete();
    }

    /**
    * Check the contents of a regular folder (not MIXEDCONTENT) to
    * ensure that it has the correct number of items in the correct
    * order.
    * @param Folder $TestFolder Folder to check.
    * @param array $TgtItemIds Items that should appear in the folder.
    */
    private function CheckContents($TestFolder, $TgtItemIds)
    {
        $this->assertSame(
            count($TgtItemIds), $TestFolder->GetItemCount() );
        $this->assertSame(
            $TgtItemIds, $TestFolder->GetItemIds() );
    }

    /**
    * Check the contents of a MIXEDCONTENT folder to
    * ensure that it has the correct number of items in the correct
    * order.
    * @param Folder $TestFolder Folder to check.
    * @param array $TgtItemIds Items that should appear in the folder.
    */
    private function CheckContentsMixed($TestFolder, $TgtItemIds)
    {
        $this->assertSame(
            count($TgtItemIds), $TestFolder->GetItemCount() );

        $TgtVal = array();
        foreach ($TgtItemIds as $ItemId)
        {
            $TgtVal []= array( "ID"=>$ItemId, "Type"=> self::TYPE_NAME);
        }

        $this->assertSame(
            $TgtVal, $TestFolder->GetItemIds() );
    }
}
