''  fbdoc - FreeBASIC User's Manual Converter/Generator
''	Copyright (C) 2006-2019 The FreeBASIC development team.
''
''	This program is free software; you can redistribute it and/or modify
''	it under 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.
''
''	This program is distributed in the hope that it will be useful,
''	but WITHOUT ANY WARRANTY; without even the implied warranty of
''	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  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., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301 USA.


'' fbdoc_buildtoc
''
''
'' chng: sep/2006 written [coderJeff]
''

#include once "fbdoc_buildtoc.bi"
#include once "fbdoc_loader.bi"
#include once "CWiki.bi"

#include once "printlog.bi"

namespace fb.fbdoc

	dim shared as CWiki ptr wiki

	'' --------------------------------------------------------------------------
	'' Content Scanning
	'' --------------------------------------------------------------------------


	'':::::
	function _LoadAndScanPageLinks _
		( _	
			byval page as CPage ptr, _
			byval paglist as CPageList ptr, _
			byval bFollowLinks as integer _
		) as integer

		dim as CList ptr lst

		dim as CPage ptr newpage, prevpage
		dim as string sPageName, sBody, sTitle

		if( page = NULL ) then
			return FALSE
		end if

		if( page->GetScanned() ) then
			return TRUE
		end if

		page->SetScanned( TRUE )

		sPageName = page->GetName()
		if( len(sPageName) = 0 ) then
			printlog "Warning: Page with no key name should never happen."
			return FALSE
		end if

		printlog "Scanning: " + sPageName
		sBody = LoadPage( sPageName )
		if( len( sBody ) = 0 ) then
			return FALSE
		end if

		wiki->Parse( page->GetName(), sBody ) 

		sTitle = wiki->GetPageTitle()
		if( len(sTitle) > 0 ) then
			page->SetPageTitle( sTitle )
		end if
		
		'' Get a list of all the links on the currently loaded page
		lst = wiki->GetDocTocLinks( (lcase(sPageName) = "catpgfunctindex") )

		dim as WikiPageLink ptr pagelink

		page->FreePageLinks()

		pagelink = lst->GetHead()

		do while( pagelink <> NULL )

			page->AddPageLink( pagelink->text, pagelink->link.url, pagelink->level, pagelink->link.linkclass )
		
			if( len(pagelink->link.url) > 0 ) then
				if( instr(pagelink->link.url, ":") = 0 ) then
				if( instr(pagelink->link.url, "/") = 0 ) then
				if( instr(pagelink->link.url, ".") = 0 ) then
						prevpage = paglist->Find( pagelink->link.url )
						if( prevpage = NULL ) then
							if( bFollowLinks ) then
								newpage = paglist->AddNewPage( pagelink->link.url, pagelink->text, 0, FALSE )
							end if
						end if
				end if
				end if
				end if
			end if

			pagelink = lst->GetNext( pagelink )
			
		loop
		
	end function


	'':::::
	private function IsTOCPage( byval sPage as zstring ptr ) as integer

		function = FALSE

		if( sPage = NULL ) then
			exit function
		end if

		if( len(*sPage) = 0 ) then
			exit function
		end if

		'' !!! TODO !!!: use a datafile to specify a list, matched on patterns of pages names
		'' to include in auto-generated TOC.  e.g. DocToc, CatPg*, CVS*

		if( _
			( lcase(*sPage) = "doctoc" ) _
			or ( lcase(left(*sPage, 5)) = "catpg") _
			or ( lcase(left(*sPage, 3)) = "cvs") _
			or ( lcase(left(*sPage, 3)) = "svn") _
			or ( lcase(*sPage) = "extlibtoc" ) _
		) then
			function = TRUE
		end if

	end function

	'':::::
	private function IsInternalURL( byref sUrl as string ) as integer
		
		function = FALSE

		if( len(sUrl) > 0 ) then
			if( instr(sUrl, ":") = 0 ) then
				if( instr(sUrl, "/") = 0 ) then
					function = TRUE
				end if
			end if
		end if

	end function

	'':::::
	private sub _BuildTOCFromPage _
		( _
			byval page as CPage ptr, _
			byval startlevel as integer, _
			byval toclist as CPageList ptr, _
			byval paglist as CPageList ptr, _
			byval lnklist as CPageList ptr _
		)

		dim as CPage ptr newpage, prevpage
		dim as string sBody, sPageName, sTitle
		dim as CList ptr lst
		dim as PageLinkItem ptr pagelink
		dim as integer bAddPage, bFollowPage, bAddLink

		if( page = NULL ) then
			exit sub
		end if

		sPageName = page->GetName()
		if( len(sPageName) = 0 ) then
			exit sub
		end if
		
		if( IsTOCPage(strptr(sPageName)) = FALSE ) then
			exit sub
		endif 

		prevpage = paglist->Find( sPageName )
		if( prevpage = NULL ) then
			exit sub
		end if

		lst = prevpage->GetPageLinks()
		if( lst->GetHead() = NULL ) then
			exit sub
		end if

		printlog "Building Toc: " + sPageName
		
		sTitle = page->GetTitle()

		'' Loop through each link.  Record the page and search for more sections
		pagelink = lst->GetHead()
		do while( pagelink <> NULL )
				
			bAddPage = FALSE
			bAddLink = FALSE
			bFollowPage = FALSE

			'' Only {{fbdoc item="sect|subsect"}} links are used as branches in TOC
			select case pagelink->linkclass
			case WIKI_PAGELINK_CLASS_SECTION, WIKI_PAGELINK_CLASS_SUBSECT
				bAddPage = TRUE

			case WIKI_PAGELINK_CLASS_KEYWORD

				'' Only internal pages can be TOC nodes
				if( IsInternalURL( pagelink->url ) ) then

					bAddPage = TRUE
					bAddLink = TRUE

					'' Only special named TOC pages are followed
					if( IsTOCPage(pagelink->url) ) then
						bFollowPage = TRUE
					end if
				end if

			case WIKI_PAGELINK_CLASS_DEFAULT

				if( IsInternalURL( pagelink->url ) ) then

					bAddLink = TRUE

				end if


			end select

			''
			if( bAddLink ) then

				newpage = lnklist->AddNewPage( _
					pagelink->url, _
					pagelink->text, _
					0, TRUE )
			
			end if

			''
			if( bAddPage ) then

				'' Add it to the TOC list
				newpage = toclist->AddNewPage( _
					pagelink->url, _
					pagelink->text, _
					startlevel + pagelink->level, TRUE )

				if( bFollowPage ) then
					' Only follow links that will be later emitted
					prevpage = paglist->Find( pagelink->url )
					if( prevpage ) then
						_BuildTOCFromPage( prevpage, startlevel + pagelink->level + 1, toclist, paglist, lnklist )
					end if
				end if
			end if

			pagelink = lst->GetNext( pagelink )

		loop

	end sub

	'':::::
	function FBDoc_BuildTOC _
		( _
			byval toc_pagename as zstring ptr, _
			byval toc_pagetitle as zstring ptr, _
			byval paglist as CPageList ptr ptr, _
			byval toclist as CPageList ptr ptr, _
			byval lnklist as CPageList ptr ptr _
		) as integer

		dim as CPage ptr page
		dim as any ptr page_i

		if( paglist = NULL ) then
			return FALSE
		end if

		if( toclist = NULL ) then
			return FALSE
		end if

		if( lnklist = NULL ) then
			return FALSE
		end if

		*paglist = new CPageList
		*toclist = new CPageList
		*lnklist = new CPageList

		if( *paglist = NULL or *toclist = NULL or *lnklist = NULL ) then
			if( *paglist ) then
				delete *paglist
				*paglist = NULL
			end if

			if( *toclist ) then
				delete *toclist
				*toclist = NULL
			end if

			if( *lnklist ) then
				delete *lnklist
				*lnklist = NULL
			end if

			return FALSE
		end if

		wiki = new CWiki

		page = (*paglist)->AddNewPage( toc_pagename, toc_pagetitle, 0, TRUE )

		'' Build a list of all pages to include in the documentation
		page = (*paglist)->NewEnum( @page_i )
		while( page )
			if( len(page->GetName()) > 0 ) then
				_LoadAndScanPageLinks( page, *paglist, TRUE )
			end if
			page = (*paglist)->NextEnum( @page_i )
		wend

		'' Build a list of pages as they will appear in the TOC
		page = (*toclist)->AddNewPage( toc_pagename, toc_pagetitle, 0, TRUE )
		_BuildTOCFromPage( page, 1, *toclist, *paglist, *lnklist )

		delete wiki
		wiki = NULL

		return TRUE

	end function


	'':::::
	function FBDoc_BuildSinglePage _
		( _
			byval toc_pagename as zstring ptr, _
			byval toc_pagetitle as zstring ptr, _
			byval paglist as CPageList ptr ptr, _
			byval toclist as CPageList ptr ptr, _
			byval followlinks as integer _
		) as integer

		dim as CPage ptr page
		dim as any ptr page_i
		dim as integer bFirstTime

		if( paglist = NULL ) then
			return FALSE
		end if

		if( toclist = NULL ) then
			return FALSE
		end if

		*paglist = new CPageList

		if( *paglist = NULL ) then
			return FALSE
		end if

		*toclist = new CPageList

		if( *toclist = NULL ) then
			delete *paglist
			*paglist = NULL
			return FALSE
		end if

		wiki = new CWiki

		page = (*paglist)->AddNewPage( toc_pagename, toc_pagetitle, 0, TRUE )
		page = (*paglist)->NewEnum( @page_i )

		bFirstTime = followlinks

		while( page )
			if( len(page->GetName()) > 0 ) then
				_LoadAndScanPageLinks( page, *paglist, bFirstTime )
				bFirstTime = FALSE
			end if
			page = (*paglist)->NextEnum( @page_i )
		wend

		''page = *toclist->AddNewPage( toc_pagename, toc_pagetitle, 0, TRUE )
		''_BuildTOCFromPage( page, 1, *toclist, *paglist )

		delete wiki
		wiki = NULL

		return TRUE

	end function

end namespace
