/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     2001/03/28
// RCS-ID:      $Id: indexing_svc.cpp,v 1.9 2001/04/23 19:07:35 georgetasker Exp $
// Copyright:   (c) Aleksandras Gluchovas (alex@soften.ktu.lt)
// Licence:   	GPL (version 2.0 or later)
/////////////////////////////////////////////////////////////////////////////

#ifdef __GNUG__
    #pragma implementation "indexing_svc.cpp"
    #pragma interface "indexing_svc.h"
#endif

// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#include "indexing_svc.h"

// include source generated by DiamondBase's dsc.exe
#include "dbsrc/Idxrel.h"

#include "wx/tokenzr.h"
#include "wx/xml/xmlres.h"

/***** Implementation for class wxPMIndexingService *****/

static wxCommandInfo _gIndexCommands[] = {

	wxCommandInfo( wxPMIndexingService::ID_INDECIES,             "&Indecies...", "", "" ),
	wxCommandInfo( wxPMIndexingService::ID_REBUILD_ALL_INDECIES, "&Rebuild all Indecies", "", "" ),
	wxCommandInfo( wxPMIndexingService::ID_CREATE_TEST_DATA,     "&Create Test Data", "", "" ),

	wxCommandInfo( 0, "" ) // list-terminator
};

static wxPMMergableMenuChain _gIndexChains[] = {

	wxPMMergableMenuChain( "/&Tools/", _gIndexCommands ),

	wxPMMergableMenuChain( "", NULL ) // list-terminator
};

IMPLEMENT_DYNAMIC_CLASS( wxPMIndexingService, wxPMSimpleService )

BEGIN_EVENT_TABLE( wxPMIndexingService, wxPMSimpleService )

	EVT_MENU( ID_INDECIES,				wxPMIndexingService::OnIndecies ) 
	EVT_MENU( ID_REBUILD_ALL_INDECIES,  wxPMIndexingService::OnRebuildAllIndecies ) 
	EVT_MENU( ID_CREATE_TEST_DATA,		wxPMIndexingService::OnCreateTestData ) 

END_EVENT_TABLE()

wxPMIndexingService::wxPMIndexingService()
{}

wxPMIndexingService::~wxPMIndexingService()
{
	CleanUp();
}

bool wxPMIndexingService::Start()
{
	int ok = theDiamondBase.loadConfig("dbconfig.txt"); // where to find the db config file(s).

	wxASSERT_MSG( ok == 0, "FATAL ERROR: failed loading 'dbconfig.txt' !" );

	mpIndexTbl = new IndexTable();
	mpFileTbl = new FileTable();
	mpItemTbl = new ItemTable();

	mpIndexTbl->Setup( new Index(), this );
	mpFileTbl->Setup( new File(), this );
	mpItemTbl->Setup( new Item(), this );

	wxPMBootSvc().RegisterUniqueServiceInstance( this, "wxPMIndexingService" );

	return TRUE;
}

bool wxPMIndexingService::Stop()
{
	CleanUp();

	wxPMBootSvc().UnregisterUniqueServiceInstance( this, "wxPMIndexingService" );

	return TRUE;
}

wxPMMergableMenuChain* wxPMIndexingService::GetMergableMenuChain()
{
	return _gIndexChains;
}

void* wxPMIndexingService::GetInterface( const char* infName, const char* version )
{
	return NULL;
}

// command handlers

void wxPMIndexingService::OnIndecies( wxCommandEvent& event )
{
	IndeciesDlg* dlg = wxDynamicCast( 
			wxTheXmlResource->LoadWindow( "IndexingSvc_IndeciesDlg", FALSE ),
			IndeciesDlg );

	dlg->Setup( this );
	dlg->ShowModal();
	dlg->Destroy();

	wxPMBootSvc().GetMainWindow()->SetFocus();
}

