/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"



#include <stdlib.h>

#ifndef _APP_HXX
#include <vcl/svapp.hxx>
#endif
#include <tools/urlobj.hxx>

#define _SVSTDARR_STRINGS
#include <svl/svstdarr.hxx>
#include <sfx2/linkmgr.hxx>			// LinkManager
#include <unotools/charclass.hxx>
#include <fmtcntnt.hxx>
#include <doc.hxx>
#include <swserv.hxx>			// fuer Server-Funktionalitaet
#include <IMark.hxx>
#include <bookmrk.hxx>
#include <section.hxx>			// fuer SwSectionFmt
#include <swtable.hxx>			// fuer SwTable
#include <node.hxx>
#include <ndtxt.hxx>
#include <pam.hxx>
#include <docary.hxx>
#include <MarkManager.hxx>

using namespace ::com::sun::star;

namespace
{

    static ::sw::mark::DdeBookmark* lcl_FindDdeBookmark(
        const IDocumentMarkAccess& rMarkAccess,
        const String& rName,
        const bool bCaseSensitive )
    {
        //Iterating over all bookmarks, checking DdeBookmarks
        const ::rtl::OUString sNameLc = bCaseSensitive ? rName : GetAppCharClass().lower(rName);
        for(IDocumentMarkAccess::const_iterator_t ppMark = rMarkAccess.getCommonMarksBegin();
            ppMark != rMarkAccess.getCommonMarksEnd();
            ppMark++)
        {
            if ( IDocumentMarkAccess::GetType( *(ppMark->get()) ) == IDocumentMarkAccess::DDE_BOOKMARK)
            {
                ::sw::mark::DdeBookmark* const pBkmk = dynamic_cast< ::sw::mark::DdeBookmark*>(ppMark->get());
                if (
                    (bCaseSensitive && (pBkmk->GetName() == sNameLc)) ||
                    (!bCaseSensitive && GetAppCharClass().lower(pBkmk->GetName()) == String(sNameLc))
                   )
                {
                    return pBkmk;
                }
            }
        }
        return NULL;
    }
}

struct _FindItem
{
    const String m_Item;
    SwTableNode* pTblNd;
    SwSectionNode* pSectNd;

    _FindItem(const String& rS)
        : m_Item(rS), pTblNd(0), pSectNd(0)
    {}
};

sal_Bool lcl_FindSection( const SwSectionFmtPtr& rpSectFmt, void* pArgs, bool bCaseSensitive )
{
    _FindItem * const pItem( static_cast<_FindItem*>(pArgs) );
	SwSection* pSect = rpSectFmt->GetSection();
	if( pSect )
	{
        String sNm( (bCaseSensitive)
                ? pSect->GetSectionName()
                : GetAppCharClass().lower( pSect->GetSectionName() ));
        String sCompare( (bCaseSensitive)
                ? pItem->m_Item
                : GetAppCharClass().lower( pItem->m_Item ) );
        if( sNm == sCompare )
		{
			// gefunden, als erfrage die Daten
			const SwNodeIndex* pIdx;
			if( 0 != (pIdx = rpSectFmt->GetCntnt().GetCntntIdx() ) &&
				&rpSectFmt->GetDoc()->GetNodes() == &pIdx->GetNodes() )
			{
				// eine Tabelle im normalen NodesArr
                pItem->pSectNd = pIdx->GetNode().GetSectionNode();
				return sal_False;
			}
//nein!!			// sollte der Namen schon passen, der Rest aber nicht, dann haben wir
			// sie nicht. Die Namen sind immer eindeutig.
		}
	}
	return sal_True;		// dann weiter
}
sal_Bool lcl_FindSectionCaseSensitive( const SwSectionFmtPtr& rpSectFmt, void* pArgs )
{
    return lcl_FindSection( rpSectFmt, pArgs, true );
}    
sal_Bool lcl_FindSectionCaseInsensitive( const SwSectionFmtPtr& rpSectFmt, void* pArgs )
{
    return lcl_FindSection( rpSectFmt, pArgs, false );
}    



