/* BBCameraWindow.m				 
 *
 * Represents a particular View of a Beaker. Here we control the beakerCamera
 * and relating controls and settings.
 *
 * For interface-info see the header file. The comments in this file mostly
 * cover only the real implementation details.
 *
 * Written by: 		Thomas Engel
 * Created:    		22.10.1993 (Copyleft)
 * Last modified: 	30.10.1994
 */

#import "BBCameraWindow.h"
#import "BBBeakerCamera.h"
#import "BBBeaker.h"
#import "BBMolecule.h"
#import "BBMoleculeShape.h"
#import <misckit/MiscString.h>

@implementation BBCameraWindow

- initFrom:aList asPartOf:aBeaker
{
	id	ourList;
	id	nameobj;
	id	ambientLight;
	id	pointLight;
	RtPoint	fromPoint;
	RtPoint	toPoint;

	
	self = [super init];
	if( !self ) return self;
	
	// All right we are a real object now and should load the NIB.
	// Inside the NIB we are declared to be the windows delegate.

	if( [NXApp loadNibSection:"CameraWindow.nib" owner:self] == nil )
		NXRunAlertPanel( NULL, "Couldn't load CameraWindow.nib",
						 "OK", NULL, NULL );

	ourBeaker = aBeaker;
	
	// Now lets get our Outfit right. Some naming has to be done here.
	
	nameobj = [MiscString new];
	[nameobj setIntValue:[[ourBeaker cameraList] count] + 1];
	[nameobj preCatStringValue:"Camera #"];
	[[[nameobj catStringValue:" ("]
			   catStringValue:[ourBeaker name]]
			   catStringValue:")"];
	
	[self setName:[nameobj stringValue]];
	[self setImage:[NXImage findImageNamed:"CameraIcon"]];

	// We have to create the simple world before we make the window front
	// After that we can add us to the beakers camera list (this is not
	// perfect..see bugs)
	
	[camera createWorldFor:ourBeaker];

	[window setTitle:[self name]];	
	[window setMiniwindowImage:[self image]];
	[window makeKeyAndOrderFront:self];

	[ourBeaker addCamera:self];
 
 	// The window is ready. Now we will add all the shapes to our
	// camera. This might be some recursive process.
	
	ourList = [aList copy];
	[self addShapesFrom:ourList];
	
	// Lights will be created here too. The default is a ambient an
	// a point light.
 	
	ambientLight = [N3DLight new];
	[ambientLight makeAmbientWithIntensity:0.5];
	[camera addLight:ambientLight];
	
	fromPoint[0] = -60.0;
	fromPoint[1] = 60.0;
	fromPoint[2] = -60.0;
	toPoint[0] = 0.0;
	toPoint[1] = 0.0;
	toPoint[2] = 0.0;
	pointLight = [N3DLight new];
	[pointLight makeDistantFrom:fromPoint to:toPoint intensity:1.0];
	[camera addLight:pointLight];

	// Now we only have to redraw the camera area and add the lights to
	// our hirarchie.
	
	[camera display];
	//[(List *)self addObject:ourList];
	// [self addObject:ambientLight];
	// [self addObject:pointLight];
	[ourList free];
	
	return self;
}

- addShapesFrom:anObject
{	
	// Only the objects that know how to produce a shape will be added.
	// Lists are treated seperatly.
	// ATTENTION: I won't use recuresive calls right now, because this might
	// lead to chaos.
	// Every region has to be group to its molecule if it already does exist.!
	// If not we have to create a molecule shape first.

	int	i;
	id	aList;
	
	// If an object understands "regions" and "molecule" it should be a group
	// of regions. The rest is just adding single regions.

	if( [anObject respondsTo:@selector(regions)] &&
		[anObject respondsTo:@selector(molecule)])
	{
		aList = [anObject regions];
		for( i=0; i<[aList count]; i++ )
			[self addShapeOf:[aList objectAt:i]];
	}
	// If it is a simple list we will try to do the same with all the
	// single elements of the list.
	
	else if( [anObject respondsTo:@selector(count)] &&
			 [anObject respondsTo:@selector(objectAt:)] )
	{
		for( i=0; i<[anObject count]; i++ )
		[self addShapeOf:[anObject objectAt:i]];
	}
	// The final step is to try adding the object on its own.
	
	else [self addShapeOf:anObject];
	
	return self;
}

