/********************************************************************
 *                                                                  *
 *  MODULE    :  WVTXTBLK.C                                         *
 *                                                                  *
 *  PURPOSE   : This file contains Memory Management routines for   *
 *              text blocks                                                   *                                 
 *                                                                  *
 *              The TypTextBlock struct is defined in wvglob.h      *
 *                                                                  *
 * Author: John S. Cooper (jcooper@planetz.com)                     *
 *   Date: Sept 30, 1993                                            *
 ********************************************************************/

/* 
 * $Id: wvtxtblk.c 1.10 1996/08/13 04:57:05 dumoulin Exp $
 */

#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define BLOCK_NUM_LINES_INC     64L
#define TEXT_SIZE_INC           1024L

// Private functions
BOOL IncreaseTextSize (TypTextBlock * textBlock, unsigned long inc);
BOOL AllocTextBlock (TypTextBlock * textBlock, unsigned long bytes, unsigned long lines);

/* ------------------------------------------------------------------------
 *    Mem Management routines for text block
 *  This is my simple alternative M. Riordan's Doc paradigm
 */
BOOL AllocTextBlock (TypTextBlock * textBlock, unsigned long bytes, unsigned long lines)
{
	textBlock->maxLines = lines;
	if ((textBlock->offset = (unsigned long huge *) GlobalAllocPtr (GMEM_MOVEABLE, textBlock->maxLines * sizeof (unsigned long huge))) == NULL) 
	{
		//      WVMessageBox (textBlock->hTextWnd, "Memory Allocation Failure", "Text Block Create-Offsets", MB_OK | MB_ICONHAND);
		return (FAIL);
	}
	textBlock->numLines = 0;

	textBlock->maxBytes = bytes;
	if ((textBlock->text = (char huge *) GlobalAllocPtr (GMEM_MOVEABLE, textBlock->maxBytes * sizeof (char huge))) == NULL) 
	{
		//      WVMessageBox (textBlock->hTextWnd, "Memory Allocation Failure", "Text Block Create-Text", MB_OK | MB_ICONHAND);
		GlobalFreePtr (textBlock->offset);
		return (FAIL);
	}
	textBlock->numBytes = 0;
	textBlock->maxLineLen = 0;
	return (SUCCESS);
}

/* InitTextBlockWithSize will not give an error message if it can't allocate
 * the data.  It will return NULL, so the caller can provide an alternate
 * solution (i.e. text-block-to-disk)
 */
TypTextBlock * InitTextBlockWithSize (HWND hParentWnd, unsigned long bytes, unsigned long lines)
{
	TypTextBlock *textBlock;

	if ((textBlock = (TypTextBlock *) GlobalAllocPtr (GMEM_MOVEABLE, sizeof (TypTextBlock))) == NULL) 
	{
		WVMessageBox (hParentWnd, "Memory Allocation Failure", "Text Block Create-Block", MB_OK | MB_ICONHAND);
		return (NULL);
	}
	textBlock->hTextWnd = hParentWnd;
	textBlock->IsBusy = TRUE;
	if (AllocTextBlock (textBlock, bytes, lines) == FAIL) 
	{
		GlobalFreePtr (textBlock);
		return (NULL);
	}

	return (textBlock);
}

TypTextBlock * InitTextBlock (HWND hParentWnd)
{
	TypTextBlock *textBlock;

	if ((textBlock = InitTextBlockWithSize (hParentWnd, TEXT_SIZE_INC, BLOCK_NUM_LINES_INC)) == NULL) 
	{
		WVMessageBox (textBlock->hTextWnd, "Memory Allocation Failure", "Text Block Creation", MB_OK | MB_ICONHAND);
		return (NULL);
	}

	return (textBlock);
}

void ResetTextBlock (TypTextBlock * textBlock)
{
	GlobalFreePtr (textBlock->text);
	GlobalFreePtr (textBlock->offset);
	textBlock->offset = NULL;
	textBlock->text = NULL;
	AllocTextBlock (textBlock, TEXT_SIZE_INC, BLOCK_NUM_LINES_INC);
}

void FreeTextBlock (TypTextBlock * textBlock)
{
	GlobalFreePtr (textBlock->text);
	GlobalFreePtr (textBlock->offset);
	GlobalFreePtr (textBlock);
}

