/*
 * tkTextPriv.h --
 *
 *	Private implementation for text widget.
 *
 * Copyright (c) 2015-2017 Gregor Cramer
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#ifndef _TKTEXT
# error "do not include this private header file"
#endif


#ifndef _TKTEXTPRIV
#define _TKTEXTPRIV

/*
 * The following struct is private for TkTextBTree.c, but we want fast access to
 * the internal content.
 *
 * The data structure below defines an entire B-tree. Since text widgets are
 * the only current B-tree clients, 'clients' and 'numPixelReferences' are
 * identical.
 */

struct TkBTreeNodePixelInfo;

struct TkTextMyBTree {
    struct Node *rootPtr;
				/* Pointer to root of B-tree. */
    unsigned clients;		/* Number of clients of this B-tree. */
    unsigned numPixelReferences;
				/* Number of clients of this B-tree which care about pixel heights. */
    struct TkBTreeNodePixelInfo *pixelInfoBuffer;
    				/* Buffer of size numPixelReferences used for recomputation of pixel
    				 * information. */
    unsigned stateEpoch;	/* Updated each time any aspect of the B-tree changes. */
    TkSharedText *sharedTextPtr;/* Used to find tagTable in consistency checking code, and to access
    				 * list of all B-tree clients. */
};

#endif /* _TKTEXTPRIV */

#ifdef _TK_NEED_IMPLEMENTATION

#include <assert.h>

#ifdef _MSC_VER
# if _MSC_VER >= 1900
#  define inline __inline
# else
#  define inline
# endif
#elif __STDC_VERSION__ < 199901L
# define inline /* we are not C99 conform */
#endif

/*
 *----------------------------------------------------------------------
 *
 * TkTextIsSpecialMark --
 *
 *	Test whether this is a special mark: "insert", or "current".
 *
 * Results:
 *	Whether this is a special mark.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
bool
TkTextIsSpecialMark(
    const TkTextSegment *segPtr)
{
    assert(segPtr);
    return !!(segPtr->insertMarkFlag | segPtr->currentMarkFlag);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIsPrivateMark --
 *
 *	Test whether this is a private mark, not visible with "inspect"
 *	or "dump". These kind of marks will be used in library/text.tcl.
 *	Furthemore in practice it is guaranteed that this mark has a
 *	unique name.
 *
 * Results:
 *	Whether this is a private mark.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
bool
TkTextIsPrivateMark(
    const TkTextSegment *segPtr)
{
    assert(segPtr);
    return segPtr->privateMarkFlag;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIsNormalMark --
 *
 *	Test whether this is a mark, and it is neither special, nor
 *	private, nor a start/end marker.
 *
 * Results:
 *	Whether this is a normal mark.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
bool
TkTextIsNormalMark(
    const TkTextSegment *segPtr)
{
    assert(segPtr);
    return segPtr->normalMarkFlag;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIsStartEndMarker --
 *
 *	Test whether this is a start/end marker. This must not be a mark,
 *	it can also be a break segment.
 *
 * Results:
 *	Whether this is a start/end marker.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
bool
TkTextIsStartEndMarker(
    const TkTextSegment *segPtr)
{
    assert(segPtr);
    return segPtr->startEndMarkFlag;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIsStableMark --
 *
 *	Test whether this is a mark, and it is neither special, nor
 *	private. Note that also a break segment is interpreted as
 *	a stable mark.
 *
 * Results:
 *	Whether this is a stable mark.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
bool
TkTextIsStableMark(
    const TkTextSegment *segPtr)
{
    return TkTextIsStartEndMarker(segPtr) || TkTextIsNormalMark(segPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIsSpecialOrPrivateMark --
 *
 *	Test whether this is a special mark, or a private mark.
 *
 * Results:
 *	Whether this is a special or private mark.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
bool
TkTextIsSpecialOrPrivateMark(
    const TkTextSegment *segPtr)
{
    assert(segPtr);
    return !!(segPtr->privateMarkFlag | segPtr->insertMarkFlag | segPtr->currentMarkFlag);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIsNormalOrSpecialMark --
 *
 *	Test whether this is a normal mark, or a special mark.
 *
 * Results:
 *	Whether this is a normal or special mark.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
bool
TkTextIsNormalOrSpecialMark(
    const TkTextSegment *segPtr)
{
    return TkTextIsNormalMark(segPtr) || TkTextIsSpecialMark(segPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIsDeadPeer --
 *
 *	Test whether given widget is dead, this means that the start
 *	index is on last line. If it is dead, then this peer will not
 *	have an insert mark.
 *
 * Results:
 *	Returns whether given peer is dead.
 *
 * Side effects:
 *	None
 *
 *----------------------------------------------------------------------
 */

