/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: txtcntnr.cpp,v 1.1.2.1 2004/07/09 01:50:20 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

/////////////////////////////////////////////////////////////////////////////
//
//  TXTCNTNR.CPP
//
//  TextContainer class implementation.
//
//	A class TextContainer object holds the actual buffer of text to be
//	rendered.
//
//  TextContainerList class implementation.
//
//	A class TextContainerList object is a list of TextContainer objects.
//

#include "hxtypes.h"
#include "hxstack.h"
#include "hxslist.h"

#include "rt_types.h" //for _CHAR
#include "rt_string.h"//for stringCopy() and stringAppend().

#include "fontdefs.h"  //(broke defs out of txtattrb.h)
#include "fontinfo.h"  //For ConvertAcrossCharsets()

#include "txtattrb.h" //for class TextAttributes.
#include "txtcntnr.h"

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static char HX_THIS_FILE[] = __FILE__;
#endif


/////////////////////////////////////////////////////////////////////////////
//class TextContainer methods:  (TextContainerList methods are further down)
/////////////////////////////////////////////////////////////////////////////
//
TextContainer::TextContainer(_CHAR* pText, ULONG32 textLen)
	: m_pBuffer(NULL)
	, m_bTextShouldBeDrawn(FALSE)
	, m_bFinalDrawWasDone(FALSE)
	, m_ulTimeOfLastClearTag(0L)
	, m_lAmountLoopedSoFar(0L)
{
    m_textHasChanged = overwriteText(pText, textLen);
    if(m_textHasChanged)
    {
	m_textLengthPlus1=textLen+1;
    }
    else
    {
	m_textLengthPlus1=0L;
    }
} 


/////////////////////////////////////////////////////////////////////////////
//
void TextContainer::deleteBuffer()
{
    if(m_pBuffer)
    {
	delete [] m_pBuffer;
	m_pBuffer = NULL;

	m_textLengthPlus1 = 0L;
    }
} 


/////////////////////////////////////////////////////////////////////////////
//
BOOL TextContainer::appendText(_CHAR* pMoreText, ULONG32 moreTextLen) 
{
    ULONG32 lenNeeded;
    BOOL append;
    if(moreTextLen<1)
    {
	return FALSE;
    }
    if(!getTextLengthPlus1()  ||  !m_pBuffer)
    {
	lenNeeded = moreTextLen+1;
	append = FALSE;
    }
    else 
    {	/*  Delete current buffer and replace with one long enough for
	 *  new text, too:  */
	lenNeeded = getTextLengthPlus1(); //includes terminating '\0' char.
	lenNeeded += moreTextLen;
	append = TRUE;
    }
    _CHAR* pTmpBuffer = new _CHAR[lenNeeded];
    HX_ASSERT_VALID_PTR(pTmpBuffer);
    if(!pTmpBuffer)
    {
	return(FALSE); //returns FALSE.
    }

    stringCopy(pTmpBuffer, m_pBuffer, m_textLengthPlus1-1);
    if(append)
    {
	stringAppend(pTmpBuffer, lenNeeded-1, pMoreText, moreTextLen);
    }
    deleteBuffer();
    m_pBuffer = pTmpBuffer;
    m_textLengthPlus1 = lenNeeded;
    m_textHasChanged=TRUE;
    return TRUE;
} 


/////////////////////////////////////////////////////////////////////////////
//
BOOL TextContainer::overwriteText(_CHAR* pNewText, ULONG32 len) 
{
    if(!pNewText  ||  0L == len)
    {
	return FALSE;
    }
    deleteBuffer();
    m_pBuffer = new _CHAR[len+1];
    HX_ASSERT_VALID_PTR(m_pBuffer); 
    if(!m_pBuffer)
    {
	return(FALSE);
    }
    stringCopy(m_pBuffer, pNewText, len);
    m_textLengthPlus1 = len+1;
    m_textHasChanged=TRUE;
    return TRUE;
}


    