void wxPMIndexingService::OnRebuildAllIndecies( wxCommandEvent& event )
{
	mFilesProcessed = 0;
	int idxCnt = 0;

	mpIndexTbl->Begin(); 
	mpIndexTbl->First();
	
	while( mpIndexTbl->Ok() )
	{
		wxString s = "Rebuilding index " + wxString( mpIndexTbl->GetName() ) + " ...";
		wxPMBootSvc().SetStatusText( s );

		if ( !RebuildIndex() )
		{
			wxPMBootSvc().SetStatusText( "Failed building index " + 
										 wxString( mpIndexTbl->GetName() ) );
			FlushAll();
			mpIndexTbl->End();
			return;
		}

		++idxCnt;
		mpIndexTbl->Next();
	}

	mpIndexTbl->End();

	FlushAll();

	wxString s; 
	s.Printf( "Finished rebuilding all indecies! (%d file(s), %d index(es) processed, )", 
			  mFilesProcessed, idxCnt );
	wxPMBootSvc().SetStatusText( s );
}

void wxPMIndexingService::OnCreateTestData( wxCommandEvent& event )
{
	CreateTestData();
}

// other methods

bool wxPMIndexingService::RegisterIndexHandler( I_IndexHandler* handler )
{
	if ( mHandlers.Index( handler ) != wxNOT_FOUND )
		return FALSE;

	mHandlers.Add( handler );

	wxASSERT(mHandlers.Index( handler ) != wxNOT_FOUND); // DBG::Make sure it is done!
	return TRUE;
}

bool wxPMIndexingService::UnregisterIndexHandler( I_IndexHandler* handler )
{
	if ( mHandlers.Index( handler ) == wxNOT_FOUND )
		return FALSE;

	mHandlers.Remove( handler );

	wxASSERT(mHandlers.Index( handler ) == wxNOT_FOUND); // DBG::Make sure it is done!
	return TRUE;
}

IndexHandlerListT& wxPMIndexingService::GetIndexHandlers()
{
	return mHandlers;
}

bool wxPMIndexingService::RebuildIndex()
{
	int indexId = mpIndexTbl->GetId();

	size_t i = 0;
	for( ; i != mHandlers.Count(); ++i )
	{
		if ( strcmp( mHandlers[i]->GetIndexHandlerName(), mpIndexTbl->GetHandlerName() ) == 0 )
			break;
	}

	if ( i == mHandlers.Count() )
		return FALSE; // handler not found

	I_IndexHandler* handler = mHandlers[i];

	wxString savedCwd = ::wxGetCwd();
	wxString rootPath = mpIndexTbl->GetContentRoot();

	if ( !::wxSetWorkingDirectory( rootPath ) )
		return FALSE;

	mpFileTbl->DeleteItemsByIndex( indexId );

	handler->OnClearIndexItems( mpIndexTbl->GetId() );
	handler->OnBeginBuild( mpIndexTbl->GetId() );
	
	wxStringTokenizer tok( mpIndexTbl->GetPaths(), ";" );

	while( tok.HasMoreTokens() )
	{
		wxString path = tok.GetNextToken();
		
		if ( ::wxIsWild( path ) )
		{
			wxString po = wxPathOnly( path );
			wxString wc = path.Mid( po.Length() );
			BuildIndexRecursively( wxPMConcatPaths( rootPath, po ), handler, wc );
		}
		else
			BuildIndexForFile( wxPMConcatPaths( rootPath, path ), handler );
	}

	::wxSetWorkingDirectory( savedCwd );

	handler->OnEndBuild( mpIndexTbl->GetId() );

	return TRUE;
}

void wxPMIndexingService::TestIt()
{
	Index entry;

	entry.begin( Index::dbIdx_by_name );

	for( entry.first(); entry.ok(); entry.next() )
	{
	}

	entry.end();
}

void wxPMIndexingService::CreateTestData()
{
	//DropAllData();

	/*
	mpIndexTbl->SetName( "First" );
	mpIndexTbl->SetLanguage( "C++" );
	mpIndexTbl->SetHandlerName( "wxWindows-Html-Help-Handler" );
	mpIndexTbl->SetBoundProject( "" );
	mpIndexTbl->SetContentRoot( "D:\\wx\\docs\\html\\" );
	mpIndexTbl->SetPaths( "*.htm;*.html" );
	mpIndexTbl->Add();
	*/

#if 1
	mpIndexTbl->SetName( "Sample" );
	mpIndexTbl->SetLanguage( "C++" );
	mpIndexTbl->SetHandlerName( "C++-Class-Info-Handler" );
	mpIndexTbl->SetBoundProject( "" );
	mpIndexTbl->SetContentRoot( "D:\\sample\\" );
	mpIndexTbl->SetPaths( "*.h;*.cpp" );
#endif

	mpIndexTbl->Add();
}

