/**************************************************************
 * 
 * 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 <tools/string.hxx>
#include <tools/debug.hxx>
#include "errhdl.hxx"
#include "swtypes.hxx"
#include "txttypes.hxx"

#include "SwGrammarMarkUp.hxx"


/*************************************************************************
 * SwWrongList::SwWrongList()
 *************************************************************************/
SwWrongList::SwWrongList( WrongListType eType ) : 
    meType       (eType),
    nBeginInvalid(STRING_LEN),  // everything correct... (the invalid area starts beyond the string)
    nEndInvalid  (STRING_LEN)
{
    maList.reserve( 5 );
}

SwWrongList::~SwWrongList()
{
    ClearList();
}

/*************************************************************************
 * SwWrongList* SwWrongList::Clone()
 *************************************************************************/

SwWrongList* SwWrongList::Clone()
{
    SwWrongList* pClone = new SwWrongList( meType );
    pClone->CopyFrom( *this );
    return pClone;
}

/*************************************************************************
 * void SwWrongList::CopyFrom( const SwWrongList& rCopy )
 *************************************************************************/

void SwWrongList::CopyFrom( const SwWrongList& rCopy )
{
    maList = rCopy.maList;
    meType = rCopy.meType;
    nBeginInvalid = rCopy.nBeginInvalid;
    nEndInvalid = rCopy.nEndInvalid;
    for( size_t i = 0; i < maList.size(); ++i )
    {
        if( maList[i].mpSubList )
            maList[i].mpSubList = maList[i].mpSubList->Clone();
    }
}

/*************************************************************************
 * SwWrongList::ClearList()
 *************************************************************************/
void SwWrongList::ClearList()
{
    for ( size_t i = 0; i < maList.size(); ++i)
    {
        if (maList[i].mpSubList)
            delete maList[i].mpSubList;
        maList[i].mpSubList = NULL;
    }
    maList.clear();
}

/*************************************************************************
 * sal_Bool SwWrongList::InWrongWord() gibt den Anfang und die Laenge des
 * Wortes zurueck, wenn es als falsch markiert ist.
 *************************************************************************/
sal_Bool SwWrongList::InWrongWord( xub_StrLen &rChk, xub_StrLen &rLn ) const
{
    MSHORT nPos = GetWrongPos( rChk );
    xub_StrLen nWrPos;
    if( nPos < Count() && ( nWrPos = Pos( nPos ) ) <= rChk )
    {
        rLn = Len( nPos );
        if( nWrPos + rLn <= rChk )
			return sal_False;
		rChk = nWrPos;
		return sal_True;
	}
	return sal_False;
}

/*************************************************************************
 * sal_Bool SwWrongList::Check() liefert den ersten falschen Bereich
 *************************************************************************/
sal_Bool SwWrongList::Check( xub_StrLen &rChk, xub_StrLen &rLn ) const
{
    MSHORT nPos = GetWrongPos( rChk );
    rLn = rLn + rChk;
    xub_StrLen nWrPos;

    if( nPos == Count() )
        return sal_False;

    xub_StrLen nEnd = Len( nPos );
    nEnd = nEnd + ( nWrPos = Pos( nPos ) );
	if( nEnd == rChk )
	{
		++nPos;
		if( nPos == Count()	)
			return sal_False;
		else
		{
            nEnd = Len( nPos );
            nEnd = nEnd + ( nWrPos = Pos( nPos ) );
		}
	}
	if( nEnd > rChk && nWrPos < rLn )
	{
		if( nWrPos > rChk )
			rChk = nWrPos;
		if( nEnd < rLn )
			rLn = nEnd;
		rLn = rLn - rChk;
		return 0 != rLn;
	}
	return sal_False;
}

/*************************************************************************
 * xub_StrLen SwWrongList::NextWrong() liefert die naechste Fehlerposition
 *************************************************************************/

