/* 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 "metallurgy/Method.h"
#include "metallurgy/TypeDef.h"

#include "metallurgy/ILMethodImpl.h"
#include "metallurgy/RuntimeMethodImpl.h"

namespace Metallurgy {

Method::Method(const Module *module, Chordata::Token token) :
	Member(module, token)
{
	wchar_t buff[1024];
	mdTypeDef tktype;

	uint32_t implflags;
	uint32_t coderva;

	const Chordata::Signature *sigBlob;
	uint32_t sigLength;

	(*module)->GetMethodProps(token, &tktype, buff, 1024, NULL, &flags, &sigBlob, &sigLength, &coderva, &implflags);

	name = buff;
	type = dynamic_cast<TypeDef *>(module->ResolveToken(tktype));
	signature = new Signature(module, sigBlob, sigLength);

	uint32_t code = implflags & Chordata::Flags::miCodeTypeMask;
	if (code == Chordata::Flags::miIL)
		impl = new ILMethodImpl(this, coderva, implflags);
	else if (code == Chordata::Flags::miRuntime)
		impl = new RuntimeMethodImpl(this, coderva, implflags);
	else
		impl = NULL;
}

Method::~Method() {
	delete impl;
}

const IHasElement *Method::getType() const {
	return type;
}

uint32_t Method::getFlags() const {
	return flags;
}

const MethodImpl *Method::getImpl() const {
	return impl;
}

Chordata::Flags::CorMethodAttr Method::getAccess() const {
	return (Chordata::Flags::CorMethodAttr) (flags & Chordata::Flags::mdMemberAccessMask);
}

const Param *Method::getParam(uint32_t sequence) const {
	mdParamDef tkparam(Chordata::Tokens::NilToken);
	(*module)->GetParamForMethodIndex(token, sequence, &tkparam);
	/*if (tkparam == Chordata::Tokens::NilToken)
		throw std::wstring(L"argument without parameter");*/
	return dynamic_cast<Param *>(module->ResolveToken(tkparam));
}

const Element *Method::getArgument(uint32_t sequence) const {
	if (signature->getHasThis() && (sequence--) == 0)
		return type->getElement();
	else
		return signature->getElement(sequence);
}

std::wstring Method::getArgName(uint32_t sequence) const {
	return (signature->getHasThis() && sequence == 0) ? L"" : getParam(signature->getHasThis() ? sequence : sequence + 1)->getName().getFull();
}

std::wstring Method::getXmlMemberKey() const {
	std::wstring key = L"M:" + type->getName().getFull() + L"." + name.getFull() + L"(";
	for (uint32_t i(0); i < signature->getCount(); i++) {
		if (i != 0) key += L",";
		key += signature->getElement(i)->getXmlMemberKey();
	}
	return key + L")";
}

bool Method::IsStatic() const {
	return (flags & Chordata::Flags::mdStatic) != 0;
}

bool Method::IsFinal() const {
	return (flags & Chordata::Flags::mdFinal) != 0;
}

bool Method::IsVirtual() const {
	return (flags & Chordata::Flags::mdVirtual) != 0;
}

bool Method::IsHideBySig() const {
	return (flags & Chordata::Flags::mdHideBySig) != 0;
}

bool Method::IsAbstract() const {
	return (flags & Chordata::Flags::mdAbstract) != 0;
}

bool Method::IsSpecialName() const {
	return (flags & Chordata::Flags::mdSpecialName) != 0;
}

}