/////////////////////////////////////////////////////////////////////////////
//
//This function converts characters from mac-roman to iso-8859-1
// (us-ascii) or vice versa, depending on both the client operating
// system and the active charset of *this.  So, if we're rendering on a
// Macintosh, the following RealText as authored in Windows Notepad
// would do as it says:
//	<font charset="iso-8859-1">"ä" will renderer as an a with an umlaut.
//		which is 0xE4 in the iso-8859-1 charset.
//	<font charset="mac-roman">"ä" will renderer as "per thousand" sign,
//		which is 0xE4 in the mac-roman charset.
//
// This should only be called from the renderer so it doesn't get double-
// translated which is why the second parameter is mandatory:
//
HX_RESULT TextContainer::ConvertNativeCharsetChars(
	BOOL bRendererIsCallingThis, UINT16 uiMaxSupportedLevel)
{
    _CHAR* pTmp = m_pBuffer;
    if (!pTmp)
    {
	return HXR_BUFFERTOOSMALL;
    }
    ULONG32 ulCharset = getFontCharset();
    return ConvertAcrossCharacterSets(pTmp, ulCharset,
	    bRendererIsCallingThis, uiMaxSupportedLevel);
}




/////////////////////////////////////////////////////////////////////////////
//class TextContainerList methods:
/////////////////////////////////////////////////////////////////////////////
//
LISTPOSITION TextContainerList::GetEndPosition()
{
    if (GetCount() > 0)
    {
	return (TextContainer*)GetTailPosition();
    }
    else
    {
	return NULL;
    }
}


/////////////////////////////////////////////////////////////////////////////
//
LISTPOSITION TextContainerList::GetStartPosition()
{
    if (GetCount() > 0)
    {
	return (TextContainer*)GetHeadPosition();
    }
    else
    {
	return NULL;
    }
}


/////////////////////////////////////////////////////////////////////////////
//
TextContainer* TextContainerList::end() //returns NULL if list is empty.
{
    if (GetCount() > 0)
    {
	return (TextContainer*)GetTail();
    }
    else
    {
	return NULL;
    }
}


/////////////////////////////////////////////////////////////////////////////
//
TextContainer* TextContainerList::start() //returns NULL if list is empty.
{
    if (GetCount() > 0)
    {
	return (TextContainer*)GetHead();
    }
    else
    {
	return NULL;
    }
}


/////////////////////////////////////////////////////////////////////////////
//
BOOL TextContainerList::insertAtEndOfList(TextContainer* tc)
{
    HX_ASSERT_VALID_PTR(tc);
    if(NULL == tc)
    {
	return FALSE;
    }
    else
    {
	LISTPOSITION lpos = AddTail(tc);
	return (lpos!=NULL);
    }
}


/////////////////////////////////////////////////////////////////////////////
//
ULONG32 TextContainerList::flush() //Returns number of nodes deleted.
{
    ULONG32 numDeleted = 0L;
    while(nodeCount())
    {
	TextContainer* pTCHead = (TextContainer*)CHXSimpleList::RemoveHead();
	HX_ASSERT_VALID_PTR(pTCHead);
	if(pTCHead) 
	{
	    pTCHead->deleteBuffer();

	    delete pTCHead;
	    pTCHead = NULL;
	    numDeleted++;
	}
    }

    return numDeleted;
}