void wxPMIndexingService::DropAllData()
{
	mpIndexTbl->RemoveAll();
	mpFileTbl->RemoveAll();
	mpItemTbl->RemoveAll();
}

IndexTable& wxPMIndexingService::GetIndexTable()
{
	return *mpIndexTbl;
}

FileTable& wxPMIndexingService::GetFileTable()
{
	return *mpFileTbl;
}

ItemTable& wxPMIndexingService::GetItemTable()
{
	return *mpItemTbl;
}

void wxPMIndexingService::FlushAll()
{
	mpIndexTbl->Flush();
	mpFileTbl->Flush();
	mpItemTbl->Flush();
}

/*** protected methods ***/

void wxPMIndexingService::BuildIndexForFile( const wxString& file, I_IndexHandler* handler )
{
	wxPMBootSvc().SetStatusText( "Indexing file " + file + " ..." );
	::wxYield();
	++mFilesProcessed;

	mpFileTbl->SetName( file );
	mpFileTbl->SetIndexId( mpIndexTbl->GetId() );
	mpFileTbl->SetLastIndexingDate( wxDateTime::Now() );
	mpFileTbl->Add();

	handler->OnIndexFile( wxPMFileInfo( file ), 
						  mpFileTbl->GetId(), mpIndexTbl->GetId() );
}

void wxPMIndexingService::GetDirEntries( wxStringList& toLst, const wxString& mask, int flags )
{
	wxString path = ::wxFindFirstFile( mask, flags );

	toLst.Clear();

	while( path.Length() )
	{
		toLst.Add( path );
		path = ::wxFindNextFile();
	}
}

void wxPMIndexingService::BuildIndexRecursively( const wxString& dir, I_IndexHandler* handler, 
												 const wxString& fileMask )
{
	if ( !::wxSetWorkingDirectory( dir ) )
		return;

	wxStringList lst; 
	GetDirEntries( lst, "*", wxDIR );
	
	size_t i = 0;
	for( ; i != lst.GetCount(); ++i )
	{
		wxString path = lst[i];

		if ( path != "." && path != ".." )

			BuildIndexRecursively( wxPMConcatPaths( dir, path ), handler, fileMask );
	}

	// index files matching the mask in current dir

	::wxSetWorkingDirectory( dir );

	GetDirEntries( lst, fileMask, wxFILE );

	for( i = 0; i != lst.GetCount(); ++i )
	
		BuildIndexForFile( wxPMConcatPaths( dir, lst[i] ), handler );
}

void wxPMIndexingService::CleanUp()
{
	if ( mpIndexTbl )
		delete mpIndexTbl;
	if ( mpFileTbl )
		delete mpFileTbl;
	if ( mpItemTbl )
		delete mpItemTbl;

	mpIndexTbl = NULL;
	mpFileTbl  = NULL;
	mpItemTbl  = NULL;
}

/***** Implementation for class IndeciesDlg *****/

IMPLEMENT_DYNAMIC_CLASS( IndeciesDlg, wxDialog )

BEGIN_EVENT_TABLE( IndeciesDlg, wxDialog )

	EVT_BUTTON( XMLID("new"),			IndeciesDlg::OnNew )
	EVT_BUTTON( XMLID("edit"),			IndeciesDlg::OnEdit )
	EVT_BUTTON( XMLID("rebuild"),		IndeciesDlg::OnRebuild )
	EVT_BUTTON( XMLID("rebuildAll"),	IndeciesDlg::OnRebuildAll )
	EVT_BUTTON( XMLID("remove"),		IndeciesDlg::OnRemove )

	EVT_BUTTON( XMLID("addSingle"),		IndeciesDlg::OnAddSingle )
	EVT_BUTTON( XMLID("addAll"),		IndeciesDlg::OnAddAll )
	EVT_BUTTON( XMLID("removeSingle"),	IndeciesDlg::OnRemoveSingle )
	EVT_BUTTON( XMLID("removeAll"),		IndeciesDlg::OnRemoveAll )

