/* 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.
*/

#ifndef METALLURGY_ILMETHODIMPL_H
#define METALLURGY_ILMETHODIMPL_H

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "metallurgy/metallurgy.h"
#include "metallurgy/MethodImpl.h"
#include "metallurgy/StandAlone.h"

#include "chordata/ILMethod.h"

#include "menes/bytebuf.hpp"

namespace Metallurgy {

	class METALLURGY_API ILMethodImpl : public MethodImpl {
	  public:
		struct Exception {
			// HOLY MOTHER OF GOD!!
			Chordata::ILMethod::Section::Exception::Flags::CorExceptionFlag Flags;
			uint8_t *	TryOffset;
			uint32_t	TryLength;
			uint8_t *	HandlerOffset;
			uint32_t	HandlerLength;

			union {
				Chordata::Token	ClassToken;
				uint32_t		FilterOffset; 
			};

			template <class Clause>
			Exception(uint8_t *offset, Clause *clause) :
				Flags(clause->Flags),
				TryOffset(offset + clause->TryOffset),
				TryLength(clause->TryLength),
				HandlerOffset(offset + clause->HandlerOffset),
				HandlerLength(clause->HandlerLength),
				ClassToken(clause->ClassToken)
			{}

			Exception(const Exception &ex) :
				Flags(ex.Flags),
				TryOffset(ex.TryOffset),
				TryLength(ex.TryLength),
				HandlerOffset(ex.HandlerOffset),
				HandlerLength(ex.HandlerLength),
				ClassToken(ex.ClassToken)
			{}
		};

		typedef std::vector<Exception> ExceptionVector;

	  protected:
		StandAlone *locals;

		uint8_t *offset;
        menes::bytebuf data;
		uint32_t length;

		ExceptionVector m_Exceptions;

		template <class Clause, class Section>
		void AddException(unsigned __int8 *offset, Section *sect, unsigned __int32 length) {
			unsigned __int8 *end = length + (unsigned __int8 *) sect;
			for (Clause *clause = sect->Clauses; (unsigned __int8 *) clause < end; clause++)
				m_Exceptions.push_back(Exception(offset, clause));
		}

	  public:
		ILMethodImpl(const Method *method, uint32_t coderva, uint32_t flags);

		uint8_t *getOffset() const;
		uint32_t getLength() const;

		StandAlone *getLocals() const;

        menes::bytebuf getData() const;

		const ExceptionVector &getExceptions() const;

		// XXX: Implementation Specific
		//IList *get_Exceptions(); 

		static Exception *remException(ExceptionVector &exs, const uint8_t *offset) {
			ExceptionVector::iterator ex(NULL);
			for (ExceptionVector::iterator exI(exs.begin()); exI < exs.end(); exI++)
				if (exI->TryOffset == offset && !(ex != NULL && ex->TryLength > exI->TryLength))
					ex = exI;
			if (ex == NULL)
				return NULL;
			else {
				Exception *exc = new Exception(*ex);
				exs.erase(ex);
				return exc;
			}
		}

		const Exception *findException(const uint8_t *offset) const {
			const Exception *ex = NULL;
			for (ExceptionVector::const_iterator exI(m_Exceptions.begin()); exI < m_Exceptions.end(); exI++)
				if (exI->TryOffset == offset && !(ex != NULL && ex->TryLength > exI->TryLength))
					ex = &*exI;
			return ex;
		}

		const Exception *isHandler(const uint8_t *offset) const {
			for (ExceptionVector::const_iterator exI(m_Exceptions.begin()); exI < m_Exceptions.end(); exI++)
				if (exI->HandlerOffset == offset)
					return &*exI;
			return NULL;
		}
	};

}

#endif//METALLURGY_ILMETHODIMPL_H