/////////////////////////////////////////////////////////////////////////////
//
//  If <CLEAR> tag is sent, effective at time t, then this function is
//  called to change all in this list whose m_endTime is > t;
//  Returns the number of nodes in the list whose endTimes were reduced to t:
//
ULONG32 TextContainerList::MarkAllForClear(BOOL bIsLiveSource)
{
    ULONG32 listSize = size();
    ULONG32 numTCsWhoseEndTimesWereReduced = 0L;

    if(listSize)
    {
	LISTPOSITION pos = GetStartPosition();

	while(pos)
	{
	    TextContainer* pTC = (TextContainer*)GetAt(pos);

	    HX_ASSERT_VALID_PTR(pTC);
	    if(pTC)
	    {
		if(pTC->MarkForClear(m_ulLatestSentTimeToRender,
			bIsLiveSource))
		{
		    numTCsWhoseEndTimesWereReduced++;
		}
	    }

	    GetNext(pos);
	} //end "while(pos)".
    } //end "if(listSize)".	
    return numTCsWhoseEndTimesWereReduced;
}


/////////////////////////////////////////////////////////////////////////////
//
////Added the following function so file format can kill
// all in this list except the few that make up the last "line" of text;
// this keeps the file format from using up more & more memory over time:
// Returns the number of nodes in the list that were deleted:
//
ULONG32 TextContainerList::deleteAllTCsUpToLastLine()
{
    ULONG32 listSize = size();
    ULONG32 numTCsDeleted = 0L;

    if(listSize)
    {
	//start @ tail so GetPrev() is always valid after a RemoveAt(pos):
	LISTPOSITION pos = GetEndPosition();
	LISTPOSITION posOfStartOfNewLine = pos;
	BOOL bStartOfFinalLineWasFound = FALSE;

	while(pos)
	{
	    TextContainer* pTC = (TextContainer*)GetAt(pos);

	    HX_ASSERT_VALID_PTR(pTC);
	    if(pTC)
	    {
		if(pTC->isStartOfNewLine()
			//Added this check because tickertapes
			// and other crawlrate-only types can have fake new
			// lines just to break them up into manageable parts:
			||  pTC->isFakeNewLine())
		{
		    posOfStartOfNewLine = pos;
		    bStartOfFinalLineWasFound = TRUE;
		    break; //start of final line was found.
		}
	    }

	    GetPrev(pos);
	} //end "while(pos)".
    
	if(!bStartOfFinalLineWasFound)
	{
	    //assume the whole thing is the last line:
	    return 0L;
	}

	//Now, delete all the remaining (earlier) TCs:
	if(pos)
	{
	    GetPrev(pos);
	}
	while(pos)
	{
	    TextContainer* pTC = (TextContainer*)GetAt(pos);

	    HX_ASSERT_VALID_PTR(pTC);
	    if(pTC)
	    {
		TextContainer* pTempTC = (TextContainer*)GetAt(pos);

		pos = RemoveAt(pos);//returns pos of NEXT (or prev if at end)

		HX_ASSERT_VALID_PTR(pTempTC);//Fixes mem leak:
		if(pTempTC)  
		{
		    delete pTempTC;
		    pTempTC = NULL;
		}
		
		LISTPOSITION endPos = GetEndPosition();
		if(pos != endPos  ||  pos == posOfStartOfNewLine)
		{
		    GetPrev(pos);
		}
		//else we're already at previous node.

		numTCsDeleted++;
		continue;
	    }

	    GetPrev(pos);
	} //end "while(pos)".
    } //end "if(listSize)".	
    return numTCsDeleted;   
}


/////////////////////////////////////////////////////////////////////////////
//
void TextContainerList::SetLatestSentTimeToRender(ULONG32 t)
{
    m_ulLatestSentTimeToRender = t;
}

/////////////////////////////////////////////////////////////////////////////
//
void TextContainerList::SetLatestSentTimeToStopRendering(ULONG32 t)
{
    m_ulLatestSentTimeToStopRendering = t;
}

/////////////////////////////////////////////////////////////////////////////
//
ULONG32 TextContainerList::GetLatestSentTimeToRender()
{
    return m_ulLatestSentTimeToRender;
}

/////////////////////////////////////////////////////////////////////////////
//
ULONG32 TextContainerList::GetLatestSentTimeToStopRendering()
{
    return m_ulLatestSentTimeToStopRendering;
}


