/////////////////////////////////////////////////////////////////////////////
// Name:        No names yet.
// Purpose:     Contrib. demo
// Author:      Aleksandras Gluchovas
// Modified by:
// Created:     22/09/98
// RCS-ID:      $Id: docripper.cpp,v 1.4 2002/09/14 09:35:50 GD Exp $
// Copyright:   (c) Aleskandars Gluchovas
// Licence:   	wxWindows licence
/////////////////////////////////////////////////////////////////////////////

// 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 "docripper.h"

#if wxUSE_IOSTREAMH
    #include <iostream.h>
#else
    #include <iostream>
#endif

// script templates

// ***** currently only HTML versions of variouse templates available ***** //

static const char* HTM_TopTempl = 

"<html><body bgcolor=#FFFFFF>\n\
\n\n<!------ Automatically Generated by \"wxDocRipper\"------->\n\n\n\
<p><h2>$(NAME)</h2><p>\n\
<ul>\n\
$(REFLIST)\
</ul><p>\n\n\
";

static const char* HTM_ContentIdxTempl = 

"\
<a name=\"r$(ID)_$(NAME)\">\n\
<p><hr>\n\
<h2><p>$(NAME)<p></h2>\
<ul>\n\
$(REFLIST)\
</ul><p>\n\n\
";

static const char* HTM_SuperContentTempl = 

"\
<a name=\"r$(ID)_$(NAME)\">\n\
<p><hr>\n\
<p><h2>$(NAME)<p></h2>\
$(BODY)\n\
";

static const char* HTM_SubContentTempl = 

"\
<a name=\"r$(ID)_$(NAME)\">\n\
<p><hr>\n\
<p><h3>$(NAME)<p></h3>\
$(BODY)\n\
";

static const char* HTM_OutLineTempl =

"\
<p>\n\
<b><font color=\"#FF0000\">$(NAME)</font></b><p>\n\
";

static const char* HTM_OutLine1Templ =

"\
<p>\n\
<b><i><font color=\"#101010\">$(NAME)</font></i></b>\n\
<ul>\n\
$(REFLIST)\
</ul>\n\n\
";

static const char* HTM_RefTempl =

"\
<li><a href=\"#r$(ID)_$(NAME)\">$(NAME)</A>\n\
";

static const char* HTM_DeadRefTempl =

"\
<li></b>$(NAME)\n\
";

/***** Implementation for class RipperDocGen *****/

RipperDocGen::RipperDocGen()

	: mTopTempl         ( HTM_TopTempl ),
	  mContentIdxTempl  ( HTM_ContentIdxTempl ),
	  mSuperContentTempl( HTM_SuperContentTempl ),
	  mSubContentTempl  ( HTM_SubContentTempl ),
	  mOutLineTempl     ( HTM_OutLineTempl ),
	  mOutLine1Templ    ( HTM_OutLine1Templ ),

	  mRefTempl         ( HTM_RefTempl ),
	  mDeadRefTempl     ( HTM_DeadRefTempl ),

	  mpCurClassSect(0)
{																							   // topIndex is not referenced
	mpTopIdx        = new ScriptSection( "Source Code Contents"       , "", &mTopTempl       , 0          );
	mpClassIdx      = new ScriptSection( "Classes Reference"          , "", &mContentIdxTempl, &mRefTempl );
	mpEnumIdx       = new ScriptSection( "Enumerations  Reference"    , "", &mContentIdxTempl,  &mRefTempl );
	mpTypeDefIdx    = new ScriptSection( "Type Definitions Reference" , "", &mContentIdxTempl, &mRefTempl );
	mpMacroIdx      = new ScriptSection( "Macros Reference"           , "", &mContentIdxTempl, &mRefTempl );
	mpGlobalVarsIdx = new ScriptSection( "Global Variables Reference" , "", &mContentIdxTempl, &mRefTempl );
	mpGlobalFuncIdx = new ScriptSection( "Global Functions  Reference", "", &mContentIdxTempl, &mRefTempl );
	mpConstIdx      = new ScriptSection( "Constants  Reference"       , "", &mContentIdxTempl, &mRefTempl );

	// assemble top index
	mpTopIdx->AddSection( mpClassIdx     , 1 );
	mpTopIdx->AddSection( mpEnumIdx      , 1 );
	mpTopIdx->AddSection( mpTypeDefIdx   , 1 );
	mpTopIdx->AddSection( mpMacroIdx     , 1 );
	mpTopIdx->AddSection( mpGlobalVarsIdx, 1 );
	mpTopIdx->AddSection( mpGlobalFuncIdx, 1 );
	mpTopIdx->AddSection( mpConstIdx     , 1 );

	// register reserved variables for index and description templates
	ScriptSection::RegisterTemplate( mTopTempl );
	ScriptSection::RegisterTemplate( mContentIdxTempl );
	ScriptSection::RegisterTemplate( mSuperContentTempl );
	ScriptSection::RegisterTemplate( mSubContentTempl );
	ScriptSection::RegisterTemplate( mOutLineTempl );
	ScriptSection::RegisterTemplate( mOutLine1Templ );
	ScriptSection::RegisterTemplate( mRefTempl );
	ScriptSection::RegisterTemplate( mDeadRefTempl );

	// create the top-most (interfile) context
	mpFileBinderCtx = new spFile();

	// the default script is HTML
	mTags = get_HTML_markup_tags();

	mpParser = 0; // no default parser!
}