//	EVT_INIT_DIALOG( IndeciesDlg::OnInitDialog )

END_EVENT_TABLE()

IndeciesDlg::IndeciesDlg()
{
}

void IndeciesDlg::Setup( wxPMIndexingService* svc )
{
	mpIndexSvc = svc;
}

void IndeciesDlg::OnNew( wxCommandEvent& event )
{
	IndexPropsDlg* dlg = wxDynamicCast( 
			wxTheXmlResource->LoadWindow( "IndexingSvc_IndexPropsDlg", FALSE ),
			IndexPropsDlg );

	dlg->Setup( mpIndexSvc );

	IndexTable& it = mpIndexSvc->GetIndexTable();

	it.SetName( "unnamed" );
	it.SetHandlerName( "" );
	it.SetContentRoot( "" );
	it.SetPaths( "" );

	if ( dlg->ShowModal() == wxID_OK )
	{
		// TBD::
	}

	dlg->Destroy();
}

void IndeciesDlg::OnEdit( wxCommandEvent& event )
{
}

void IndeciesDlg::OnRebuild( wxCommandEvent& event )
{
}

void IndeciesDlg::OnRebuildAll( wxCommandEvent& event )
{
}

void IndeciesDlg::OnRemove( wxCommandEvent& event )
{
}

void IndeciesDlg::OnAddSingle( wxCommandEvent& event )
{
}

void IndeciesDlg::OnAddAll( wxCommandEvent& event )
{
}

void IndeciesDlg::OnRemoveSingle( wxCommandEvent& event )
{
}

void IndeciesDlg::OnRemoveAll( wxCommandEvent& event )
{
}

void IndeciesDlg::OnInitDialog( wxInitDialogEvent& event )
{
}

/***** Implementation for class IndexPropsDlg *****/

IMPLEMENT_DYNAMIC_CLASS( IndexPropsDlg, wxDialog )

BEGIN_EVENT_TABLE( IndexPropsDlg, wxDialog )

	EVT_BUTTON( XMLID("del"), IndexPropsDlg::OnDel )
	EVT_BUTTON( XMLID("add"), IndexPropsDlg::OnAdd )
	EVT_BUTTON( XMLID("browse"), IndexPropsDlg::OnBrowse )

	EVT_INIT_DIALOG( IndexPropsDlg::OnInitDialog )

END_EVENT_TABLE()

IndexPropsDlg::IndexPropsDlg()
{}

void IndexPropsDlg::Setup( wxPMIndexingService* svc )
{}

void IndexPropsDlg::OnBrowse( wxCommandEvent& event )
{}

void IndexPropsDlg::OnDel( wxCommandEvent& event )
{}

void IndexPropsDlg::OnAdd( wxCommandEvent& event )
{
	AddFilesDlg* dlg = wxDynamicCast( 
			wxTheXmlResource->LoadWindow( "IndexingSvc_AddFilesDlg", FALSE ),
			AddFilesDlg );

	dlg->Setup( mpIndexSvc );

	if ( dlg->ShowModal() == wxID_OK )
	{
	}

	dlg->Destroy();
}

void IndexPropsDlg::OnInitDialog( wxInitDialogEvent& event )
{}

/***** Implementation for class AddFilesDlg *****/

IMPLEMENT_DYNAMIC_CLASS( AddFilesDlg, wxDialog )

BEGIN_EVENT_TABLE( AddFilesDlg, wxDialog )

	EVT_BUTTON( XMLID(""), AddFilesDlg::OnSelect )

	EVT_INIT_DIALOG( AddFilesDlg::OnInitDialog )

END_EVENT_TABLE()

AddFilesDlg::AddFilesDlg()
{}

void AddFilesDlg::Setup( wxPMIndexingService* svc )
{
}

void AddFilesDlg::OnSelect( wxCommandEvent& event )
{
}

void AddFilesDlg::OnInitDialog( wxInitDialogEvent& event )
{
}

/***** Implementation for class DbTableBase *****/

DbTableBase::DbTableBase()
	: mpRel( NULL ),
	  mpSvc( NULL )
{}

