/*=============================================================================
	CAComponent.cpp
 
	$Log: CAComponent.cpp,v $
	Revision 1.13  2004/12/10 22:21:19  bills
	slight tweak to GetResourceVersion
	
	Revision 1.12  2004/09/28 23:15:09  jcm10
	add missing include
	
	Revision 1.11  2004/07/08 17:50:41  bills
	tweaks to compdesc constructor
	
	Revision 1.10  2004/04/21 20:08:34  bills
	better variable name
	
	Revision 1.9  2003/10/01 08:08:52  bills
	clean up printing
	
	Revision 1.8  2003/09/02 23:31:09  bills
	tweak error handling
	
	Revision 1.7  2003/08/09 01:01:09  bills
	check res version details
	
	Revision 1.6  2003/07/05 20:13:26  bills
	add a constructor
	
	Revision 1.5  2003/06/30 21:52:49  bills
	fix comp name handling
	
	Revision 1.4  2003/06/15 23:58:15  bills
	fix (Component) const
	
	Revision 1.3  2003/03/12 05:05:05  bills
	refactor persistence
	
	Revision 1.2  2003/03/11 20:57:29  bills
	first pass at save/restore state
	
	Revision 1.1  2003/03/11 00:38:21  bills
	initial checkin
	

	Created by William Stewart on Sat Mar 08 2003.
	Copyright (c) 2003 __MyCompanyName__. All rights reserved.

=============================================================================*/

#include "CAComponent.h"
#include "CAComponentDescription.h"
#include "CACFDictionary.h"
#include <stdlib.h>

CAComponent::CAComponent (const ComponentDescription& inDesc, CAComponent* next)
	: mManuName(0), mAUName(0), mCompName(0), mCompInfo (0)
{
	mComp = FindNextComponent ((next ? next->Comp() : NULL), const_cast<ComponentDescription*>(&inDesc));
	if (mComp)
		GetComponentInfo (Comp(), &mDesc, NULL, NULL, NULL);
	else
		memcpy (&mDesc, &inDesc, sizeof(ComponentDescription));
}

CAComponent::CAComponent (const Component& comp) 
	: mComp (comp),
	  mManuName(0), 
	  mAUName(0), 
	  mCompName(0), 
	  mCompInfo (0) 
{
	GetComponentInfo (Comp(), &mDesc, NULL, NULL, NULL);
}

CAComponent::CAComponent (const ComponentInstance& compInst) 
	: mComp (Component(compInst)), 
	  mManuName(0), 
	  mAUName(0), 
	  mCompName(0), 
	  mCompInfo (0) 
{ 
	GetComponentInfo (Comp(), &mDesc, NULL, NULL, NULL);
}

CAComponent::CAComponent (OSType inType, OSType inSubtype, OSType inManu)
	: mDesc (inType, inSubtype, inManu),
	  mManuName(0), mAUName(0), mCompName(0), mCompInfo (0)
{
	mComp = FindNextComponent (NULL, &mDesc);
	GetComponentInfo (Comp(), &mDesc, NULL, NULL, NULL);
}

CAComponent::~CAComponent ()
{
	Clear();
}

OSStatus		CAComponent::GetResourceVersion (UInt32 &outVersion) const
{
	bool versionFound = false;
	short componentResFileID = kResFileNotOpened;
	OSStatus result;
	short thngResourceCount;
	
	short curRes = CurResFile();
	require_noerr (result = OpenAComponentResFile( mComp, &componentResFileID), home);
	require_noerr (result = componentResFileID <= 0, home);
	
	UseResFile(componentResFileID);

	thngResourceCount = Count1Resources(kComponentResourceType);
	
	require_noerr (result = ResError(), home);
			// only go on if we successfully found at least 1 thng resource
	require_noerr (thngResourceCount <= 0 ? -1 : 0, home);

	// loop through all of the Component thng resources trying to 
	// find one that matches this Component description
	for (short i = 0; i < thngResourceCount && (!versionFound); i++)
	{
		// try to get a handle to this code resource
		Handle thngResourceHandle = Get1IndResource(kComponentResourceType, i+1);
		if (thngResourceHandle != NULL && ((*thngResourceHandle) != NULL))
		{
			if (UInt32(GetHandleSize(thngResourceHandle)) >= sizeof(ExtComponentResource))
			{
				ExtComponentResource * componentThng = (ExtComponentResource*) (*thngResourceHandle);

				// check to see if this is the thng resource for the particular Component that we are looking at
				// (there often is more than one Component described in the resource)
				if ((componentThng->cd.componentType == mDesc.Type()) 
						&& (componentThng->cd.componentSubType == mDesc.SubType()) 
						&& (componentThng->cd.componentManufacturer == mDesc.Manu()))
				{
					outVersion = componentThng->componentVersion;
					versionFound = true;
				}
			}
			ReleaseResource(thngResourceHandle);
		}
	}

	if (!versionFound)
		result = resNotFound;
		
	UseResFile(curRes);	// revert
	
	if ( componentResFileID != kResFileNotOpened )
		CloseComponentResFile(componentResFileID);
		
home:
	return result;
}

