//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// textreportviewer_t.cpp -- tests textreportviewer
//

#include "stdtw.h"
#include <sys/stat.h>
#include <stdlib.h>

#include "tw/fcoreport.h"
#include "fco/fcospecimpl.h"
#include "fco/fcosetimpl.h"
#include "fs/fsobject.h"
#include "core/serializerimpl.h"
#include "core/archive.h"
#include "test/test.h"
#include "core/errorbucketimpl.h"
#include "tw/textreportviewer.h"
#include "fs/fspropset.h"
#include "fs/fspropcalc.h"
#include "core/fsservices.h"
#include <iostream>
#include <fstream>
#include "fco/fcospechelper.h"
#include "fco/fcospecattr.h"
#include "tw/fcoreportutil.h"
#include "tw/headerinfo.h"

#ifdef FIXED_TRV_TEST_SUITE

void MakeFile( TSTRING& fcoNameMakeMe );
void MakeDir( const TCHAR* const lpszDirName );

// 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;
}

void MakeFile( TSTRING& strNameMakeMe )
{
    try
    {
        iFSServices* pFSServices = iFSServices::GetInstance(); ASSERT( pFSServices );
        pFSServices->MakeTempFilename( strNameMakeMe );

        std::string strA;
        for( TSTRING::iterator i = strNameMakeMe.begin(); i != strNameMakeMe.end(); i++ )
        {
            char ach[4];
            ASSERT( MB_CUR_MAX <= 4 );

            int n = wctomb( ach, *i );
            ASSERT( n != -1 );
            
            for( int j = 0; j < n; j++ )
                strA += ach[j];
        }

		TOFSTREAM file( strA.c_str() );
        ASSERT( file );
        file.close();
    }
    catch( eFSServices e )
    {
        ASSERT( false );
    }
    catch( ... )
    {
        ASSERT( false );
    }
}

void MakeDir( const TCHAR* const lpszDirName )
{
    try
    {
	    iFSServices* pFSServices = iFSServices::GetInstance();
	    TSTRING newdir(lpszDirName);

        pFSServices->Mkdir( newdir );
    }
    catch( eFSServices e )
    {
        ASSERT( false );
    }
}

#endif //FIXED_TRV_TEST_SUITE