DbTableBase::~DbTableBase()
{
	if ( mpRel ) delete mpRel;
}

bool DbTableBase::Begin( int indexId )
{
	if ( indexId == -1 )
		return mpRel->begin() == db_ok;
	else
		return mpRel->begin( (const int32)indexId ) == db_ok;
}

bool DbTableBase::End()
{
	return mpRel->end() == db_ok;
}

bool DbTableBase::First()
{
	return (mpRel->first() != NULL);
}

bool DbTableBase::Next()
{
	return (mpRel->next() != NULL);
}

bool DbTableBase::Ok()
{
	return mpRel->ok();
}

bool DbTableBase::Add()
{
	return mpRel->add() == db_ok;
}

bool DbTableBase::Delete()
{
	return mpRel->del() == db_ok;
}

void DbTableBase::Setup( diaRel* rel, wxPMIndexingService* svc )
{
	mpRel = rel;
	mpSvc = svc;
}

bool DbTableBase::Extract()
{
	return mpRel->extract() == db_ok;
}

bool DbTableBase::Write()
{
	return mpRel->write() == db_ok;
}

bool DbTableBase::RemoveAll()
{
	mpRel->begin();
	bool failed = FALSE;

	for( mpRel->first(); mpRel->ok(); mpRel->next() )

		failed |= (mpRel->del() != db_ok);

	mpRel->end();
	mpRel->flush();

	return failed == FALSE;
}

bool DbTableBase::Flush()
{
	return mpRel->flush() == db_ok;
}

bool DbTableBase::Seek()
{
	return mpRel->seek() == db_ok;
}

/***** Implementation for class IndexTable *****/

const char* IndexTable::GetName()
{
	return ((Index*)mpRel)->name;
}

const char* IndexTable::GetLanguage()
{
	return ((Index*)mpRel)->language;
}

const char* IndexTable::GetHandlerName()
{
	return ((Index*)mpRel)->handlerName;
}

const char* IndexTable::GetBoundProject()
{
	return ((Index*)mpRel)->boundProject;
}

const char* IndexTable::GetContentRoot()
{
	return ((Index*)mpRel)->contentRoot;
}

const char* IndexTable::GetPaths()
{
	return ((Index*)mpRel)->paths;
}

int IndexTable::GetId()
{
	return ((Index*)mpRel)->id;
}

void IndexTable::SetName( const wxString& name )
{
	strcpy( ((Index*)mpRel)->name, name.c_str() );
}

void IndexTable::SetLanguage( const wxString& lang )
{
	((Index*)mpRel)->language = lang.c_str();
}

void IndexTable::SetHandlerName( const wxString& handler )
{
	((Index*)mpRel)->handlerName = handler.c_str();
}

void IndexTable::SetBoundProject( const wxString& project )
{
	((Index*)mpRel)->boundProject = project.c_str();
}

void IndexTable::SetContentRoot( const wxString& root )
{
	((Index*)mpRel)->contentRoot = root.c_str();
}

void IndexTable::SetPaths( const wxString& paths )
{
	((Index*)mpRel)->paths = paths.c_str();
}

void IndexTable::SetId( int id )
{
	((Index*)mpRel)->id = id;
}

/***** Implementation for class FileTable *****/

const char* FileTable::GetName()
{
	return ((File*)mpRel)->name;
}

wxDateTime FileTable::GetLastIndexingDate()
{
	// FIXME:: TBD:: ...
	return wxDateTime::Now();
}

int FileTable::GetId()
{
	return ((File*)mpRel)->id;
}

int FileTable::GetIndexId()
{
	return ((File*)mpRel)->fk_index;
}

void FileTable::SetName( const wxString& name )
{
	strcpy( ((File*)mpRel)->name, name.c_str() );
}

void FileTable::SetLastIndexingDate( const wxDateTime& date )
{
	// FIXME:: TBD:: ...
}

void FileTable::SetIndexId( int indexId )
{
	((File*)mpRel)->fk_index = indexId;
}