xub_StrLen SwWrongList::NextWrong( xub_StrLen nChk ) const
{
    xub_StrLen nRet;
    xub_StrLen nPos = GetWrongPos( nChk );
    if( nPos < Count() )
    {
        nRet = Pos( nPos );
        if( nRet < nChk && nRet + Len( nPos ) <= nChk )
        {
            if( ++nPos < Count() )
                nRet = Pos( nPos );
            else
                nRet = STRING_LEN;
        }
    }
    else
        nRet = STRING_LEN;
    if( nRet > GetBeginInv() && nChk < GetEndInv() )
        nRet = nChk > GetBeginInv() ? nChk : GetBeginInv();
    return nRet;
}

/*************************************************************************
 *                 MSHORT SwWrongList::GetWrongPos( xub_StrLen nValue )
 *  sucht die erste Position im Array, die groessergleich nValue ist,
 * dies kann natuerlich auch hinter dem letzten Element sein!
 *************************************************************************/

MSHORT SwWrongList::GetWrongPos( xub_StrLen nValue ) const
{
    MSHORT nOben = Count(), nMitte = 0, nUnten = 0;

    if( nOben > 0 )
    {
        // For smart tag lists, we may not use a binary search. We return the
        // position of the first smart tag which coveres nValue
        if ( 0 != maList[0].maType.getLength() || maList[0].mpSubList )
        {
            std::vector<SwWrongArea>::const_iterator aIter = maList.begin();
            while ( aIter != maList.end() )
            {
                const xub_StrLen nSTPos = (*aIter).mnPos;
                const xub_StrLen nSTLen = (*aIter).mnLen;
                if ( nSTPos <= nValue && nValue < nSTPos + nSTLen )
                    break;
                else if ( nSTPos > nValue )
                    break;

                ++aIter;
                ++nUnten;
            }
            return nUnten;
        }

        --nOben;
        while( nUnten <= nOben )
        {
            nMitte = nUnten + ( nOben - nUnten ) / 2;
            xub_StrLen nTmp = Pos( nMitte );
            if( nTmp == nValue )
            {
                nUnten = nMitte;
                break;
            }
            else if( nTmp < nValue )
            {
                if( nTmp + Len( nMitte ) >= nValue )
                {
                    nUnten = nMitte;
                    break;
                }
                nUnten = nMitte + 1;
            }
            else if( nMitte == 0 )
            {
                break;
            }
            else
                nOben = nMitte - 1;
        }
    }

    // nUnten now points to an index i into the wrong list which
    // 1. nValue is inside [ Area[i].pos, Area[i].pos + Area[i].len ] (inkl!!!)
    // 2. nValue < Area[i].pos

    return nUnten;
}

/*************************************************************************
 *                 void SwWrongList::_Invalidate()
 *************************************************************************/

void SwWrongList::_Invalidate( xub_StrLen nBegin, xub_StrLen nEnd )
{
    if ( nBegin < GetBeginInv() )
        nBeginInvalid = nBegin;
    if ( nEnd > GetEndInv() )
        nEndInvalid = nEnd;
}

void SwWrongList::SetInvalid( xub_StrLen nBegin, xub_StrLen nEnd )
{ 
    nBeginInvalid = nBegin;
    nEndInvalid = nEnd;
}


/*************************************************************************
 *                      SwWrongList::Move( xub_StrLen nPos, long nDiff )
 *  veraendert alle Positionen ab nPos um den angegebenen Wert,
 *  wird nach Einfuegen oder Loeschen von Buchstaben benoetigt.
 *************************************************************************/