BOOL AddLineToTextBlock (TypTextBlock * textBlock, const char *line)
{
	size_t len;
	unsigned long nextSeg;

	if (line == NULL || *line == '\0')
		return (SUCCESS);

	// manage offset list
	if (textBlock->numLines + 1 >= textBlock->maxLines) 
	{
		textBlock->maxLines += BLOCK_NUM_LINES_INC;
		if ((textBlock->offset = (unsigned long huge *) GlobalReAllocPtr (textBlock->offset,
			 textBlock->maxLines * sizeof (unsigned long huge), GMEM_MOVEABLE)) == NULL) 
		{
			WVMessageBox (textBlock->hTextWnd, "Memory Allocation Failure", "Text Block Add-Offsets", MB_OK | MB_ICONHAND);
			return (FAIL);
		}
	}

	// manage text string
	len = strlen (line) + 1;              // include the NULL in the count

	if (textBlock->numBytes + len >= textBlock->maxBytes)
		if (IncreaseTextSize (textBlock, max (len, TEXT_SIZE_INC)) == FAIL)
			return (FAIL);
//MDS this is Soooo Win3.x... TODO: fix
	// check if this line would cause text to cross a 64k segment
	// if so, fill in with nulls, and start new line at next seg 
	// WHY DO I HAVE TO DO THIS?!?!? Windows is supposed to take
	// care of this isn't it?!  memcpy is supposed to work with huge
	nextSeg = ((textBlock->numBytes + 65536L) / 65536L) * 65536L;
	if (textBlock->numBytes + len > nextSeg) 
	{
		if (nextSeg + len > textBlock->maxBytes)        // do we need to add some space?

		if (IncreaseTextSize (textBlock, max (nextSeg - textBlock->numBytes, TEXT_SIZE_INC)) == FAIL)
			return (FAIL);
		while (textBlock->numBytes < nextSeg)
			textBlock->text[(textBlock->numBytes)++] = '\0';
	}
	// set pointer offset to this new string
	textBlock->offset[textBlock->numLines] = textBlock->numBytes;

	// copy it in, including the NULL
	memcpy ((char huge *) &(textBlock->text[textBlock->numBytes]), line, len);

	// increment counters
	textBlock->numLines++;
	textBlock->numBytes += (unsigned long) len;

	// save max string len for use in scroll bar ranges in status wnd
	if (len > textBlock->maxLineLen)
		textBlock->maxLineLen = len;

	return (SUCCESS);
}

BOOL IncreaseTextSize (TypTextBlock * textBlock, unsigned long inc)
{
	textBlock->maxBytes += inc;
	if ((textBlock->text = (char huge *) GlobalReAllocPtr (textBlock->text,
		textBlock->maxBytes * sizeof (char huge), GMEM_MOVEABLE)) == NULL) 
	{
		WVMessageBox (textBlock->hTextWnd, "Memory Allocation Failure", "Text Block Add-Text", MB_OK | MB_ICONHAND);
		return (FAIL);
	}
	return (SUCCESS);
}

/* ------------------------------------------------------------------------
 * AddEndedLineToAttach
 * Front end to AddLineToTextBlock which sets the end-of-line chars
 * To add blank line, send line parameter as ""
 */
BOOL AddEndedLineToTextBlock (TypTextBlock * textBlock, char *line)
{
	char *sNew, *ptr;
	unsigned int len;
	BOOL result;

	len = strcspn (line, "\n\r");
	ptr = sNew = (char *) GlobalAllocPtr (GMEM_MOVEABLE, (len + 4) * sizeof (char));
	if (ptr == NULL) 
	{
		return (FAIL);
	}

	if (len > 0)
		memcpy (ptr, line, len);

	ptr[len++] = '\r';
	ptr[len++] = '\n';
	ptr[len] = '\0';

	result = AddLineToTextBlock (textBlock, sNew);
	GlobalFreePtr (sNew);
	return (result);
}

/* ------------------------------------------------------------------------
 * ReadFileToTextBlock
 * Reads text file into text block
 * File must be all ASCII
 */