void FileTable::DeleteItemsByIndex( int indexId )
{
	SetId( -1 );
	SetIndexId( indexId );
	mpRel->begin( File::dbIdx_by_index );

	mpRel->seek();
	
	for( mpRel->next(); mpRel->ok() && ((File*)mpRel)->fk_index == indexId; 
		 mpRel->next() )
	
		mpRel->del();
	
	mpRel->end();
}

bool FileTable::FindById( int fileId )
{
	SetId( fileId );
	mpRel->begin( File::dbIdx_by_id );

	bool ok = mpRel->find() == db_ok;

	mpRel->end();
	return ok;
}

void FileTable::SetId( int id )
{
	((File*)mpRel)->id = id;
}

/***** Implementation for class ItemTable *****/

bool ItemTable::Begin( int indexId )
{
	if ( indexId == BY_NAME )

		return (mpRel->begin( Item::dbIdx_by_name ) != NULL);
	else
		return (mpRel->begin() != NULL);
}

const char* ItemTable::GetName()
{
	return ((Item*)mpRel)->name;
}

const char* ItemTable::GetContext()
{
	return ((Item*)mpRel)->context;
}

wxString ItemTable::GetDataStr()
{
	return wxString( (const char*)((Item*)mpRel)->dataStr );
}

int ItemTable::GetLineNo()
{
	return ((Item*)mpRel)->lineNo;
}

int ItemTable::GetLineCount()
{
	return ((Item*)mpRel)->lineCount;
}

int ItemTable::GetType()
{
	return ((Item*)mpRel)->type;
}

int ItemTable::GetId()
{
	return ((Item*)mpRel)->id;
}

int ItemTable::GetFileId()
{
	return ((Item*)mpRel)->fk_file;
}

int ItemTable::GetIndexId()
{
	return ((Item*)mpRel)->fk_index;
}

void ItemTable::SetName( const wxString& name )
{
	strcpy( ((Item*)mpRel)->name, name.c_str() );
}

void ItemTable::SetContext( const wxString& context )
{
	strcpy( ((Item*)mpRel)->context, context.c_str() );
}

void ItemTable::SetDateStr( const wxString& dataStr )
{
	((Item*)mpRel)->dataStr = dataStr.c_str();
}

void ItemTable::SetLineNo( int lineNo )
{
	((Item*)mpRel)->lineNo = lineNo;
}

void ItemTable::SetLineCount( int count )
{
	((Item*)mpRel)->lineCount = count;
}

void ItemTable::SetType( int type )
{
	((Item*)mpRel)->type = type;
}

void ItemTable::SetFileId( int fileId )
{
	((Item*)mpRel)->fk_file = fileId;
}

void ItemTable::SetIndexId( int indexId )
{
	((Item*)mpRel)->fk_index = indexId;
}

void ItemTable::DeleteItemsByType( int _type, int indexId )
{
	SetId( -1 );
	SetIndexId( indexId );
	SetType( _type );
	mpRel->begin( Item::dbIdx_by_type );

	mpRel->seek();

	for( mpRel->next(); mpRel->ok() &&
						GetType() == _type &&
						GetIndexId() == indexId ; mpRel->next() )

		mpRel->del();

	mpRel->end();
}

void ItemTable::SetId( int id )
{
	((Item*)mpRel)->id = id;
}

bool ItemTable::FindByName( const wxString& _name, const wxString& _context, 
							int _type, bool matchSubstring )
{
	SetId( -1 );
	SetName( _name );
	SetContext( _context );
	SetType( _type );

	mpRel->begin( Item::dbIdx_by_name );
	//mpRel->first();

	mpRel->seek();
	mpRel->next();

	while( mpRel->ok() )
	{
		wxString n = GetName();
		wxString c = GetContext();
		int t = GetType();
		int id = GetId();

		if ( matchSubstring ||
			 ( GetName() == _name &&
			   GetContext() == _context &&
			   GetType() == _type )
		   )
		{
			mpRel->end();
			return TRUE;
		}

		mpRel->next();
	}
	
	mpRel->end();
	
	return FALSE;
}

bool ItemTable::FindByType( int _type )
{
	SetId( -1 );
	SetType( _type );

	mpRel->begin( Item::dbIdx_by_type );

	mpRel->seek();
	mpRel->next();

	return mpRel->ok();
}

