status_t
BasicTerminalBuffer::_ResizeHistory(int32 width, int32 historyCapacity)
{
	if (width == fWidth && historyCapacity == HistoryCapacity())
		return B_OK;

	if (historyCapacity <= 0) {
		// new history capacity is 0 -- delete the old history object
		delete fHistory;
		fHistory = NULL;

		return B_OK;
	}

	HistoryBuffer* history = new(std::nothrow) HistoryBuffer;
	if (history == NULL)
		return B_NO_MEMORY;

	status_t error = history->Init(width, historyCapacity);
	if (error != B_OK) {
		delete history;
		return error;
	}

	// Transfer the lines from the old history to the new one.
	if (fHistory != NULL) {
		int32 historySize = min_c(HistorySize(), historyCapacity);
		TerminalLine* lineBuffer = ALLOC_LINE_ON_STACK(fWidth);
		for (int32 i = historySize - 1; i >= 0; i--) {
			TerminalLine* line = fHistory->GetTerminalLineAt(i, lineBuffer);
			if (line->length > width)
				_TruncateLine(line, width);
			history->AddLine(line);
		}
	}

	delete fHistory;
	fHistory = history;

	return B_OK;
}
status_t
BasicTerminalBuffer::_ResizeRewrap(int32 width, int32 height,
	int32 historyCapacity)
{
//debug_printf("BasicTerminalBuffer::_ResizeRewrap(): (%ld, %ld, history: %ld) -> "
//"(%ld, %ld, history: %ld)\n", fWidth, fHeight, HistoryCapacity(), width, height,
//historyCapacity);

	// The width stays the same. _ResizeSimple() does exactly what we need.
	if (width == fWidth)
		return _ResizeSimple(width, height, historyCapacity);

	// The width changes. We have to allocate a new line array, a new history
	// and re-wrap all lines.

	TerminalLine** screen = _AllocateLines(width, height);
	if (screen == NULL)
		return B_NO_MEMORY;

	HistoryBuffer* history = NULL;

	if (historyCapacity > 0) {
		history = new(std::nothrow) HistoryBuffer;
		if (history == NULL) {
			_FreeLines(screen, height);
			return B_NO_MEMORY;
		}

		status_t error = history->Init(width, historyCapacity);
		if (error != B_OK) {
			_FreeLines(screen, height);
			delete history;
			return error;
		}
	}

	int32 historySize = HistorySize();
	int32 totalLines = historySize + fHeight;

	// re-wrap
	TerminalLine* lineBuffer = ALLOC_LINE_ON_STACK(fWidth);
	TermPos cursor;
	int32 destIndex = 0;
	int32 sourceIndex = 0;
	int32 sourceX = 0;
	int32 destTotalLines = 0;
	int32 destScreenOffset = 0;
	int32 maxDestTotalLines = INT_MAX;
	bool newDestLine = true;
	bool cursorSeen = false;
	TerminalLine* sourceLine = _HistoryLineAt(-historySize, lineBuffer);

	while (sourceIndex < totalLines) {
		TerminalLine* destLine = screen[destIndex];

		if (newDestLine) {
			// Clear a new dest line before using it. If we're about to
			// overwrite an previously written line, we push it to the
			// history first, though.
			if (history != NULL && destTotalLines >= height)
				history->AddLine(screen[destIndex]);
			destLine->Clear(fAttributes, width);
			newDestLine = false;
		}

		int32 sourceLeft = sourceLine->length - sourceX;
		int32 destLeft = width - destLine->length;
//debug_printf("    source: %ld, left: %ld, dest: %ld, left: %ld\n",
//sourceIndex, sourceLeft, destIndex, destLeft);

		if (sourceIndex == historySize && sourceX == 0) {
			destScreenOffset = destTotalLines;
			if (destLeft == 0 && sourceLeft > 0)
				destScreenOffset++;
			maxDestTotalLines = destScreenOffset + height;
//debug_printf("      destScreenOffset: %ld\n", destScreenOffset);
		}

		int32 toCopy = min_c(sourceLeft, destLeft);
		// If the last cell to copy is the first cell of a
		// full-width char, don't copy it yet.
		if (toCopy > 0 && IS_WIDTH(
				sourceLine->cells[sourceX + toCopy - 1].attributes)) {
//debug_printf("      -> last char is full-width -- don't copy it\n");
			toCopy--;
		}

		// translate the cursor position
		if (fCursor.y + historySize == sourceIndex
			&& fCursor.x >= sourceX
			&& (fCursor.x < sourceX + toCopy
				|| (destLeft >= sourceLeft
					&& sourceX + sourceLeft <= fCursor.x))) {
			cursor.x = destLine->length + fCursor.x - sourceX;
			cursor.y = destTotalLines;

			if (cursor.x >= width) {
				// The cursor was in free space after the official end
				// of line.
				cursor.x = width - 1;
			}
//debug_printf("      cursor: (%ld, %ld)\n", cursor.x, cursor.y);

			cursorSeen = true;
		}

		if (toCopy > 0) {
			memcpy(destLine->cells + destLine->length,
				sourceLine->cells + sourceX, toCopy * sizeof(TerminalCell));
			destLine->length += toCopy;
		}

		destLine->attributes = sourceLine->attributes;

		bool nextDestLine = false;
		if (toCopy == sourceLeft) {
			if (!sourceLine->softBreak)
				nextDestLine = true;
			sourceIndex++;
			sourceX = 0;
			sourceLine = _HistoryLineAt(sourceIndex - historySize,
				lineBuffer);
		} else {
			destLine->softBreak = true;
			nextDestLine = true;
			sourceX += toCopy;
		}

		if (nextDestLine) {
			destIndex = (destIndex + 1) % height;
			destTotalLines++;
			newDestLine = true;
			if (cursorSeen && destTotalLines >= maxDestTotalLines)
				break;
		}
	}

	// If the last source line had a soft break, the last dest line
	// won't have been counted yet.
	if (!newDestLine) {
		destIndex = (destIndex + 1) % height;
		destTotalLines++;
	}

//debug_printf("  total lines: %ld -> %ld\n", totalLines, destTotalLines);

	if (destTotalLines - destScreenOffset > height)
		destScreenOffset = destTotalLines - height;

	cursor.y -= destScreenOffset;

	// When there are less lines (starting with the screen offset) than
	// there's room in the screen, clear the remaining screen lines.
	for (int32 i = destTotalLines; i < destScreenOffset + height; i++) {
		// Move the line we're going to clear to the history, if that's a
		// line we've written earlier.
		TerminalLine* line = screen[i % height];
		if (history != NULL && i >= height)
			history->AddLine(line);
		line->Clear(fAttributes, width);
	}

	// Update the values
	_FreeLines(fScreen, fHeight);
	delete fHistory;

	fScreen = screen;
	fHistory = history;

	if (fWidth != width) {
		status_t error = _ResetTabStops(width);
		if (error != B_OK)
			return error;
	}

//debug_printf("  cursor: (%ld, %ld) -> (%ld, %ld)\n", fCursor.x, fCursor.y,
//cursor.x, cursor.y);
	fCursor.x = cursor.x;
	fCursor.y = cursor.y;
	fSoftWrappedCursor = false;
//debug_printf("  screen offset: %ld -> %ld\n", fScreenOffset, destScreenOffset % height);
	fScreenOffset = destScreenOffset % height;
//debug_printf("  height %ld -> %ld\n", fHeight, height);
//debug_printf("  width %ld -> %ld\n", fWidth, width);
	fHeight = height;
	fWidth = width;

	fScrollTop = 0;
	fScrollBottom = fHeight - 1;
	fOriginMode = fSavedOriginMode = false;

	return B_OK;
}
status_t ArpRemoteTerminal::PutConfiguration(const BMessage* values)
{
	if( !values ) return B_BAD_VALUE;
	
	// Use the ArpMessage interface for extracting our values, since it
	// provides some convenience functions for working with colors
	// and fonts.
	ArpMessage msg(*values);
	int i;
	
	const bool locked = LockLooper();
	
	try {
		int32 mode = 0;
		bool hasMode = msg.HasInt32(ModeConfigName);
		if( hasMode )
			TermSetMode((mode=msg.GetInt32(ModeConfigName, TermMode())));
			
		if( msg.HasString(LFCharsConfigName) )
			TermSetLFChars(msg.GetString(LFCharsConfigName, TermLFChars()));
		else if( hasMode && (mode&1<<1) )
			TermSetLFChars("\n");
		if( msg.HasString(CRCharsConfigName) )
			TermSetCRChars(msg.GetString(CRCharsConfigName, TermCRChars()));
		else if( hasMode && (mode&1<<1) )
			TermSetCRChars("\n\r");
			
		if( msg.HasString(EnterStreamConfigName) )
			TermSetEnterStream(msg.GetString(EnterStreamConfigName, TermEnterStream()));
		else if( hasMode && (mode&1<<6) )
			TermSetEnterStream("\n");
			
		if( msg.HasFont(PlainFontConfigName) ) {
			BFont font(&GetStyledFont(TERM_STYLEPLAIN));
			if( msg.FindFont(PlainFontConfigName, &font) == B_OK ) {
				SetFont(&font);
			}
		}
		if( msg.HasInt32(EncodingConfigName) )
			TermSetEncoding(msg.GetInt32(EncodingConfigName, TermEncoding()));
		if( msg.HasInt32(StyleConfigName) )
			TermSetStyle(msg.GetInt32(StyleConfigName, TermStyle()));
		if( msg.HasFloat("Tint Foreground") ) {
			SetTintForeground(msg.GetFloat("Tint Foreground", 1.75));
		}
		if( msg.HasFloat("Tint Background") ) {
			SetTintBackground(msg.GetFloat("Tint Background", .25));
		}
		{
			char* name;
			type_code type;
			int32 count;
			for( i=0;
				 msg.GetInfo(B_RGB_COLOR_TYPE,i,&name,&type,&count)==B_OK;
				 i++ ) {
				int32 idx = ColorVar2Index(name);
				if( idx >= 0 ) {
					bool bg=false;
					TermColorID col = ColorIndex2ID(idx, &bg);
					if( bg ) backcolor_from_msg(msg, name, 0, col);
					else forecolor_from_msg(msg, name, 0, col);
				}
			}
		}
		if( msg.HasInt32(NumRowsConfigName) || msg.HasInt32(NumColsConfigName) ) {
			int32 r=0,c=0;
			TermGetFixedSize(&r, &c);
			TermSetFixedSize(msg.GetInt32(NumRowsConfigName, r),
							 msg.GetInt32(NumColsConfigName, c));
		}
		if( msg.HasInt32(RegionTopConfigName) ||
			msg.HasInt32(RegionBottomConfigName) ||
			msg.HasInt32(RegionLeftConfigName) ||
			msg.HasInt32(RegionRightConfigName) ) {
			int32 t=0, b=0, l=0, r=0;
			TermGetRegion(&t, &b, &l, &r);
			TermSetRegion(msg.GetInt32(RegionTopConfigName, t),
						  msg.GetInt32(RegionBottomConfigName, b),
						  msg.GetInt32(RegionLeftConfigName, l),
						  msg.GetInt32(RegionRightConfigName, r));
		}
		if( msg.HasInt32(AutoScrollConfigName) )
			SetAutoScrollMode((AutoScrollType)msg.GetInt32(AutoScrollConfigName,
														   AutoScrollMode()));
		if( msg.HasInt32(HistoryUseConfigName) )
			SetHistoryUse((HistoryUseType)msg.GetInt32(HistoryUseConfigName,
													   HistoryUse()));
		if( msg.HasInt32(HistorySizeConfigName) )
			SetHistorySize(msg.GetInt32(HistorySizeConfigName, HistorySize()));
		if( msg.HasBool(VerifyPasteConfigName) )
			SetPasteVerified(msg.GetBool(VerifyPasteConfigName, PasteVerified()));
		if( msg.HasBool(RMBPasteConfigName) )
			SetRMBPaste(msg.GetBool(RMBPasteConfigName, RMBPaste()));
		if( msg.HasString(CurEmulatorConfigName) ) {
			const char* name = msg.GetString(CurEmulatorConfigName, 0);
			if( name ) {
				const ArpEmulationType* type = Emulator().EmulationType();
				if( !type || strcmp(name, type->Name) ) {
					delete SetEmulator(name);
				}
			}
		}
		
		TermClean();
	} catch(...) {
		if( locked ) UnlockLooper();
		throw;
	}
	
	ReportChange(&msg);
	
	if( locked ) UnlockLooper();
		
	return msg.GetError();
}
status_t ArpRemoteTerminal::GetConfiguration(BMessage* values) const
{
	if( !values ) return B_BAD_VALUE;
	
	// Use the ArpMessage interface for adding our values, since it
	// provides some convenience functions for working with colors
	// and fonts.
	ArpMessage msg(*values);
	int i;
	
	const bool locked = const_cast<ArpRemoteTerminal*>(this)->LockLooper();
	
	try {
		ArpD(cdb << ADH << "Getting settings; initial error = "
						<< (msg.GetError()) << endl);

		msg.SetInt32(ModeConfigName, TermMode());
		msg.SetString(LFCharsConfigName, TermLFChars());
		msg.SetString(CRCharsConfigName, TermCRChars());
		msg.SetString(EnterStreamConfigName, TermEnterStream());
		msg.SetFont(PlainFontConfigName, &GetStyledFont(TERM_STYLEPLAIN));
		msg.SetInt32(EncodingConfigName, TermEncoding());
		msg.SetInt32(StyleConfigName, TermStyle());
		const char* varName;
		for( i=0; (varName=ColorIndex2Var(i)) != 0; i++ ) {
			bool bg=false;
			TermColorID col = ColorIndex2ID(i, &bg);
			if( bg ) msg.SetRGBColor(varName, ArpColor(TermTextBackground(col)));
			else msg.SetRGBColor(varName, ArpColor(TermTextForeground(col)));
		}
		{
			int32 r=0,c=0;
			TermGetFixedSize(&r, &c);
			msg.SetInt32(NumRowsConfigName, r);
			msg.SetInt32(NumColsConfigName, c);
		}
		{
			int32 t=0, b=0, l=0, r=0;
			TermGetRegion(&t, &b, &l, &r);
			msg.SetInt32(RegionTopConfigName, t);
			msg.SetInt32(RegionBottomConfigName, b);
			msg.SetInt32(RegionLeftConfigName, l);
			msg.SetInt32(RegionRightConfigName, r);
		}
		msg.SetInt32(AutoScrollConfigName, AutoScrollMode());
		msg.SetInt32(HistoryUseConfigName, HistoryUse());
		msg.SetInt32(HistorySizeConfigName, HistorySize());
		msg.SetBool(VerifyPasteConfigName, PasteVerified());
		msg.SetBool(RMBPasteConfigName, RMBPaste());
		
		const ArpEmulationType* type = Emulator().EmulationType();
		if( type ) {
			msg.AddString(CurEmulatorConfigName,
						  type->Synonym ? type->Synonym : type->Name);
		}
		
		ArpD(cdb << ADH << "Got settings; final error = "
						<< (msg.GetError()) << endl);
					
		if( msg.GetError() == B_NO_ERROR ) {
			*values = msg;
		}
	} catch(...) {
		if( locked ) const_cast<ArpRemoteTerminal*>(this)->UnlockLooper();
		throw;
	}
	
	if( locked ) const_cast<ArpRemoteTerminal*>(this)->UnlockLooper();
	
	return msg.GetError();
}