void SwWrongList::Move( xub_StrLen nPos, long nDiff )
{
    MSHORT i = GetWrongPos( nPos );
    if( nDiff < 0 )
    {
        xub_StrLen nEnd = nPos + xub_StrLen( -nDiff );
        MSHORT nLst = i;
        xub_StrLen nWrPos;
        xub_StrLen nWrLen;
        sal_Bool bJump = sal_False;
        while( nLst < Count() && Pos( nLst ) < nEnd )
            ++nLst;
        if( nLst > i && ( nWrPos = Pos( nLst - 1 ) ) <= nPos )
        {
            nWrLen = Len( nLst - 1 );
            // calculate new length of word
            nWrLen = ( nEnd > nWrPos + nWrLen ) ?
                       nPos - nWrPos :
                       static_cast<xub_StrLen>(nWrLen + nDiff);
            if( nWrLen )
            {
                maList[--nLst].mnLen = nWrLen;
                bJump = sal_True;
            }
        }
        Remove( i, nLst - i );

        if ( bJump )
            ++i;
        if( STRING_LEN == GetBeginInv() )
            SetInvalid( nPos ? nPos - 1 : nPos, nPos + 1 );
        else
        {
            ShiftLeft( nBeginInvalid, nPos, nEnd );
            ShiftLeft( nEndInvalid, nPos, nEnd );
            _Invalidate( nPos ? nPos - 1 : nPos, nPos + 1 );
		}
	}
	else
	{
		xub_StrLen nWrPos;
		xub_StrLen nEnd = nPos + xub_StrLen( nDiff );
		if( STRING_LEN != GetBeginInv() )
		{
			if( nBeginInvalid > nPos )
				nBeginInvalid = nBeginInvalid + xub_StrLen( nDiff );
			if( nEndInvalid >= nPos )
				nEndInvalid = nEndInvalid + xub_StrLen( nDiff );
		}
		// Wenn wir mitten in einem falschen Wort stehen, muss vom Wortanfang
		// invalidiert werden.
        if( i < Count() && nPos >= ( nWrPos = Pos( i ) ) )
        {
            Invalidate( nWrPos, nEnd );
            xub_StrLen nWrLen = Len( i ) + xub_StrLen( nDiff );
            maList[i++].mnLen = nWrLen;
            nWrLen = nWrLen + nWrPos;
            Invalidate( nWrPos, nWrLen );
        }
        else
            Invalidate( nPos, nEnd );
    }
    while( i < Count() )
    {
        const xub_StrLen nTmp = static_cast<xub_StrLen>(nDiff + maList[i].mnPos);
        maList[i++].mnPos = nTmp;
    }
}

/*************************************************************************
 *                      SwWrongList::Fresh
 *
 * For a given range [nPos, nPos + nLen[ and an index nIndex, this function
 * basically counts the number of SwWrongArea entries starting with nIndex
 * up to nPos + nLen. All these entries are removed.
 *************************************************************************/
sal_Bool SwWrongList::Fresh( xub_StrLen &rStart, xub_StrLen &rEnd, xub_StrLen nPos,
                             xub_StrLen nLen, MSHORT nIndex, xub_StrLen nCursorPos )
{
    // length of word must be greater than 0 and cursor position must be outside the word
    sal_Bool bRet = nLen && ( nCursorPos > nPos + nLen || nCursorPos < nPos );

    xub_StrLen nWrPos = 0;
    xub_StrLen nWrEnd = rEnd;
    MSHORT nCnt = nIndex;
    if( nCnt < Count() && ( nWrPos = Pos( nIndex ) ) < nPos )
    {
        if( rStart > nWrPos )
            rStart = nWrPos;
    }

    while( nCnt < Count() && ( nWrPos = Pos( nCnt ) ) < nPos )
        nWrEnd = nWrPos + Len( nCnt++ );

    if( nCnt < Count() && nWrPos == nPos && Len( nCnt ) == nLen )
    {
        ++nCnt;
        bRet = sal_True;
    }
    else
    {
        if( bRet )
        {
            if( rStart > nPos )
                rStart = nPos;
            nWrEnd = nPos + nLen;
        }
    }

    nPos = nPos + nLen;

    if( nCnt < Count() && ( nWrPos = Pos( nCnt ) ) < nPos )
    {
        if( rStart > nWrPos )
            rStart = nWrPos;
    }

    while( nCnt < Count() && ( nWrPos = Pos( nCnt ) ) < nPos )
        nWrEnd = nWrPos + Len( nCnt++ );

    if( rEnd < nWrEnd )
        rEnd = nWrEnd;

    Remove( nIndex, nCnt - nIndex );

    return bRet;
}

void SwWrongList::Invalidate( xub_StrLen nBegin, xub_StrLen nEnd )
{
    if (STRING_LEN == GetBeginInv())
        SetInvalid( nBegin, nEnd );
    else 
        _Invalidate( nBegin, nEnd );
}

