void cFCOReportUtil::FinalizeReport(cFCOReport& rr)
{
    cFCOReportGenreIter genreIter(rr);
    for (genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next())
    {
        InitPropDisplayer(genreIter);
    }
}
// we use this instead of TraceContents() so we can test the report iterators.
static void TraceReport(const cFCOReport& r, cDebug& d)
{
    d.TraceDebug("Global Error Queue:\n");
    r.GetErrorQueue()->TraceContents();

    cFCOReportGenreIter genreIter(r);
    int genreCount = 0;

    for (genreIter.SeekBegin(); !genreIter.Done(); genreIter.Next())
    {
        d.TraceDebug("> Genre [%d]:\n", genreCount);

        cFCOReportSpecIter specIter(genreIter);
        int ct = 0;

        for(specIter.SeekBegin(); ! specIter.Done(); specIter.Next(), ct++)
        {
            d.TraceDebug(">>> Spec [%d]:\n", ct);
            ASSERT(specIter.GetSpec());
            specIter.GetSpec()->TraceContents();
            specIter.GetErrorQueue()->TraceContents();

            d.TraceDebug(">>> Added Files:\n");
            specIter.GetAddedSet()->TraceContents();
            d.TraceDebug(">>> Removed Files:\n");
            specIter.GetRemovedSet()->TraceContents ();

            // trace out changed files
            cFCOReportChangeIter changeIter(specIter);
            int changeCtr = 0;
            for(changeIter.SeekBegin(); ! changeIter.Done(); changeIter.Next(), changeCtr++)
            {
                d.TraceDebug(">>>>> Changed fco [%d]\n", changeCtr);
                d.TraceDebug(">>>>>   Old FCO:\n");
                changeIter.GetOld()->TraceContents();
                d.TraceDebug(">>>>>   New FCO:\n");
                changeIter.GetNew()->TraceContents();
                changeIter.GetChangeVector().TraceContents();
            }
        }
    }
}
void TestTextReportViewer()
{
    cFCOReport  report;
    cFCOReportGenreIter genreIter(report);
    cFCOReportSpecIter specIter(genreIter);

    cDebug d("TestFCOReport");
    
    cFCOName fcoNameSpec1;
    cFCOName fcoNameSpec2;
    TSTRING  fcoNameTempFile;
    try
    {
        iFSServices* pFSServices = iFSServices::GetInstance();
        ASSERT( pFSServices );

        TSTRING fcoNameTempDir;
        pFSServices->GetTempDirName( fcoNameTempDir );
        
        fcoNameSpec1 = fcoNameTempDir += _T("SPEC1/");
        fcoNameSpec2 = fcoNameTempDir += _T("SPEC2/");
        
        pFSServices->Mkdir( fcoNameTempDir );
        pFSServices->Mkdir( fcoNameSpec1.AsString() );
        pFSServices->Mkdir( fcoNameSpec2.AsString() );
        
        fcoNameTempFile = fcoNameTempDir += _T("twtempXXXXXX");
        pFSServices->MakeTempFilename( fcoNameTempFile );
    }
    catch(eFSServices& /* e */)
    {
        // TODO: properly handle error
        ASSERT( false );
    }

    // need two prop calcs because.....
    // if cFSPropCalc::VisitFSObject succeeds, cFSPropCalc stores the FCO in
    // an internal set (why, I don't know), and the cFCOSet ASSERTs that the same 
    // FCO isn't inserted more than once.  But since we visit changed FCOs twice 
    // in this test routine, we need two calcs.  Make sense?  Oh, well.
    cFSPropCalc* pPropCalc = new cFSPropCalc;
    cFSPropCalc* pPropCalc2 = new cFSPropCalc;
 
    
    cFCOSpecStopPointSet *pStopPts = new cFCOSpecStopPointSet;
    cFCOSpecImpl*    pSpec          = new cFCOSpecImpl( fcoNameSpec1.AsString(), NULL, pStopPts);
    cFCOSpecAttr* pAttr         = new cFCOSpecAttr;
    
    
    cFCOPropVector v;    
    for( int i = 0; i < 32; i++ )
        v.AddItem( i );
    
    pPropCalc->SetPropVector(v);
    pPropCalc2->SetPropVector(v);


    TSTRING fcoNameMakeMe;
    fcoNameMakeMe = fcoNameSpec1.AsString() + _T("/added_fileXXXXXX");
    MakeFile( fcoNameMakeMe );
    cFSObject*  addedFCO        = new cFSObject( cFCOName(fcoNameMakeMe));
    pPropCalc->VisitFSObject( *addedFCO );

    
    // MakeTempFile can't handle strings with escaped quotes, so we'll have to do this ourselves
    fcoNameMakeMe = fcoNameSpec1.AsString() + _T("/\"quoted\\_and_backslashed_file1\"");    
    //TOFSTREAM file1( fcoNameMakeMe.c_str() );
    //ASSERT( file1 );
    //file1.close();

    cFSObject*  addedFCO2       = new cFSObject( cFCOName(fcoNameMakeMe) );
    //pPropCalc->VisitFSObject( *addedFCO2 );
    
    
    // MakeTempFile can't handle strings with escaped quotes, so we'll have to do this ourselves
    fcoNameMakeMe = fcoNameSpec1.AsString() + _T("/quoted_file\"2\"XXXXXX");
    //TOFSTREAM file2( fcoNameMakeMe.c_str() );
    //ASSERT( file2 );
    //file2.close();

    cFSObject*  addedFCO3       = new cFSObject( cFCOName(fcoNameMakeMe) );
    //pPropCalc->VisitFSObject( *addedFCO3 );

    
    fcoNameMakeMe = fcoNameSpec1.AsString() + _T("/removed_fileXXXXXX");
    MakeFile( fcoNameMakeMe );
    cFSObject*  removedFCO      = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc->VisitFSObject( *removedFCO );

    
    fcoNameMakeMe = fcoNameSpec1.AsString() + _T("/removed_fileXXXXXX");
    MakeFile( fcoNameMakeMe );
    cFSObject*  removedFCO2     = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc->VisitFSObject( *removedFCO2 );

    pSpec->SetStartPoint( fcoNameSpec1 );
    pAttr->SetName( fcoNameSpec1.AsString() );
    pAttr->SetSeverity(53);
    pStopPts->Add( cFCOName( fcoNameSpec1.AsString() + _T("/End1")) );
    report.AddSpec(0x00020001, pSpec, pAttr, &specIter); // TODO:bam - use cFS::Genre
    pAttr->Release();    

    
    specIter.GetAddedSet()->Insert(addedFCO);
    specIter.GetAddedSet()->Insert(addedFCO2);
    specIter.GetAddedSet()->Insert(addedFCO3);
    specIter.GetRemovedSet()->Insert(removedFCO);
    specIter.GetRemovedSet()->Insert(removedFCO2);

    // make changed FCO1
    cFCOPropVector changedPropVector1;

    
    fcoNameMakeMe = fcoNameSpec1.AsString() + _T("/changed_fileXXXXXX");
    MakeFile( fcoNameMakeMe );
    cFSObject*  oldChangedFCO   = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc->VisitFSObject( *oldChangedFCO );
    (static_cast<cFSPropSet*> (oldChangedFCO->GetPropSet()))->SetSize(123);
    //(static_cast<cFSPropSet*> (oldChangedFCO->GetPropSet()))->SetUID(_T("old"));

    cFSObject*  newChangedFCO   = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc2->VisitFSObject( *newChangedFCO );
    (static_cast<cFSPropSet*> (newChangedFCO->GetPropSet()))->SetSize(666);
    //(static_cast<cFSPropSet*> (newChangedFCO->GetPropSet()))->SetUID(_T("new"));

    changedPropVector1.AddItem(cFSPropSet::PROP_SIZE);
    changedPropVector1.AddItem(cFSPropSet::PROP_UID);
    report.AddChangedFCO(specIter, oldChangedFCO, newChangedFCO, changedPropVector1);

    // make changed FCO2
    cFCOPropVector changedPropVector2;

    fcoNameMakeMe = fcoNameSpec1.AsString() + _T("/changed_fileXXXXXX");
    MakeFile( fcoNameMakeMe );
    cFSObject*  oldChangedFCO2  = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc->VisitFSObject( *oldChangedFCO2 );
    
    //(static_cast<cFSPropSet*> (oldChangedFCO2->GetPropSet()))->SetGSID( _T("S-1-1-0") );

    cFSObject*  newChangedFCO2  = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc2->VisitFSObject( *newChangedFCO2 );
    //(static_cast<cFSPropSet*> (newChangedFCO2->GetPropSet()))->SetGSID( _T("S-1-1-1") );

    //changedPropVector2.AddItem(cFSPropSet::PROP_GSID);
    report.AddChangedFCO(specIter, oldChangedFCO2, newChangedFCO2, changedPropVector2);

    // add some errors
//    report.GetErrorQueue()->AddError(eError(_T("this is a general error")));
 //   report.GetErrorQueue()->AddError(eError(_T("this too is a general error")));
        

    cFCOSpecStopPointSet *pStopPts2     = new cFCOSpecStopPointSet;
    cFCOSpecImpl*       pSpec2              = new cFCOSpecImpl( fcoNameSpec2.AsString(), NULL, pStopPts2);
    cFCOSpecAttr*   pAttr2              = new cFCOSpecAttr;

    fcoNameMakeMe = fcoNameSpec2.AsString() + _T("/added_fileXXXXXX");
    MakeFile( fcoNameMakeMe );
    cFSObject*  addedFCO5       = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc->VisitFSObject( *addedFCO5 );
    
    fcoNameMakeMe = fcoNameSpec2.AsString() + _T("/removed_fileXXXXXX");
    MakeFile( fcoNameMakeMe );
    cFSObject*  removedFCO5     = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc->VisitFSObject( *removedFCO5 );

    pSpec2->SetStartPoint( fcoNameSpec2 );    
    pAttr2->SetName( fcoNameSpec2.AsString() );
    pAttr2->SetSeverity(64);
    pStopPts2->Add( cFCOName( fcoNameSpec2.AsString() + _T("/End2") ) );
    report.AddSpec(0x00020001, pSpec2, pAttr2, &specIter); // TODO:bam -- use cFS::Genre
    pAttr2->Release();
    
    specIter.GetAddedSet()->Insert(addedFCO5);
    specIter.GetRemovedSet()->Insert(removedFCO5);

        

    // make changed FCO3
    cFCOPropVector changedPropVector3;
    
    fcoNameMakeMe = fcoNameSpec2.AsString() + _T("/changed_fileXXXXXX");
    MakeFile( fcoNameMakeMe );
    cFSObject*  oldChangedFCO3  = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc->VisitFSObject( *oldChangedFCO3 );
    (static_cast<cFSPropSet*> (oldChangedFCO3->GetPropSet()))->SetBlockSize(313222);
    
    cFSObject*  newChangedFCO3  = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc2->VisitFSObject( *newChangedFCO3 );
    (static_cast<cFSPropSet*> (newChangedFCO3->GetPropSet()))->SetBlockSize(22213145);

    changedPropVector3.AddItem(cFSPropSet::PROP_BLOCK_SIZE);
    report.AddChangedFCO(specIter, oldChangedFCO3, newChangedFCO3, changedPropVector3);

    // make changed FCO4
    cFCOPropVector changedPropVector4;
    
    fcoNameMakeMe = fcoNameSpec2.AsString() + _T("/changed_fileXXXXXX");
    MakeFile( fcoNameMakeMe );
    cFSObject*  oldChangedFCO4  = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc->VisitFSObject( *oldChangedFCO4 );
    (static_cast<cFSPropSet*> (oldChangedFCO4->GetPropSet()))->SetSize(9104498);
    (static_cast<cFSPropSet*> (oldChangedFCO4->GetPropSet()))->SetMode( S_IREAD | S_IWRITE );    
    
    cFSObject*  newChangedFCO4  = new cFSObject( cFCOName(fcoNameMakeMe) );
    pPropCalc2->VisitFSObject( *newChangedFCO4 );
    (static_cast<cFSPropSet*> (newChangedFCO4->GetPropSet()))->SetSize(66);
    (static_cast<cFSPropSet*> (newChangedFCO4->GetPropSet()))->SetMode( S_IREAD | S_IWRITE | S_IEXEC );

    changedPropVector4.AddItem(cFSPropSet::PROP_SIZE);
    changedPropVector4.AddItem(cFSPropSet::PROP_MODE);
    report.AddChangedFCO(specIter, oldChangedFCO4, newChangedFCO4, changedPropVector4);

    specIter.SeekBegin();
    specIter.Next();
    //specIter.GetErrorQueue()->AddError(2, "this is an \"/etc2\" spec error",NULL);


    d.TraceDebug(_T("\n======================================================\nStart PrintTextReport...\n======================================================\n\n\n"));
        
    TSTRING tstrEmpty( _T("") );
    cFCOReportHeader rhi;
    cFCOReportUtil::FinalizeReport( report );
    cTextReportViewer trv;
    trv.DisplayReportAndHaveUserUpdateIt( rhi, report, _T("") );

            // test writing of USID
            cFileArchive outFile;
            outFile.OpenReadWrite(_T("tmp.twr"));
            cSerializerImpl outSer(outFile, cSerializerImpl::S_WRITE);

            //TraceReport(report, d);
            outSer.Init();
            outSer.WriteObject(&report);
            outSer.Finit();

            outFile.Close();

            cFileArchive inFile;
            inFile.OpenRead(_T("tmp.twr"));
            cSerializerImpl inSer(inFile, cSerializerImpl::S_READ);

            cFCOReport inReport;

            inSer.Init();
            inSer.ReadObject(&inReport);
            inSer.Finit();

            d.TraceDebug("Read in serialized report:\n");
            //TraceReport(inReport, d);
            trv.PrintTextReport( rhi, inReport, TSTRING( TEMP_DIR _T( "/test2.txt" ) ) );    

            //TODO: this does not work any more
            //trv.LaunchEditorOnFile( TSTRING( TEMP_DIR _T("/test2.txt") ), _T("") );


    // look at results
    trv.PrintTextReport( rhi, report, fcoNameTempFile );    
    //TODO: this does not work any more
    //cTextReportViewer::LaunchEditorOnFile( fcoNameTempFile, _T("") );

    
    iFSServices* pFSServices = iFSServices::GetInstance();
    ASSERT( pFSServices );
    pFSServices->FileDelete( addedFCO->GetName().AsString() );
    pFSServices->FileDelete( addedFCO2->GetName().AsString() );
    pFSServices->FileDelete( addedFCO3->GetName().AsString() );
    pFSServices->FileDelete( addedFCO5->GetName().AsString() );
    pFSServices->FileDelete( removedFCO->GetName().AsString() );
    pFSServices->FileDelete( removedFCO2->GetName().AsString() );
    pFSServices->FileDelete( removedFCO5->GetName().AsString() );
    pFSServices->FileDelete( newChangedFCO->GetName().AsString() );
    pFSServices->FileDelete( newChangedFCO2->GetName().AsString() );
    pFSServices->FileDelete( newChangedFCO3->GetName().AsString() );
    pFSServices->FileDelete( newChangedFCO4->GetName().AsString() );
    pFSServices->FileDelete( fcoNameTempFile );

    // don't remove TEMP_DIR since other people may be using it
    pFSServices->Rmdir( fcoNameSpec1.AsString() );
    pFSServices->Rmdir( fcoNameSpec2.AsString() );

    pSpec->Release();
    pSpec2->Release();
    addedFCO->Release();
    addedFCO2->Release();
    addedFCO3->Release();
    addedFCO5->Release();
    removedFCO->Release();
    removedFCO2->Release();
    removedFCO5->Release();
    delete pPropCalc;
    delete pPropCalc2;
    oldChangedFCO->Release();
    newChangedFCO->Release();
    oldChangedFCO2->Release();
    newChangedFCO2->Release();
    oldChangedFCO3->Release();
    newChangedFCO3->Release();
    oldChangedFCO4->Release();
    newChangedFCO4->Release();

    return;
}