// 
//   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
//   Free Software Foundation, Inc.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

// The AS3 abc block format reader.
//

#ifndef GNASH_ABC_BLOCK_H
#define GNASH_ABC_BLOCK_H

#include "string_table.h"
#include "MultiName.h"
#include "Namespace.h"
#include "as_value.h"

#include <vector>
#include <string>
#include <stdexcept>

namespace gnash {
    namespace abc {
        class AbcBlock;
        class Machine;
        class Class;
        class Method;
    }
    class SWFStream; // for read signature
    class ClasstHierarchy;
}

namespace gnash {

/// ABC-only resources for parsing and execution.
namespace abc {

/// Class describing a static property
//
/// Traits are non-dynamic properties. That is, they are not deletable or
/// modifiable in certain ways through ActionScript. They exist for reasons
/// of performance. A property lookup on an object always checks the Traits
/// before dynamic properties.
//
/// Traits can belong to Methods, Classes, and Scripts. Classes have both
/// instance and Class traits.
//
/// TODO: Traits currently need finalization. This performs two tasks. At least
/// one, and possibly both, are wrong:
/// 1. Trait definitions contain references to AbcBlock definitions. Currently
///    these references are resolved during finalization. It may be possible
///    to do this during parsing.
/// 2. Traits should be made available to ActionScript. Currently this is done
///    by attaching them to an object. This is plain wrong and doesn't even
///    work in many cases.
//
/// TODO: As Traits are stored in the correct Class, Method etc, they do not
///       need to store a target.
class Trait
{
public:

    enum Kind
	{
		KIND_SLOT = 0,
		KIND_CONST = 6,
		KIND_METHOD = 1,
		KIND_GETTER = 2,
		KIND_SETTER = 3,
		KIND_CLASS = 4,
		KIND_FUNCTION = 5
	};

	Trait()
        :
        _hasValue(false),
        _kind(KIND_SLOT),
        _slotID(0),
        _typeIndex(0),
        _classInfoIndex(0),
        _value(),
        _name(0),
        _globalName(),
        _namespace(0),
        _method(0),
        _valueSet(false),
        _classTarget(0),
        _methodTarget(0),
        _static(false)
	{}

	bool read(SWFStream* in, AbcBlock *block);

	bool finalize(AbcBlock* block, abc::Class* cl, bool do_static);

	bool finalize_mbody(AbcBlock* block, Method* m);

	void set_target(abc::Class* cl, bool do_static) {
        _classTarget = cl;
        _static = do_static;
    }

	void set_target(Method *m) {
        _classTarget = 0;
        _methodTarget = m;
    }

	bool finalize(AbcBlock* block)
	{
		if (_classTarget) {
			return finalize(block, _classTarget, _static);
        }
		return finalize_mbody(block, _methodTarget);
	}

private:

    friend class AbcBlock;

	bool _hasValue;
	Kind _kind;
	std::uint32_t _slotID;
	std::uint32_t _typeIndex;
	std::uint32_t _classInfoIndex;
	as_value _value;

	URI _name;
    string_table::key _globalName;

	Namespace* _namespace;
	Method* _method;
	bool _valueSet;

	abc::Class* _classTarget;
	Method* _methodTarget;
	bool _static;

};

/// Output stream operator for abc::Trait::Kind
std::ostream& operator<<(std::ostream& o, const Trait::Kind k);

namespace {

template<typename T>
inline void checkBounds(size_t i, const T& container)
{
    if (i >= container.size()) {
        throw std::range_error("Attempt to access pool out of range");
    }
}

}


/// The ActionScript bytecode of a single ABC tag in a SWF.
//
/// ABC blocks have their own "names" for all resources. In Gnash, these are
/// a string table index. They are different from global names. These are used
/// to locate resources inside the ABC block.
// 
/// ABC blocks have a set of "namespace" resources. Some namespaces are
/// private. We make these into anonymous namespaces.
// 
/// We assume all non-private namespaces are public. Some are "package"
/// namespaces; these seem to coincide with the built-in packages or 0,
/// the global namespace.
/// 
/// We always search for these public namespaces by global URI in our
/// ClassHierarchy. If we use ABC names, "flash.text" will not find the built-in
/// flash.text namespace. Using the global name means that we 'import' the
/// built-in namespace into our own resources.
//
/// Instances / Scriptes 
//
/// Likewise, classes are always given a global name, not an ABC name. This is
/// because they become globally available, including (we assume) to other ABC
/// blocks, so using an ABC name means they cannot be located externally.
/// Even if ABC block resources should not be available to other blocks (which
/// seems unlikely), using an ABC name for classes risks name conflicts with
/// the built-in classes already in a namespace: ABC names and global names
/// can have the same index even when the names are different.
//
/// Class lookup
//
/// This is particularly important for locateClass (called by instantiateScript
/// from SymbolScript tag execution). The SymbolScript tag identifies a class
/// using a global name, which may be qualified with a namespace. If it is
/// not qualified, we look in the global namespace 0.
// 
/// When we call locateClass, we use global names, not ABC names, because
/// classes are identified by global names (see above). However, we
/// still look only in the ABC block's namespaces. The block's first namespace
/// is always the global namespace; other package namespaces are imported
/// according to the block's namespace constants.
class AbcBlock
{
public:
    
