//===========================================================================//
// File:	L4plasma.cpp                                                        //
// Project: MUNGA           Brick: Applications-specific controls module     //
// Contents: Interface specification for plasma display                      //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  -----------------------------------------------------------//
// 02/08/95  CPB  Initial coding.                                            //
// 06/15/95  CPB  Changed to Video8BitBuffered, added transmission.          //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All rights reserved //
//                      PROPRIETARY and CONFIDENTIAL                         //
//===========================================================================//

#include <mungal4.hpp>
#pragma hdrstop

#if !defined(L4PLASMA_HPP)
#	include <l4plasma.hpp>
#endif

#if !defined(TIME_HPP)
#	include <time.hpp>
#endif

//########################################################################
//############################# PlasmaDisplay ############################
//########################################################################
PlasmaDisplay::PlasmaDisplay(
	Word port,
	Word int_num
):
	Video8BitBuffered(plasmaWidth, plasmaHeight)
{
	//------------------------------------
	// Create serial interface
	//------------------------------------
	pc_serial = new PCSerial(PCS_9600, PCS_N81, port, int_num);
	if (pc_serial != NULL)
	{
		Register_Object(pc_serial);
		pc_serial->FlowControlEnable(False);
		//------------------------------------
		// Turn off the cursor
		//------------------------------------
		static char
			cursorOffString[] = { 27 /*ESC*/, 'G', (char) 0x00 };

		pc_serial->SendString(cursorOffString, 3);
	}
	//------------------------------------
	// Clear update flag array
	//------------------------------------
	currentTransferLine = 0;
	updateInProgress = False;

	for(int i=0; i<plasmaHeight; ++i)
	{
		updateFlag[i] = 0;
	}

	Check_Fpu();
}

PlasmaDisplay::~PlasmaDisplay()
{
	if (pc_serial != NULL)
	{
		//---------------------------------------------------------------------
		// Flush data to plasma display
		//---------------------------------------------------------------------
		WaitForUpdate();

		// This odd sequence of allocation, setting, and adding is necessary:
		// if you try to combine them, the compiler gets confused as to which
		// method to invoke for 'Now()'.
		Time
			then;
		then = Now();
		then += 5.0;
		while (Now() < then)
		{
			//---------------------------------------------------
			// Drop out of loop when ALL data has been sent
			//---------------------------------------------------
			if (pc_serial->TransmitCharCount() <= 0)                  
			{
				if (!pc_serial->IsActive())
				{
					break;
				}
			}
		}
		//---------------------------------------------------------------------
		// Delete the serial interface
		//---------------------------------------------------------------------
		Unregister_Object(pc_serial);
		delete pc_serial;
	}
	Check_Fpu();
}

Logical
	PlasmaDisplay::TestInstance() const
{
	return Video8BitBuffered::TestInstance(); 
}

void
	PlasmaDisplay::ShowInstance(char *indent)
{
	cout << indent << "Plasma:\n";

	char
		temp[80];

	Str_Copy(temp,indent, 80);
	Str_Cat(temp,"...", 80);

	Video8BitBuffered::ShowInstance(temp);
	Check_Fpu();
}


void
	sendLine(
		PCSerial *pc_serial,
		int line_number,
		Byte *source_pointer,
		int source_width
	)
{
	char
		graphics[32];	// We only need 16...the extra is just in case.
	int
		x,
		index;
	Byte
		byte,
		byte_bit;
	//--------------------------------------------
	// Send header
	//--------------------------------------------
	graphics[0] = (char) 27;							// ascii ESC
	graphics[1] = 'P';									// graphics command
	graphics[2] = (char) 0;								// screen number
	graphics[3] = (char) line_number;				// Y position
	graphics[4] = (char) 0;								// X BYTE position
	graphics[5] = (char) ((source_width+7)>>3);	// width in bytes
	graphics[6] = (char) 1;

	pc_serial->SendString(graphics, 7);
	//--------------------------------------------
	// Send data
	//--------------------------------------------
	byte = 0;
	byte_bit = 0x80;
	index = 0;
	for(x=0; x<source_width; ++x)
	{
		//
		// Add bit to byte
		//
		if (*source_pointer++)
		{
			byte |= byte_bit;
		}
		//
		// Update bit position
		//
		byte_bit >>= 1;
		//
		// If entire byte collected, send it
		//
		if (byte_bit == 0)
		{
			Verify(index < sizeof(graphics));
			graphics[index++] = byte;
			byte = 0;
			byte_bit = 0x80;
		}
	}
	//
	// End of source line: send remaining bits
	//
	if (byte_bit != 0x80)
	{
		graphics[index++] = byte;
	}
	//
	// Send graphics data
	//
	pc_serial->SendString(graphics, index);
	Check_Fpu();
}

void
	PlasmaDisplay::Scan(Logical forceAll)
{
	Word
		changed_line = (Word) forceAll,
		*changed_line_pointer = changedLine;
	char
		*update_pointer=updateFlag;
	int
		y;

	for(y=0; y<plasmaHeight; ++y,++update_pointer)
	{
		//---------------------------------------------------------
		// Scan for changed lines
		//---------------------------------------------------------
		changed_line += *changed_line_pointer;
		*changed_line_pointer++ = 0;

		if (changed_line)
		{
			*update_pointer = 1;

			updateInProgress = True;
		}
	}

	Check_Fpu();
}

Logical
	PlasmaDisplay::Update(Logical force_all)
{
	int
		transfer_count,
		total_count;
	Logical
		result = True;		// True == 'more to send, not done'

	if (pc_serial != NULL)
	{
		//-----------------------------------------
		// Accumulate changed lines
		//-----------------------------------------
		forceAll |= force_all;

		if (currentTransferLine == 0)
		{
			Scan(forceAll);
			forceAll = False;
		}
		//-----------------------------------------
		// Send only if the buffer is empty
		//-----------------------------------------
		if (pc_serial->TransmitCharCount() == 0)
		{
			total_count = plasmaHeight;
			transfer_count = 4;
			while(transfer_count>0)
			{
				//-----------------------------------------
				// Transfer a line if it's ready
				//-----------------------------------------
				if (updateFlag[currentTransferLine])
				{
					//-----------------------------------------
					// Decrement the 'transfer count'
					//-----------------------------------------
					--transfer_count;
					//-----------------------------------------
					// Clear the 'update' flag
					//-----------------------------------------
					updateFlag[currentTransferLine] = 0;
					//-----------------------------------------
					// Send the line data to the display
					//-----------------------------------------
					sendLine(
						pc_serial,
						currentTransferLine,
						pixelBuffer->Data.MapPointer +
							(pixelBuffer->Data.Size.x * currentTransferLine),
						pixelBuffer->Data.Size.x
					);
				}
				//-----------------------------------------
				// Increment currentTransferLine
				//-----------------------------------------
				if (++currentTransferLine >= plasmaHeight)
				{
					currentTransferLine = 0;
				}
				//-----------------------------------------
				// Reset to top if all lines scanned
				//-----------------------------------------
				if (--total_count < 0)
				{
					currentTransferLine = 0;	// reset to top line
					updateInProgress = False;
					result = False;				// False == 'all data sent'
					break;
				}
			}
		}
	}
	Check_Fpu();
	return result;
}

void
	PlasmaDisplay::WaitForUpdate()
{
	Time
		end_time;

	end_time = Now();
	end_time += 5.0;		// We'll wait this many seconds to update the display-

	while (updateInProgress)
	{
		Update(False);

		if (Now() >= end_time)
		{
			break;
		}
	}
	Check_Fpu();
}