sal_Bool lcl_FindTable( const SwFrmFmtPtr& rpTableFmt, void* pArgs )
{
    _FindItem * const pItem( static_cast<_FindItem*>(pArgs) );
	String sNm( GetAppCharClass().lower( rpTableFmt->GetName() ));
    if (sNm.Equals( pItem->m_Item ))
	{
		SwTable* pTmpTbl;
		SwTableBox* pFBox;
		if( 0 != ( pTmpTbl = SwTable::FindTable( rpTableFmt ) ) &&
			0 != ( pFBox = pTmpTbl->GetTabSortBoxes()[0] ) &&
			pFBox->GetSttNd() &&
			&rpTableFmt->GetDoc()->GetNodes() == &pFBox->GetSttNd()->GetNodes() )
		{
			// eine Tabelle im normalen NodesArr
            pItem->pTblNd = (SwTableNode*)
										pFBox->GetSttNd()->FindTableNode();
			return sal_False;
		}
//nein!		// sollte der Namen schon passen, der Rest aber nicht, dann haben wir
		// sie nicht. Die Namen sind immer eindeutig.
	}
	return sal_True;		// dann weiter
}



bool SwDoc::GetData( const String& rItem, const String& rMimeType,
                     uno::Any & rValue ) const
{
    //search for bookmarks and sections case senstive at first. If nothing is found then try again case insensitive
    bool bCaseSensitive = true;
    while( true )
    {
        ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*pMarkManager, rItem, bCaseSensitive);
        if(pBkmk) 
            return SwServerObject(*pBkmk).GetData(rValue, rMimeType);

        // haben wir ueberhaupt das Item vorraetig?
        String sItem( bCaseSensitive ? rItem : GetAppCharClass().lower(rItem));
        _FindItem aPara( sItem );
        ((SwSectionFmts&)*pSectionFmtTbl).ForEach( 0, pSectionFmtTbl->Count(),
                                                    bCaseSensitive ? lcl_FindSectionCaseSensitive : lcl_FindSectionCaseInsensitive, &aPara );
        if( aPara.pSectNd )
        {
            // gefunden, als erfrage die Daten
            return SwServerObject( *aPara.pSectNd ).GetData( rValue, rMimeType );
        }
        if( !bCaseSensitive ) 
            break;
        bCaseSensitive = false;
    }

    _FindItem aPara( GetAppCharClass().lower( rItem ));
	((SwFrmFmts*)pTblFrmFmtTbl)->ForEach( 0, pTblFrmFmtTbl->Count(),
											lcl_FindTable, &aPara );
	if( aPara.pTblNd )
	{
		return SwServerObject( *aPara.pTblNd ).GetData( rValue, rMimeType );
	}

	return sal_False;
}



bool SwDoc::SetData( const String& rItem, const String& rMimeType,
                     const uno::Any & rValue )
{
    //search for bookmarks and sections case senstive at first. If nothing is found then try again case insensitive
    bool bCaseSensitive = true;
    while( true )
    {
        ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*pMarkManager, rItem, bCaseSensitive);
        if(pBkmk) 
            return SwServerObject(*pBkmk).SetData(rMimeType, rValue);

        // haben wir ueberhaupt das Item vorraetig?
        String sItem( bCaseSensitive ? rItem : GetAppCharClass().lower(rItem));
        _FindItem aPara( sItem );
        pSectionFmtTbl->ForEach( 0, pSectionFmtTbl->Count(), bCaseSensitive ? lcl_FindSectionCaseSensitive : lcl_FindSectionCaseInsensitive, &aPara );
        if( aPara.pSectNd )
        {
            // gefunden, als erfrage die Daten
            return SwServerObject( *aPara.pSectNd ).SetData( rMimeType, rValue );
        }
        if( !bCaseSensitive ) 
            break;
        bCaseSensitive = false;
    }

    String sItem(GetAppCharClass().lower(rItem));
    _FindItem aPara( sItem );
	pTblFrmFmtTbl->ForEach( 0, pTblFrmFmtTbl->Count(), lcl_FindTable, &aPara );
	if( aPara.pTblNd )
	{
		return SwServerObject( *aPara.pTblNd ).SetData( rMimeType, rValue );
	}

	return sal_False;
}