void RipperDocGen::Init( SourceParserBase* pParser )
{
	mpParser = pParser;
}

RipperDocGen::~RipperDocGen()
{
	delete mpFileBinderCtx;
}

void RipperDocGen::AppendComments( spContext& fromContext, string& str )
{
	if ( !fromContext.HasComments() ) return;

	size_t start = str.length();

	str += mTags[TAG_BOLD].end;
	str += mTags[TAG_PARAGRAPH].start;

	MCommentListT& lst = fromContext.GetCommentList();

	for( size_t i = 0; i != lst.size(); ++i )
	{
	
		if ( i != 0 )
			
			if ( lst[i]->StartsParagraph() )
			{
				str += mTags[TAG_PARAGRAPH].start;

			}
	
		str += lst[i]->mText;
	}

	// remove new lines, and insert paragraph breaks

	// if empty lines found

	size_t len = str.length();

	for( size_t n = start; n != len; ++n )
	
		if ( str[n] == 10 || 
		     str[n] == 13  ) 

		{

			if ( n + 2 < len )

			{

				if ( ( str[n] == 13 && str[n+1] == 10 &&  // FIXME:: quick-hack

					   str[n+2] == 13 ) ||

					 ( str[n] == 10 && str[n+1] == 10 )

			    )

				{

					str.insert( n + 1, "<p>" ); // FIXME:: quick-hack

					len += 3;

				}

			}



			str[n] = ' ';

		}


	str += mTags[TAG_PARAGRAPH].end;
}

void RipperDocGen::AppendMulitilineStr( string& st, string& mlStr )
{
	st = mTags[TAG_FIXED_FONT].start;
	st += mlStr;
	st += mTags[TAG_FIXED_FONT].end;
}

void RipperDocGen::AppendHighlightedSource( string& st, string source )
{
	// FIXME:: below should not be fixed :)
	char buf[1024*32];

	// DBG:::
//	ASSERT( source.length() + 1 < sizeof(buf) );

	strcpy( buf, source.c_str() );
						
	// highlight things
	mSrcPainter.Init();
	mSrcPainter.ProcessSource( buf, strlen(buf) );
	mSrcPainter.GetResultString( st, mTags );
}

bool RipperDocGen::CheckIfUncommented( spContext& ctx, ScriptSection& toSect )
{
	if ( ctx.HasComments() ) return 0;

	toSect.AddReference( 
		new ScriptSection( GetScopedName( ctx ), "", 0, &mDeadRefTempl )
	);

	return 1;
}

ScriptTemplate* RipperDocGen::GetRefTemplFor( spContext& ctx )
{
	if ( ctx.HasComments() )

		return &mRefTempl;
	else
		return &mDeadRefTempl;
}

string RipperDocGen::GetScopedName( spContext& ofCtx )
{
	if ( ofCtx.IsInFile() ) return ofCtx.GetName();
	else
		return ofCtx.GetOutterContext()->GetName() + 
			   "::" + ofCtx.GetName();
}