sal_Bool SwWrongList::InvalidateWrong( )
{
	if( Count() )
	{
        xub_StrLen nFirst = Pos( 0 );
        xub_StrLen nLast = Pos( Count() - 1 ) + Len( Count() - 1 );
		Invalidate( nFirst, nLast );
		return sal_True;
	}
	else
		return sal_False;
}

SwWrongList* SwWrongList::SplitList( xub_StrLen nSplitPos )
{
    SwWrongList *pRet = NULL;
    MSHORT nLst = 0;
    xub_StrLen nWrPos;
    xub_StrLen nWrLen;
    while( nLst < Count() && Pos( nLst ) < nSplitPos )
        ++nLst;
    if( nLst && ( nWrPos = Pos( nLst - 1 ) )
        + ( nWrLen = Len( nLst - 1 ) ) > nSplitPos )
    {
        nWrLen += nWrPos - nSplitPos;
        maList[--nLst].mnPos = nSplitPos;
        maList[nLst].mnLen = nWrLen;
    }
    if( nLst )
    {
        if( WRONGLIST_GRAMMAR == GetWrongListType() )
            pRet = new SwGrammarMarkUp();
        else
            pRet = new SwWrongList( GetWrongListType() );
        pRet->Insert(0, maList.begin(), ( nLst >= maList.size() ? maList.end() : maList.begin() + nLst ) );
        pRet->SetInvalid( GetBeginInv(), GetEndInv() );
        pRet->_Invalidate( nSplitPos ? nSplitPos - 1 : nSplitPos, nSplitPos );
        Remove( 0, nLst );
    }
    if( STRING_LEN == GetBeginInv() )
        SetInvalid( 0, 1 );
    else
    {
        ShiftLeft( nBeginInvalid, 0, nSplitPos );
        ShiftLeft( nEndInvalid, 0, nSplitPos );
        _Invalidate( 0, 1 );
	}
    nLst = 0;
    while( nLst < Count() )
	{
        nWrPos = maList[nLst].mnPos - nSplitPos;
        maList[nLst++].mnPos = nWrPos;
    }
    return pRet;
}

void SwWrongList::JoinList( SwWrongList* pNext, xub_StrLen nInsertPos )
{
    if (pNext)
    {
        DBG_ASSERT( GetWrongListType() == pNext->GetWrongListType(), "type mismatch with next list" );
    }
    if( pNext )
    {
        sal_uInt16 nCnt = Count();
        pNext->Move( 0, nInsertPos );
        Insert(nCnt, pNext->maList.begin(), pNext->maList.end());

        Invalidate( pNext->GetBeginInv(), pNext->GetEndInv() );
        if( nCnt && Count() > nCnt )
        {
            xub_StrLen nWrPos = Pos( nCnt );
            xub_StrLen nWrLen = Len( nCnt );
            if( !nWrPos )
            {
                nWrPos = nWrPos + nInsertPos;
                nWrLen = nWrLen - nInsertPos;
                maList[nCnt].mnPos = nWrPos;
                maList[nCnt].mnLen = nWrLen;
            }
            if( nWrPos == Pos( nCnt - 1 ) + Len( nCnt - 1 ) )
            {
                nWrLen = nWrLen + Len( nCnt - 1 );
                maList[nCnt - 1].mnLen = nWrLen;
                Remove( nCnt, 1 );
            }
        }
    }
    Invalidate( nInsertPos ? nInsertPos - 1 : nInsertPos, nInsertPos + 1 );
}


void SwWrongList::InsertSubList( xub_StrLen nNewPos, xub_StrLen nNewLen, sal_uInt16 nWhere, SwWrongList* pSubList )
{
    if (pSubList)
    {
        DBG_ASSERT( GetWrongListType() == pSubList->GetWrongListType(), "type mismatch with sub list" );
    }
    std::vector<SwWrongArea>::iterator i = maList.begin();
    if ( nWhere >= maList.size() )
        i = maList.end(); // robust
    else
        i += nWhere;
    maList.insert(i, SwWrongArea( rtl::OUString(), 0, nNewPos, nNewLen, pSubList ) );
}