::sfx2::SvLinkSource* SwDoc::CreateLinkSource(const String& rItem)
{
    SwServerObject* pObj = NULL;

    //search for bookmarks and sections case senstive at first. If nothing is found then try again case insensitive
    bool bCaseSensitive = true;
    while( true )
    {
        // bookmarks
        ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*pMarkManager, rItem, bCaseSensitive);
        if(pBkmk && pBkmk->IsExpanded()
            && (0 == (pObj = pBkmk->GetRefObject())))
        {
            // mark found, but no link yet -> create hotlink 
            pObj = new SwServerObject(*pBkmk);
            pBkmk->SetRefObject(pObj);
            GetLinkManager().InsertServer(pObj);
        }     
        if(pObj) 
            return pObj;

        _FindItem aPara(bCaseSensitive ? rItem : GetAppCharClass().lower(rItem));
        // sections
        ((SwSectionFmts&)*pSectionFmtTbl).ForEach(0, pSectionFmtTbl->Count(), bCaseSensitive ? lcl_FindSectionCaseSensitive : lcl_FindSectionCaseInsensitive, &aPara);
        if(aPara.pSectNd
            && (0 == (pObj = aPara.pSectNd->GetSection().GetObject())))
        {
            // section found, but no link yet -> create hotlink 
            pObj = new SwServerObject( *aPara.pSectNd );
            aPara.pSectNd->GetSection().SetRefObject( pObj );
            GetLinkManager().InsertServer(pObj);
        }
        if(pObj) 
            return pObj;
        if( !bCaseSensitive ) 
            break;
        bCaseSensitive = false;
    }

    _FindItem aPara( GetAppCharClass().lower(rItem) );
    // tables 
    ((SwFrmFmts*)pTblFrmFmtTbl)->ForEach(0, pTblFrmFmtTbl->Count(), lcl_FindTable, &aPara);
    if(aPara.pTblNd
        && (0 == (pObj = aPara.pTblNd->GetTable().GetObject())))
    {
        // table found, but no link yet -> create hotlink 
        pObj = new SwServerObject(*aPara.pTblNd);
        aPara.pTblNd->GetTable().SetRefObject(pObj);
        GetLinkManager().InsertServer(pObj);
    }
    return pObj;
}