void RipperDocGen::AddToCurrentClass( ScriptSection* pSection, spContext& ctx, 
									  const char* subSectionName )
{
	string sName;

	if ( ctx.mVisibility == SP_VIS_PROTECTED )

		sName = "Protected members/";
	else
	if ( ctx.mVisibility == SP_VIS_PRIVATE )

		sName = "Private members/";
	else
		sName = "Public members/";

	sName += subSectionName;

	ScriptSection* pSect = mpCurClassSect->GetSubsection( sName.c_str() );

	if ( CheckIfUncommented( ctx, *pSect ) )
	{
		delete pSection;
		return;
	}

	pSect->AddReference( pSection );

	mpCurClassSect->AddSection( pSection );
}

void RipperDocGen::LinkSuperClassRefs()
{
	MMemberListT clLst;

	// collect all classes in the context tree
	mpFileBinderCtx->GetContextList( clLst, SP_CTX_CLASS );

	for( size_t i = 0; i != clLst.size(); ++i )
	{
		spClass& cl = *((spClass*)clLst[i]);

		// FIXME:: why sometimes GetUserData() returns NULL?
		if ( !cl.GetUserData() )
			continue;

		ScriptSection* pClSect = (ScriptSection*)cl.GetUserData();
		ScriptSection* pSuperSect = pClSect->GetSubsection("Derived from");

		for( size_t n = 0; n != cl.mSuperClassNames.size(); ++n )
		{
			string& superClName = cl.mSuperClassNames[n];

			spClass* pFound = NULL;

			string* name;

			for( size_t k = 0; k != clLst.size(); ++k )
			{
				name = &clLst[k]->GetName();

				if ( clLst[k]->GetName() == superClName )
				{
					pFound = (spClass*)clLst[k];
					break;
				}
			}

			if ( !pFound )
			{
				ScriptSection* pNotFound = 
					new ScriptSection( superClName, "", 0, &mDeadRefTempl );

				pSuperSect->AddReference( pNotFound );
			}
			else
				if ( pFound->GetUserData() )
				
					pSuperSect->AddReference( 
						(ScriptSection*)pFound->GetUserData() );
		}
	}
}

void RipperDocGen::ProcessFile( const char* sourceFile )
{
	wxSTD cout << "Processing file " << sourceFile << "..." << wxSTD endl;

	spFile* pCtx = mpParser->ParseFile( sourceFile );

	if ( pCtx == NULL )
	{
		wxSTD cout << "Cannot open file " << sourceFile << ", skipped..." << wxSTD endl;

		return;
	}

	VisitAll( *pCtx, TRUE );

	mpFileBinderCtx->AddMember( pCtx );
}

// implementations of "visiting procedures"

void RipperDocGen::VisitEnumeration( spEnumeration& en )
{
	// FOR NOW:: do not reference "nameless" enums
	if ( en.GetName() == "" ) return;

	if ( CheckIfUncommented( en, *mpEnumIdx ) )
		return;

	string body;
	body += mTags[TAG_BOLD].start;

	AppendMulitilineStr( body, en.mEnumContent );

	body += mTags[TAG_BOLD].end;

	string line;
	AppendHighlightedSource( line, body );
	AppendComments( en, line );

	mpEnumIdx->AddSection( 
		new ScriptSection( en.GetName(), line, 
						   &mSubContentTempl,
						   GetRefTemplFor( en ) ), 1
	);
}

void RipperDocGen::VisitTypeDef( spTypeDef& td )
{
	if ( CheckIfUncommented( td, *mpTypeDefIdx ) )
		return;

	string body;
	body += mTags[TAG_BOLD].start;
	body += "typdef ";
	body += mTags[TAG_BOLD].end;

	AppendMulitilineStr( body, td.mOriginalType );
	body += td.mOriginalType;
	body += ' ';

	body += mTags[TAG_BOLD].start;
	body += td.GetName();
	body += mTags[TAG_BOLD].end;

	string line;
	AppendHighlightedSource( line, body );
	AppendComments( td, line );

	mpTypeDefIdx->AddSection( 
		new ScriptSection( td.GetName(), line, 
						   &mSubContentTempl,
						   GetRefTemplFor( td ) ), TRUE
	);
}

