/* Metallurgy - Higher Level Interface to Chordata
 * Copyright (C) 2001-2002  Jay Freeman (saurik)
*/

/*
 *        Redistribution and use in source and binary
 * forms, with or without modification, are permitted
 * provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the
 *    above copyright notice, this list of conditions
 *    and the following disclaimer in the documentation
 *    and/or other materials provided with the
 *    distribution.
 * 3. The name of the author may not be used to endorse
 *    or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "stdafx.h"
#include "Helper.h"

#include "menes-com/Framework.h"

#include "metallurgy/Assembly.h"
#include "metallurgy/AssemblyRef.h"
#include "metallurgy/File.h"

#include "chordata/chordata.h"
#include "chordata/IMetaDataDispenser.h"

#include "chordata/Tokens.h"
using namespace Chordata::Tokens;

#include "chordata/Utils.h"
using namespace Chordata::Utils;

namespace Metallurgy {

Assembly::Assembly(const std::wstring &path) :
	AssemblyToken(this, 0x20000001),
	module(new Module(path, this)),
	path(path)
{
	/*HANDLE(Diapexis::Unknown) unknown;
	unknown = Diapexis::Instance(_idof(Chordata::Microsoft::Dispenser));

	HANDLE(Chordata::IMetaDataDispenser) dispenser;
  	unknown->QueryInterface(_idof(Chordata::IMetaDataDispenser), (void **) &dispenser.Mend());
	dispenser->OpenScope((L"file:" + path).c_str(), 0, _idof(Chordata::IMetaDataAssemblyImport), (Diapexis::Unknown **) &import.Mend());*/

	(*module)->QueryInterface(_idof(Chordata::IMetaDataAssemblyImport), (void **) &import.Mend());
	Initialize();
}

/*Assembly::Assembly(Chordata::IMetaDataAssemblyImport *import) :
	import(import),
	AssemblyToken(this, 0x20000001)
{
	Initialize();
}*/

void Assembly::Initialize() {
    tokens.insert(TokenMap::value_type(token, this));

	metadata.LocaleString = new wchar_t[1024];
	metadata.LocaleLength = 1024;

	// XXX: This is totally bogus...
	metadata.Processor = (uint32_t *) 0x01210121;
	metadata.OS = (Chordata::OSInfo *) 0x01310131;

	wchar_t buff[1024];

	import->GetAssemblyProps(token, NULL, NULL, NULL, buff, 1024, NULL, &metadata, &flags);

	name = buff;
}

Assembly::~Assembly() {
	for (TokenMap::iterator token = tokens.begin(); token != tokens.end(); token++)
		if (token->second != this)
			delete token->second;
	delete [] metadata.LocaleString;
	delete module;
}

Chordata::IMetaDataAssemblyImport *Assembly::operator ->() const {
	return import;
}

Chordata::IMetaDataAssemblyImport *Assembly::getImport() const {
	return import;
}

AssemblyToken *Assembly::ResolveToken(Chordata::Token token) {
	if (TokenIsNil(token))
		return NULL;

	TokenMap::const_iterator item = tokens.find(token);
	if (item != tokens.end())
		return dynamic_cast<AssemblyToken *>(item->second);

	if (!(*module)->IsValidToken(token))
		throw std::wstring(L"Completely bogus corrupted token value.  I blame Microsoft.");

	AssemblyToken *unit = NULL;
	switch (GetTokenType(token)) {
		case mdtAssemblyRef:
			unit = new AssemblyRef(this, token);
		break;
		case mdtFile:
			unit = new File(this, token);
		break;

		default:
			unit = new AssemblyToken(this, token);
		break;
	}

	tokens.insert(TokenMap::value_type(token, unit));
	return unit;
}

const Module *Assembly::getModule() const {
	return module;
}

const std::wstring &Assembly::getPath() const {
	return path;
}

std::wstring Assembly::getShortPath() const {
	return path.substr(path.rfind(L'\\') + 1);
}

std::wstring Assembly::getName() const {
	return name;
}

uint32_t Assembly::getFlags() const {
	return flags;
}

std::wstring Assembly::getLocale() const {
	return metadata.LocaleString;
}

TokenList Assembly::getAssemblyRefs() {
	ENUMERATE0(mdAssemblyRef, this, EnumAssemblyRefs, AssemblyRef)
}

TokenList Assembly::getFiles() {
	ENUMERATE0(mdFile, this, EnumFiles, File)
}

}