sal_Bool SwDoc::SelectServerObj( const String& rStr, SwPaM*& rpPam,
							SwNodeRange*& rpRange ) const
{
	// haben wir ueberhaupt das Item vorraetig?
	rpPam = 0;
	rpRange = 0;

	String sItem( INetURLObject::decode( rStr, INET_HEX_ESCAPE,
						 				INetURLObject::DECODE_WITH_CHARSET,
										RTL_TEXTENCODING_UTF8 ));

	xub_StrLen nPos = sItem.Search( cMarkSeperator );

	const CharClass& rCC = GetAppCharClass();

	// Erweiterung fuer die Bereiche, nicht nur Bookmarks/Bereiche linken,
	// sondern auch Rahmen(Text!), Tabellen, Gliederungen:
	if( STRING_NOTFOUND != nPos )
	{
		sal_Bool bWeiter = sal_False;
		String sName( sItem.Copy( 0, nPos ) );
		String sCmp( sItem.Copy( nPos + 1 ));
		rCC.toLower( sItem );

		_FindItem aPara( sName );

		if( sCmp.EqualsAscii( pMarkToTable ) )
		{
			rCC.toLower( sName );
			((SwFrmFmts*)pTblFrmFmtTbl)->ForEach( 0, pTblFrmFmtTbl->Count(),
													lcl_FindTable, &aPara );
			if( aPara.pTblNd )
			{
				rpRange = new SwNodeRange( *aPara.pTblNd, 0,
								*aPara.pTblNd->EndOfSectionNode(), 1 );
				return sal_True;
			}
		}
		else if( sCmp.EqualsAscii( pMarkToFrame ) )
		{
			SwNodeIndex* pIdx;
			SwNode* pNd;
			const SwFlyFrmFmt* pFlyFmt = FindFlyByName( sName );
			if( pFlyFmt &&
				0 != ( pIdx = (SwNodeIndex*)pFlyFmt->GetCntnt().GetCntntIdx() ) &&
				!( pNd = &pIdx->GetNode())->IsNoTxtNode() )
			{
				rpRange = new SwNodeRange( *pNd, 1, *pNd->EndOfSectionNode() );
				return sal_True;
			}
		}
		else if( sCmp.EqualsAscii( pMarkToRegion ) )
		{
			sItem = sName;				// wird unten behandelt	!
			bWeiter = sal_True;
		}
		else if( sCmp.EqualsAscii( pMarkToOutline ) )
		{
			SwPosition aPos( SwNodeIndex( (SwNodes&)GetNodes() ));
			if( GotoOutline( aPos, sName ))
			{
				SwNode* pNd = &aPos.nNode.GetNode();
				//sal_uInt8 nLvl = pNd->GetTxtNode()->GetTxtColl()->GetOutlineLevel();//#outline level,zhaojianwei
                const int nLvl = pNd->GetTxtNode()->GetAttrOutlineLevel()-1;//<-end,zhaojianwei

				const SwOutlineNodes& rOutlNds = GetNodes().GetOutLineNds();
				sal_uInt16 nTmpPos;
				rOutlNds.Seek_Entry( pNd, &nTmpPos );
				rpRange = new SwNodeRange( aPos.nNode, 0, aPos.nNode );

				// dann suche jetzt noch das Ende vom Bereich
				for( ++nTmpPos;
						nTmpPos < rOutlNds.Count() &&
						nLvl < rOutlNds[ nTmpPos ]->GetTxtNode()->
								//GetTxtColl()->GetOutlineLevel();//#outline level,zhaojianwei
								GetAttrOutlineLevel()-1;//<-end,zhaojianwei
					++nTmpPos )
					;		// es gibt keinen Block

				if( nTmpPos < rOutlNds.Count() )
					rpRange->aEnd = *rOutlNds[ nTmpPos ];
				else
					rpRange->aEnd = GetNodes().GetEndOfContent();
				return sal_True;
			}
		}

		if( !bWeiter )
			return sal_False;
	}

    //search for bookmarks and sections case senstive at first. If nothing is found then try again case insensitive
    bool bCaseSensitive = true;
    while( true )
    {
        ::sw::mark::DdeBookmark* const pBkmk = lcl_FindDdeBookmark(*pMarkManager, sItem, bCaseSensitive);
        if(pBkmk)
        {
            if(pBkmk->IsExpanded())
                rpPam = new SwPaM(
                    pBkmk->GetMarkPos(),
                    pBkmk->GetOtherMarkPos());
            return static_cast<bool>(rpPam);
        }

        // 
        _FindItem aPara( bCaseSensitive ? sItem : rCC.lower( sItem ) );

        if( pSectionFmtTbl->Count() )
        {
            ((SwSectionFmts&)*pSectionFmtTbl).ForEach( 0, pSectionFmtTbl->Count(),
                                                    bCaseSensitive ? lcl_FindSectionCaseSensitive : lcl_FindSectionCaseInsensitive, &aPara );
            if( aPara.pSectNd )
            {
                rpRange = new SwNodeRange( *aPara.pSectNd, 1,
                                        *aPara.pSectNd->EndOfSectionNode() );
                return sal_True;

            }
        }
        if( !bCaseSensitive ) 
            break;
        bCaseSensitive = false;
    }
	return sal_False;
}