inline
bool
TkTextIsDeadPeer(
    const TkText *textPtr)
{
    return !textPtr->startMarker->sectionPtr->linePtr->nextPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeLinePixelInfo --
 *
 *	Return widget pixel information for specified line.
 *
 * Results:
 *	The pixel information of this line for specified widget.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkTextPixelInfo *
TkBTreeLinePixelInfo(
    const TkText *textPtr,
    TkTextLine *linePtr)
{
    assert(textPtr);
    assert(textPtr->pixelReference >= 0);
    assert(linePtr);

    return linePtr->pixelInfo + textPtr->pixelReference;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeGetStartLine --
 *
 *	This function returns the first line for this text widget.
 *
 * Results:
 *	The first line in this widget.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkTextLine *
TkBTreeGetStartLine(
    const TkText *textPtr)
{
    assert(textPtr);
    return textPtr->startMarker->sectionPtr->linePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeGetLastLine --
 *
 *	This function returns the last line for this text widget.
 *
 * Results:
 *	The last line in this widget.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkTextLine *
TkBTreeGetLastLine(
    const TkText *textPtr)
{
    TkTextLine *endLine;

    assert(textPtr);
    endLine = textPtr->endMarker->sectionPtr->linePtr;
    return endLine->nextPtr ? endLine->nextPtr : endLine;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeGetShared --
 *
 *	Get the shared resource for given tree.
 *
 * Results:
 *	The shared resource.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkSharedText *
TkBTreeGetShared(
    TkTextBTree tree)		/* Return shared resource of this tree. */
{
    return ((struct TkTextMyBTree *) tree)->sharedTextPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeIncrEpoch --
 *
 *	Increment the epoch of the tree.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Increment the epoch number.
 *
 *----------------------------------------------------------------------
 */

inline
unsigned
TkBTreeIncrEpoch(
    TkTextBTree tree)		/* Tree to increment epoch. */
{
    return ((struct TkTextMyBTree *) tree)->stateEpoch += 1;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeEpoch --
 *
 *	Return the epoch for the B-tree. This number is incremented any time
 *	anything changes in the tree.
 *
 * Results:
 *	The epoch number.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
unsigned
TkBTreeEpoch(
    TkTextBTree tree)		/* Tree to get epoch for. */
{
    return ((struct TkTextMyBTree *) tree)->stateEpoch;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeGetRoot --
 *
 *	Return the root node of the B-Tree.
 *
 * Results:
 *	The root node of the B-Tree.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
struct Node *
TkBTreeGetRoot(
    TkTextBTree tree)		/* Tree to get root node for. */
{
    return ((struct TkTextMyBTree *) tree)->rootPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeNextLine --
 *
 *	Given an existing line in a B-tree, this function locates the next
 *	line in the B-tree, regarding the end line of this widget.
 *	B-tree.
 *
 * Results:
 *	The return value is a pointer to the line that immediately follows
 *	linePtr, or NULL if there is no such line.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkTextLine *
TkBTreeNextLine(
    const TkText *textPtr,	/* Next line in the context of this client, can be NULL. */
    TkTextLine *linePtr)	/* Pointer to existing line in B-tree. */
{
    return textPtr && linePtr == TkBTreeGetLastLine(textPtr) ? NULL : linePtr->nextPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreePrevLine --
 *
 *	Given an existing line in a B-tree, this function locates the previous
 *	line in the B-tree, regarding the start line of this widget.
 *
 * Results:
 *	The return value is a pointer to the line that immediately preceeds
 *	linePtr, or NULL if there is no such line.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkTextLine *
TkBTreePrevLine(
    const TkText *textPtr,	/* Relative to this client of the B-tree, can be NULL. */
    TkTextLine *linePtr)	/* Pointer to existing line in B-tree. */
{
    return textPtr && linePtr == TkBTreeGetStartLine(textPtr) ? NULL : linePtr->prevPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreePrevLogicalLine --
 *
 *	Given a line, this function is searching for the previous logical line,
 *	which don't has a predecessing line with elided newline. If the search
 *	reaches the start of the text, then the first line will be returned,
 *	even if it's not a logical line (the latter can only happen in peers
 *	with restricted ranges).
 *
 * Results:
 *	The return value is the previous logical line, in most cases this
 *	will be simply the previous line.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkTextLine *
TkBTreePrevLogicalLine(
    const TkSharedText* sharedTextPtr,
    const TkText *textPtr,	/* can be NULL */
    TkTextLine *linePtr)
{
    assert(linePtr);
    assert(linePtr != (textPtr ?
	    TkBTreeGetStartLine(textPtr) : sharedTextPtr->startMarker->sectionPtr->linePtr));

    return TkBTreeGetLogicalLine(sharedTextPtr, textPtr, linePtr->prevPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeCountLines --
 *
 *	This function counts the number of lines inside a given range.
 *
 * Results:
 *	The return value is the number of lines inside a given range.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
unsigned
TkBTreeCountLines(
    const TkTextBTree tree,
    const TkTextLine *linePtr1,	/* Start counting at this line. */
    const TkTextLine *linePtr2)	/* Stop counting at this line (don't count this line). */
{
    assert(TkBTreeLinesTo(tree, NULL, linePtr1, NULL) <= TkBTreeLinesTo(tree, NULL, linePtr2, NULL));

    if (linePtr1 == linePtr2) {
	return 0; /* this is catching a frequent case */
    }
    if (linePtr1->nextPtr == linePtr2) {
	return 1; /* this is catching a frequent case */
    }

    return TkBTreeLinesTo(tree, NULL, linePtr2, NULL) - TkBTreeLinesTo(tree, NULL, linePtr1, NULL);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexSetPeer --
 *
 *	Set this index to the start of the line.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
void
TkTextIndexSetPeer(
    TkTextIndex *indexPtr,
    TkText *textPtr)
{
    assert(indexPtr->tree);
    
    indexPtr->textPtr = textPtr;
    indexPtr->priv.lineNoRel = -1;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexGetShared --
 *
 *	Get the shared resource of this index.
 *
 * Results:
 *	The shared resource.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkSharedText *
TkTextIndexGetShared(
    const TkTextIndex *indexPtr)
{
    assert(indexPtr);
    assert(indexPtr->tree);
    return TkBTreeGetShared(indexPtr->tree);
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeGetTags --
 *
 *	Return information about all of the tags that are associated with a
 *	particular character in a B-tree of text.
 *
 * Results:
 *      The return value is the root of the tag chain, containing all tags
 *	associated with the character at the position given by linePtr and ch.
 *	If there are no tags at the given character then a NULL pointer is
 *	returned.
 *
 * Side effects:
 *	The attribute nextPtr of TkTextTag will be modified for any tag.
 *
 *----------------------------------------------------------------------
 */

inline
TkTextTag *
TkBTreeGetTags(
    const TkTextIndex *indexPtr)/* Indicates a particular position in the B-tree. */
{
    const TkTextSegment *segPtr = TkTextIndexGetContentSegment(indexPtr, NULL);
    return TkBTreeGetSegmentTags(TkTextIndexGetShared(indexPtr), segPtr, indexPtr->textPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexGetLine --
 *
 *	Get the line pointer of this index.
 *
 * Results:
 *	The line pointer.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkTextLine *
TkTextIndexGetLine(
    const TkTextIndex *indexPtr)/* Indicates a particular position in the B-tree. */
{
    assert(indexPtr->priv.linePtr);
    assert(indexPtr->priv.linePtr->parentPtr); /* expired? */

    return indexPtr->priv.linePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexSetToLastChar2 --
 *
 *	Set the new line pointer, and set this index to one before the
 *	end of the line.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
void
TkTextIndexSetToLastChar2(
    TkTextIndex *indexPtr,	/* Pointer to index. */
    TkTextLine *linePtr)	/* Pointer to line. */
{
    assert(indexPtr->tree);
    assert(linePtr);
    assert(linePtr->parentPtr); /* expired? */

    indexPtr->priv.linePtr = linePtr;
    indexPtr->priv.lineNo = -1;
    indexPtr->priv.lineNoRel = -1;
    TkTextIndexSetToLastChar(indexPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexGetSegment --
 *
 *	Get the pointer to stored segment.
 *
 * Results:
 *	Pointer to the stored segment, this can be NULL.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
TkTextSegment *
TkTextIndexGetSegment(
    const TkTextIndex *indexPtr)/* Pointer to index. */
{
    TkTextSegment *segPtr;

    assert(indexPtr->tree);
    assert(indexPtr->priv.linePtr);
    assert(indexPtr->priv.linePtr->parentPtr); /* expired? */

    segPtr = indexPtr->priv.segPtr;

    if (!segPtr
	    || (indexPtr->priv.isCharSegment
		&& TkBTreeEpoch(indexPtr->tree) != indexPtr->stateEpoch)) {
	return NULL;
    }

    assert(!segPtr || segPtr->typePtr);    /* expired? */
    assert(!segPtr || segPtr->sectionPtr); /* linked? */
    assert(!segPtr || segPtr->sectionPtr->linePtr == indexPtr->priv.linePtr);

    return segPtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexSave --
 *
 *	Makes the index robust, so that it can be rebuild after modifications.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
void
TkTextIndexSave(
    TkTextIndex *indexPtr)
{
    TkTextIndexGetLineNumber(indexPtr, indexPtr->textPtr);
    TkTextIndexGetByteIndex(indexPtr);
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexSameLines --
 *
 *	Test whether both given indicies are referring the same line.
 *
 * Results:
 *	Return true if both indices are referring the same line, otherwise
 *	false will be returned.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
bool
TkTextIndexSameLines(
    const TkTextIndex *indexPtr1,	/* Pointer to index. */
    const TkTextIndex *indexPtr2)	/* Pointer to index. */
{
    assert(indexPtr1->priv.linePtr);
    assert(indexPtr2->priv.linePtr);
    assert(indexPtr1->priv.linePtr->parentPtr); /* expired? */
    assert(indexPtr2->priv.linePtr->parentPtr); /* expired? */

    return indexPtr1->priv.linePtr == indexPtr2->priv.linePtr;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexSetEpoch --
 *
 *	Update epoch of given index, don't clear the segment pointer.
 *	Use this function with care, it must be ensured that the
 *	segment pointer is still valid.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
void
TkTextIndexUpdateEpoch(
    TkTextIndex *indexPtr,
    unsigned epoch)
{
    assert(indexPtr->priv.linePtr);
    assert(indexPtr->priv.linePtr->parentPtr); /* expired? */

    indexPtr->stateEpoch = epoch;
    indexPtr->priv.lineNo = -1;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexInvalidate --
 *
 *	Clear position attributes: segPtr, and byteIndex.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The given index will be in an invalid state, the TkIndexGet*
 *	functions cannot be used.
 *
 *----------------------------------------------------------------------
 */

inline
void
TkTextIndexInvalidate(
    TkTextIndex *indexPtr)	/* Pointer to index. */
{
    indexPtr->priv.segPtr = NULL;
    indexPtr->priv.byteIndex = -1;
}

/*
 *----------------------------------------------------------------------
 *
 * TkTextIndexSetEpoch --
 *
 *	Set epoch of given index, and clear the segment pointer if
 *	the new epoch is different from last epoch.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
void
TkTextIndexSetEpoch(
    TkTextIndex *indexPtr,
    unsigned epoch)
{
    assert(indexPtr->priv.linePtr);
    assert(indexPtr->priv.linePtr->parentPtr); /* expired? */

    if (indexPtr->stateEpoch != epoch) {
	indexPtr->stateEpoch = epoch;
	indexPtr->priv.segPtr = NULL;
	indexPtr->priv.lineNo = -1;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * TkBTreeGetNumberOfDisplayLines --
 *
 *	Return the current number of display lines. This is the number
 *	of lines known by the B-Tree (not the number of lines known
 *	by the display stuff).
 *
 *	We are including the implementation into this private header file,
 *	because it uses some facts only known by the display stuff.
 *
 * Results:
 *	Returns the current number of display lines (known by B-Tree).
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

inline
int
TkBTreeGetNumberOfDisplayLines(
    const TkTextPixelInfo *pixelInfo)
{
    const TkTextDispLineInfo *dispLineInfo;

    if (pixelInfo->height == 0) {
	return 0;
    }
    if (!(dispLineInfo = pixelInfo->dispLineInfo)) {
	return 1;
    }
    if (pixelInfo->epoch & 0x80000000) {
	/*
	 * This will return the old number of display lines, because the
	 * computation of the corresponding logical line is currently in
	 * progress, and unfinished.
	 */
	return dispLineInfo->entry[dispLineInfo->numDispLines].pixels;
    }
    return dispLineInfo->numDispLines;
}

#if TCL_UTF_MAX <= 4 && TK_MAJOR_VERSION == 8 && TK_MINOR_VERSION < 7
/*
 *----------------------------------------------------------------------
 *
 * TkUtfToUniChar --
 *
 *	Only needed for backporting, see source of version 8.7 about
 *	this function.
 *
 *	IMO this function is only a bad hack, Tcl should provide the
 *	appropriate functionality.
 *
 *----------------------------------------------------------------------
 */

inline
int
TkUtfToUniChar(const char *src, int *chPtr)
{
    Tcl_UniChar ch;
    int result = Tcl_UtfToUniChar(src, &ch);
    *chPtr = ch;
    return result;
}

#endif /* end of backport for 8.6/8.5 */

#undef _TK_NEED_IMPLEMENTATION
#endif /* _TK_NEED_IMPLEMENTATION */
/* vi:set ts=8 sw=4: */