void RipperDocGen::VisitPreprocessorLine( spPreprocessorLine& pd )
{
	if ( pd.mDefType != SP_PREP_DEF_REDEFINE_SYMBOL )

	

		return;


	if ( CheckIfUncommented( pd, *mpMacroIdx ) )
		return;

	string body;
	body += mTags[TAG_FIXED_FONT].start;

	string coloredLine = pd.mLine;
	AppendHighlightedSource( coloredLine, pd.mLine );

	AppendMulitilineStr( body, coloredLine );

	body += mTags[TAG_FIXED_FONT].end;

	AppendComments( pd, body );

	mpMacroIdx->AddSection( 
		new ScriptSection( pd.GetName(), body, 
						   &mSubContentTempl,
						   GetRefTemplFor( pd ) ), TRUE
	);
}

void RipperDocGen::VisitClass( spClass& cl )
{
	// FOR NOW:: do not document nested classes -
	//           nicier visiting method yet needed

	if ( cl.IsInClass() )
	{
		SkipChildren(); // spVisitor's method
		return;
	}

	string body;
	AppendComments( cl, body );

	mpCurClassSect = 		
		new ScriptSection( cl.GetName(), body, &mSuperContentTempl, &mRefTempl );

	// set up reference in the class context, pointing back
	// to the section where this class is represented
	cl.SetUserData( mpCurClassSect );

	ScriptSection* pSuper    = new ScriptSection( "Derived from"    ,"", &mOutLine1Templ,0, 1 );

	ScriptSection* pPublic    = new ScriptSection( "Public members"    ,"", &mOutLineTempl,0, 1 );
	ScriptSection* pProtected = new ScriptSection( "Protected members" ,"", &mOutLineTempl,0, 1 );
	ScriptSection* pPrivate   = new ScriptSection( "Private members"   ,"", &mOutLineTempl,0, 1 );

	pPublic->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) );
	pPublic->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) );


	pProtected->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) );
	pProtected->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) );


	pPrivate->AddSection( new ScriptSection( "Operations", "", &mOutLine1Templ, 0, 1 ) );
	pPrivate->AddSection( new ScriptSection( "Attributes", "", &mOutLine1Templ, 0, 1 ) );

	mpCurClassSect->AddSection( pSuper    );
	mpCurClassSect->AddSection( pPublic    );
	mpCurClassSect->AddSection( pProtected );
	mpCurClassSect->AddSection( pPrivate   );

	mpClassIdx->AddSection( mpCurClassSect, TRUE );
}

void RipperDocGen::VisitAttribute( spAttribute& attr )
{
	string body;
	body += mTags[TAG_BOLD].start;
	body += attr.mType;
	body += mTags[TAG_BOLD].end;

	body += mTags[TAG_ITALIC].start;
	body += ' ';
	body += attr.GetName();
	body += mTags[TAG_ITALIC].end;

	string line;
	AppendHighlightedSource( line, body );
	AppendComments( attr, line );

	ScriptSection* pSection = 
		new ScriptSection( GetScopedName( attr ), line, 
						   &mSubContentTempl,
						   GetRefTemplFor( attr ) );

	if ( attr.mIsConstant )
	
		mpConstIdx->AddSection( pSection, TRUE );
	
	else
	if ( !attr.IsInClass() )
	{
		if ( CheckIfUncommented( attr, *mpGlobalVarsIdx ) )
			return;

		mpGlobalVarsIdx->AddSection( pSection, TRUE );
	}
	else

		AddToCurrentClass( pSection, attr, "Attributes" );
}

void RipperDocGen::VisitOperation( spOperation& op )
{
	string body;

	AppendHighlightedSource( body, op.GetFullName(mTags) );

	AppendComments( op, body );

	ScriptSection* pSection = 
		new ScriptSection( GetScopedName( op ), body, 
						   &mSubContentTempl,
						   GetRefTemplFor( op ) );

	if ( !op.IsInClass() )
	{
		if ( CheckIfUncommented( op, *mpGlobalFuncIdx ) )
			return;

		mpGlobalFuncIdx->AddSection( pSection, 1 );
	}
	else
		AddToCurrentClass( pSection, op, "Operations" );
}

bool RipperDocGen::OnSaveDocument( ScriptStream& stm )
{
	LinkSuperClassRefs();

	// FOR NOW:: doesn't work yet
	//mpTopIdx->RemoveEmptySections();

	return 1; // saving can proceed now 
}