// New functions: Necessary because SwWrongList has been changed to use std::vector
void SwWrongList::Insert(sal_uInt16 nWhere, std::vector<SwWrongArea>::iterator startPos, std::vector<SwWrongArea>::iterator endPos)
{
    std::vector<SwWrongArea>::iterator i = maList.begin();
    if ( nWhere >= maList.size() )
        i = maList.end(); // robust
    else
        i += nWhere;
    maList.insert(i, startPos, endPos); // insert [startPos, endPos[ before i

    // ownership of the sublist is passed to maList, therefore we have to set the
    // pSubList-Pointers to 0
    while ( startPos != endPos )
    {
        (*startPos).mpSubList = 0;
        ++startPos;
    }
}

void SwWrongList::Remove(sal_uInt16 nIdx, sal_uInt16 nLen )
{
    if ( nIdx >= maList.size() ) return;
    std::vector<SwWrongArea>::iterator i1 = maList.begin();
    i1 += nIdx;

    std::vector<SwWrongArea>::iterator i2 = i1;
    if ( nIdx + nLen >= static_cast<sal_uInt16>(maList.size()) )
        i2 = maList.end(); // robust
    else
        i2 += nLen;

    std::vector<SwWrongArea>::iterator iLoop = i1;
    while ( iLoop != i2 )
    {
        if ( (*iLoop).mpSubList )
            delete (*iLoop).mpSubList;
        ++iLoop;
    }

#if OSL_DEBUG_LEVEL > 1
    const int nOldSize = Count();
    (void) nOldSize;
#endif

    maList.erase(i1, i2);

#if OSL_DEBUG_LEVEL > 1
    ASSERT( Count() + nLen == nOldSize, "SwWrongList::Remove() trouble" )
#endif
}

void SwWrongList::RemoveEntry( xub_StrLen nBegin, xub_StrLen nEnd ) {
    sal_uInt16 nDelPos = 0;
    sal_uInt16 nDel = 0;
    std::vector<SwWrongArea>::iterator aIter = maList.begin();
    while( aIter != maList.end() && (*aIter).mnPos < nBegin )
    {
        ++aIter;
        ++nDelPos;
    }
    if( WRONGLIST_GRAMMAR == GetWrongListType() )
    {
        while( aIter != maList.end() && nBegin < nEnd && nEnd > (*aIter).mnPos )
        {
            ++aIter;
            ++nDel;
        }
    }
    else
    {
        while( aIter != maList.end() && nBegin == (*aIter).mnPos && nEnd == (*aIter).mnPos +(*aIter).mnLen )
        {
            ++aIter;
            ++nDel;
        }
    }
    if( nDel )
        Remove( nDelPos, nDel );
}

bool SwWrongList::LookForEntry( xub_StrLen nBegin, xub_StrLen nEnd ) {
    std::vector<SwWrongArea>::iterator aIter = maList.begin();
    while( aIter != maList.end() && (*aIter).mnPos < nBegin )
        ++aIter;
    if( aIter != maList.end() && nBegin == (*aIter).mnPos && nEnd == (*aIter).mnPos +(*aIter).mnLen )
        return true;
    return false;
}

void SwWrongList::Insert( const rtl::OUString& rType,
                          com::sun::star::uno::Reference< com::sun::star::container::XStringKeyMap > xPropertyBag,
                          xub_StrLen nNewPos, xub_StrLen nNewLen )
{
    std::vector<SwWrongArea>::iterator aIter = maList.begin();

    while ( aIter != maList.end() )
    {
        const xub_StrLen nSTPos = (*aIter).mnPos;

        if ( nNewPos < nSTPos )
        {
            // insert at current position
            break;
        }
        else if ( nNewPos == nSTPos )
        {
            while ( aIter != maList.end() && (*aIter).mnPos == nSTPos )
            {
                const xub_StrLen nSTLen = (*aIter).mnLen;

                if ( nNewLen < nSTLen )
                {
                    // insert at current position
                    break;
                }

                ++aIter;
            }

            break;
        }

        ++aIter;
    }

    maList.insert(aIter, SwWrongArea( rType, xPropertyBag, nNewPos, nNewLen, 0 ) );
}