    enum NamespaceConstant
	{
		PRIVATE_NS = 0x05,
        CONSTANT_NS = 0x08,
		PACKAGE_NS = 0x16,
		PACKAGE_INTERNAL_NS = 0x17,
		PROTECTED_NS = 0x18,
		EXPLICIT_NS = 0x19,
		STATIC_PROTECTED_NS = 0x1A
    };

    enum MethodConstant
    {
        METHOD_ARGS = 0x01,
		METHOD_ACTIVATION = 0x02,
		METHOD_MORE = 0x04,
		METHOD_OPTIONAL_ARGS = 0x08,
		METHOD_IGNORE = 0x10,
		METHOD_NATIVE = 0x20,
		METHOD_DEFAULT_NS = 0x40,
		METHOD_ARG_NAMES = 0x80
    };

    enum InstanceConstant
    {
		INSTANCE_SEALED = 0x01,
		INSTANCE_FINAL = 0x02,
		INSTANCE_INTERFACE = 0x04,
		INSTANCE_DYNAMIC = 0x00,
		INSTANCE_PROTECTED_NS = 0x08
    };

    enum PoolConstant
    {
		POOL_STRING = 0x01,
		POOL_INTEGER = 0x03,
		POOL_UINTEGER = 0x04,
		POOL_DOUBLE = 0x06,
		POOL_NAMESPACE = 0x08,
		POOL_FALSE = 0x0A,
		POOL_TRUE = 0x0B,
		POOL_NULL = 0x0C
	};
    
    typedef std::vector<Namespace*> NamespaceSet;

	AbcBlock();

    abc::Class* locateClass(MultiName &m);

	abc::Class* locateClass(const std::string& className);

    bool read(SWFStream& in);

	void update_global_name(unsigned int multiname_index);

    /// Scripts can contain several classes.
    //
    /// TODO: why on earth are Scripts implemented using Classes?
    const std::vector<abc::Class*>& scripts() const {
        return _scripts;
    }

    std::uint32_t uIntegerPoolAt(size_t i) const {
        checkBounds(i, _uIntegerPool);
        return _uIntegerPool[i];
    }

    const std::string& stringPoolAt(size_t i) const {
        checkBounds(i, _stringPool);
        return _stringPool[i];
    }

    std::int32_t integerPoolAt(size_t i) const {
        checkBounds(i, _integerPool);
        return _integerPool[i];
    }

    double doublePoolAt(size_t i) const {
        checkBounds(i, _doublePool);
        return _doublePool[i];
    }

    Method* methodPoolAt(size_t i) const {
        checkBounds(i, _methods);
        return _methods[i];
    }

    MultiName multinamePoolAt(size_t i) const {
        checkBounds(i, _multinamePool);
        return _multinamePool[i];
    }

    abc::Class* classPoolAt(size_t i) const {
        checkBounds(i, _classes);
        return _classes[i];
    }

    Namespace* namespacePoolAt(size_t i) const {
        checkBounds(i, _namespacePool);
        return _namespacePool[i];
    }

    void prepare(Machine* mach);

private:
	
    friend class abc::Trait;

	bool pool_value(std::uint32_t index, PoolConstant type, as_value &v);

	bool read_version();
	bool read_integer_constants();
	bool read_unsigned_integer_constants();
	bool read_double_constants();
	bool read_string_constants();
	bool read_namespaces();
	bool read_namespace_sets();
	bool read_multinames();
	bool read_method_infos();
	bool skip_metadata();
	bool read_instances();
	bool read_classes();
	bool read_scripts();
	bool read_method_bodies();

	void check_multiname_name(std::uint32_t name);

	void check_multiname_namespace(std::uint32_t ns);

	void check_multiname_namespaceset(std::uint32_t nsset);

	void setMultinameNames(MultiName *n, abc::URI ABCName);

	void setNamespaceURI(Namespace *ns, abc::URI ABCName);

	std::vector<std::int32_t> _integerPool;
	std::vector<std::uint32_t> _uIntegerPool;
	std::vector<double> _doublePool;
	std::vector<std::string> _stringPool;
	std::vector<Namespace*> _namespacePool;
	std::vector<NamespaceSet> _namespaceSetPool;
	std::vector<Method*> _methods;
	std::vector<MultiName> _multinamePool;
	std::vector<Class*> _classes; 
	std::vector<Class*> _scripts;

	string_table* _stringTable;
	SWFStream* _stream; // Not stored beyond one read.

	abc::Class *mTheObject;
	ClassHierarchy *mCH;

	std::uint32_t mVersion;


};

std::ostream& operator<<(std::ostream& o, AbcBlock::NamespaceConstant c);
std::ostream& operator<<(std::ostream& o, AbcBlock::MethodConstant c);
std::ostream& operator<<(std::ostream& o, AbcBlock::InstanceConstant c);
std::ostream& operator<<(std::ostream& o, AbcBlock::PoolConstant c);

} // namespace abc
} // namespace gnash


#endif 