void			CAComponent::Clear ()
{
	if (mManuName) { CFRelease (mManuName); mManuName = 0; }
	if (mAUName) { CFRelease (mAUName);  mAUName = 0; }
	if (mCompName) { CFRelease (mCompName); mCompName = 0; }
	if (mCompInfo) { CFRelease (mCompInfo); mCompInfo = 0; }
}

CAComponent&	CAComponent::operator= (const CAComponent& y)
{
	Clear();

	mComp = y.mComp;
	mDesc = y.mDesc;

	if (y.mManuName) { mManuName = y.mManuName; CFRetain (mManuName); }
	if (y.mAUName) { mAUName = y.mAUName; CFRetain (mAUName); }
	if (y.mCompName) { mCompName = y.mCompName; CFRetain (mCompName); } 
	if (y.mCompInfo) { mCompInfo = y.mCompInfo; CFRetain (mCompInfo); }

	return *this;
}

void 		CAComponent::SetCompNames () const
{
	if (!mManuName) {
		Handle h1 = NewHandle(4);
		CAComponentDescription desc;
		OSStatus err = GetComponentInfo (Comp(), &desc, h1, 0, 0);
		
		if (err) return;
		
		HLock(h1);
		char* ptr1 = *h1;
		// Get the manufacturer's name... look for the ':' character convention
		int len = *ptr1++;
		char* displayStr = 0;

		const_cast<CAComponent*>(this)->mCompName = CFStringCreateWithPascalString(NULL, (const unsigned char*)*h1, kCFStringEncodingUTF8);
		
		for (int i = 0; i < len; ++i) {
			if (ptr1[i] == ':') { // found the name
				ptr1[i] = 0;
				displayStr = ptr1;
				break;
			}
		}
		
		if (displayStr)
		{
			const_cast<CAComponent*>(this)->mManuName = CFStringCreateWithCString(NULL, displayStr, kCFStringEncodingUTF8);
										
			//move displayStr ptr past the manu, to the name
			// we move the characters down a index, because the handle doesn't have any room
			// at the end for the \0
			int i = strlen(displayStr), j = 0;
			while (displayStr[++i] == ' ' && i < len)
					;
			while (i < len)
				displayStr[j++] = displayStr[i++];
			displayStr[j] = 0;

			const_cast<CAComponent*>(this)->mAUName = CFStringCreateWithCString(NULL, displayStr, kCFStringEncodingUTF8);
		} 
		
		DisposeHandle (h1);
	}
}

void	CAComponent::SetCompInfo () const
{
	if (!mCompInfo) {
		Handle h1 = NewHandle(4);
		CAComponentDescription desc;
		OSStatus err = GetComponentInfo (Comp(), &desc, 0, h1, 0);
		if (err) return;
		HLock (h1);
		const_cast<CAComponent*>(this)->mCompInfo = CFStringCreateWithPascalString(NULL, (const unsigned char*)*h1, kCFStringEncodingUTF8);

		DisposeHandle (h1);
	}
}

void	_ShowCF (FILE* file, CFStringRef str)
{
	if (CFGetTypeID(str) != CFStringGetTypeID()) {
		CFShow(str);
		return;
	}

	UInt32 len = CFStringGetLength(str);
	char* chars = (char*)malloc (len * 2); // give us plenty of room for unichar chars
	if (CFStringGetCString (str, chars, len * 2, kCFStringEncodingUTF8))
		fprintf (file, "%s", chars);
	else
		CFShow (str);

	free (chars);
}

void	CAComponent::Print(FILE* file) const
{
	fprintf (file, "CAComponent: 0x%X", int(Comp()));
	if (mManuName) {
		fprintf (file, ", Manu:"); _ShowCF (file, mManuName);
		if (mAUName) fprintf (file, ", Name:"); _ShowCF (file, mAUName);
	}
	fprintf (file, ", "); 
	Desc ().Print(file);
}