- addShapeOf:oneObject
{
	// This does add a single object to the view hirarchy.
	// Used only internally!
	// Adding is only possible if the object knows about its shape.
	// If it gets added to a molecule or not depends on the object. Does it
	// know it molecule ? Sometimes we might have to create the molecule first.
	
	id	molShape;
	id	aList;
	
	if( [oneObject respondsTo:@selector(shape)] )
	{
		// If the object knows its molecule it should be a region. So we
		// might add it to the molecules shape.
		// If its class is the molecule shape it is a molecule and we will
		// only add it if it is not already visible.
		
		if( [oneObject respondsTo:@selector(molecule)] )
		{
			molShape = [self findShapeFor:[oneObject molecule]];
			if( [oneObject shapeClass] == [BBMoleculeShape class] )
			{
				if( !molShape ) [camera addShape:[oneObject shape]];
			}
			else
			{
				if( !molShape )
				{
					aList = [List new];
					molShape = [[[[oneObject molecule] 
											 shapeClass] 
											 alloc]
											 initFrom:aList
											 asPartOf:[oneObject molecule]];
					[aList free];
					[camera addShape:molShape];
				}
				[molShape addRegion:oneObject];
			}
		}
		// otherwise we will just add it to the world. Who knows what it is.
		
		else [camera addShape:[oneObject shape]];
	}
	return self;
}

- findShapeFor:anObject
{
	// Currently we are only using this method to find a molecule shape
	// for a certain molecule.
	// This method may be extended or move to some other object (beakerShape)
	
	id	aShape;
	
	aShape = [[camera worldShape] descendant];

	while( aShape != nil )
	{
		if( [aShape respondsTo:@selector(molecule)] )
		{
			if( [aShape molecule] == anObject ) return aShape;
		}
		aShape = [aShape nextPeer];
	}
	// Not found..sorry
	
	return nil;
}

- print:sender
{
	// Some print defaults...

	[[[NXApp printInfo] setVertCentered:YES] setOrientation:NX_PORTRAIT 
						andAdjust:YES];
	[[NXApp printInfo] setMarginLeft:0.0 right:0.0 top:0.0 bottom:0.0];
	[[NXApp printInfo] setHorizPagination:NX_FITPAGINATION];
	[[NXApp printInfo] setVertPagination:NX_FITPAGINATION];

	[camera printPSCode:self];
	return self;
}

- close:sender
{
	// This might appear when the whole Beaker document has to get closed.
	
	return [ourBeaker close:self];
}

- closeWindow
{
	[window close];
	return self;
}

- window
{
	return window;
}

- camera
{
	return camera;
}

- windowDidBecomeMain:sender
{
	// Once we become the main window we should inform the rotator where his
	// action should got.

	[[[NXApp delegate] rotator] setTarget:[camera worldShape]];
	[[[NXApp delegate] rotator] setCamera:camera];

	return self;
}

@end

/*
 * History: 30.10.94 Switched to the MiscString.
 *
 *			17.05.94 Added automatic shape creation and printing method.
 *
 *			14.05.94 New name, and new code.
 *
 *			21.01.94 Added closeing the window.
 *
 *			10.01.94 Changed some methods to work properly with our
 *					 new objectWell.
 *
 *			28.12.93 Added the obj-image stuff and some general miniwindow
 *					 and camera support. But there is still more to come.
 *
 *			22.12.93 First coding has begun.
 *
 *
 * Bugs: - Well adding the camera to the beaker when inited is not perfect. 
 *		   Maybe it is better to add it when it gets created. I should check
 *		   that...inside BBBrowserManager.
 *
 *		 - Adding the shapes is a big BUG! Right now I really don't like it.
 *		   But it should work for a while.
 */