BOOL ReadFileToTextBlock (HWND hParentWnd, TypTextBlock * block, char *fileName)
{
	register int i;
	TypMRRFile *MRRFile;
	HFILE hFile;
	int numRead, result;
	char mybuf[TEMPBUFSIZE];
	BOOL leadingBlanks;

	hFile = MRROpenFile (fileName, OF_READ, &MRRFile);
	if (hFile <= 0) 
	{
		sprintf (str, "Could not open file %s for read", fileName);
		WVMessageBox (hParentWnd, str, "Open File Error", MB_OK | MB_ICONHAND);
		return (FAIL);
	}
	else 
	{
		CodingState = ATTACH_READFILE;
		leadingBlanks = TRUE;
		result = SUCCESS;
		while ((numRead = MRRReadLine (MRRFile, mybuf, TEMPBUFSIZE)) > -1) 
		{
			mybuf[numRead] = '\0';
			if (leadingBlanks) 
			{          // skip leading blank lines

				if (IsBlankStr (mybuf))
					continue;
				else
					leadingBlanks = FALSE;
			}

			for (i = 0; i < numRead; i++) 
			{
				unsigned char c = (unsigned char) mybuf[i];
				if ( (c < 0x20 || c == 0x7f ) && ! isspace(c)) 
				{
					sprintf (str, "File %s appears to contain non-ASCII data.\nBinary files must be encoded for attachment", fileName);
					WVMessageBox (hParentWnd, str, "Non-ASCII File Error", MB_OK | MB_ICONINFORMATION);
					result = FAIL;
					goto endRead;
				}
			}
			if (AddLineToTextBlock (block, mybuf) == FAIL)
				break;
			if (hCodedBlockWnd && currentCoded) 
			{
				currentCoded->numLines++;
				currentCoded->numBytes += numRead;
				if (currentCoded->numLines % STATUS_UPDATE_FREQ == 0)
					UpdateBlockStatus ();
			}
		}
	}

endRead:;
	MRRCloseFile (MRRFile);
	CodingState = INACTIVE;
	return (result);
}

/* ------------------------------------------------------------------------
 * ReplaceLineInTextBlock
 * This is ugly, and really inefficient of the textblock is big
 * 1. If newline has length > oldline, then make sure text array has enough
 *    room, then shift right the string starting at the end of oldline by 
 *    n characters where n is len(newline)-len(oldline)
 * 2. If newline has length < oldline then shift the string starting at the
 *    end of oldline by n characters where n is len(oldline)-len(newline)
 * 3. If oldline and newline are same length, do no shifting
 * 4. Then copy in the new string
 * 5. Adjust all affected offsets (the lines after this one) by size diff
 * 6. God this is ugly, hope we don't use it much!
 */
BOOL ReplaceLineInTextBlock (TypTextBlock * textBlock, unsigned long num, char *line)
{
	size_t newLen, oldLen;
	int sizeDiff;
	register char *dst, *src;
	register unsigned long i;

	if (line == NULL || *line == '\0')
		return (SUCCESS);

	if (num >= textBlock->numLines)
		return (FAIL);

	newLen = strlen (line) + 1;
	oldLen = strlen (TextBlockLine (textBlock, num)) + 1;
	sizeDiff = newLen - oldLen;

	if (sizeDiff > 0)  // new line longer than old line                           
	{
		// increase text array size if necessary
		if (textBlock->numBytes + sizeDiff > textBlock->maxBytes) 
		{
			textBlock->maxBytes *= 2;
			if ((textBlock->text = (char huge *) GlobalReAllocPtr (textBlock->text,
				textBlock->maxBytes * sizeof (char), GMEM_MOVEABLE)) == NULL) 
			{
				WVMessageBox (textBlock->hTextWnd, "Memory Allocation Failure", "Text Block Add", MB_OK | MB_ICONHAND);
				return (FAIL);
			}
		}
		// shift the text array right by sizeDiff chars
		// sizeDiff iterations, walking backwards from end
		for (src = textBlock->text + textBlock->numBytes, dst = src + sizeDiff;; dst--, src--) 
		{
			*dst = *src;
			if (src == TextBlockLine (textBlock, num))
				break;
		}

	}
	else if (sizeDiff < 0)
	{
		// shift the text array left by sizeDiff chars
		// numBytes - &oldline iterations, walking forwards
		for (src = TextBlockLine (textBlock, num) + oldLen, dst = src + sizeDiff; 
				dst < &textBlock->text[textBlock->numBytes + 1]; dst++, src++)
			*dst = *src;
	}
	// adjust all affected offsets (lines after this line)
	if (sizeDiff != 0)
	{
		for (i = num + 1; i < textBlock->numLines; i++)
			textBlock->offset[i] += sizeDiff;
	}
	textBlock->numBytes += sizeDiff;

	// save max string len for use in scroll bar ranges in status wnd
	if (newLen > textBlock->maxLineLen)
		textBlock->maxLineLen = newLen;

	// copy it in, including the NULL
	memcpy (TextBlockLine (textBlock, num), line, newLen);

	return (SUCCESS);
}

//
// Local Variables:
// tab-width: 2
// end:
//
