/*
 * Copyright (C) 2002,2003,2004 Daniel Heck
 * Copyright (C) 2007,2008,2009 Ronald Lamprecht
 *
 * 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 2
 * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */
#ifndef ENIGMA_OBJECT_HH
#define ENIGMA_OBJECT_HH

#include "ecl_alist.hh"
#include "ecl_math.hh"
#include "enigma.hh"
#include "Value.hh"
#include <map>
#include <stdint.h>

namespace enigma {

struct Message;

enum ObjectFlagsBits {
    OBJBIT_LIGHTNEWDIRS = 15,    ///< GridObject 4 direction bits with current/new light
    OBJBIT_LIGHTOLDDIRS = 240,   ///< GridObject 4 direction bits with previous light
    OBJBIT_LIGHTALLDIRS = 255,   ///< GridObject all 8 direction bits with old an new light
    OBJBIT_PHOTOACTIV = 1 << 8,  ///< GridObject registered as photo activ
    OBJBIT_INVERSE = 1 << 9,     ///< Object invert action value
    OBJBIT_NOP = 1 << 10,        ///< Object use nop instead toggle as default action
    OBJBIT_INIT = 1 << 11        ///< Object needs init message for @ finalization
};

enum ValidationResult {
    VALID_OK,
    VALID_ACCESS_DENIED,
    VALID_UNKNOWN_KEY,
    VALID_TYPE_MISMATCH,
    VALID_ILLEAGAL_VALUE
};

/**
 * Object is the base class for all "objects" in the world.
 * The most important facilities this class provides are:
 *
 * - A way to clone() and dispose() objects.  This is mainly used
 *     in function MakeObject() to create new objects of a given
 *     type.
 *
 * - A way to pass messages between unrelated objects via message().
 *     This allows us to send messages to objects from Lua and to
 *     decouple objects types as much as possible.
 *
 * - A way to get and set attributes.  These attributes are quite
 *     similar to instance variables, but they can be easily modified
 *     from Lua.  This makes it possible to modify certain object
 *     parameters (such as the text on a piece of paper or the color
 *     of an oxyd stone) in level descriptions.
 *
 * The various Object subclasses instances need to register a template
 * instance for each object name. To avoid the inclusion of every
 * subclass declaration file into the registry for registry driven forward
 * initialization we make use of the static file based initialization
 * that occurs prior to the main application startup. To be independent of
 * the undefined sequence in which the files are initialized we store the
 * template instances, object names and id's in function local static caches.
 * These caches are copied to the final runtime data structures on the main
 * application startup. We call this feature "boot"-initialization. The macros
 * BOOT_REGISTER_START and BOOT_REGISTER_END will be used once at the end of
 * every subclass file. They embrace the BootRegister() function calls that
 * register the templates.
 */
class Object {

public:
    enum ObjectType { OBJECT, OTHER, STONE, FLOOR, ITEM, ACTOR };

    typedef ecl::AssocList<std::string, Value> AttribMap;

    Object();
    Object(const char *kind);
    Object(const Object &src_obj);
    virtual ~Object();

    static Object *getObject(int id);
    int getId() const;

    /* ---------- depreceated methods ---------- */

    const AttribMap &get_attribs() const {
        return attribs;
    }  // just used by ObjectRepos::dump_info()

    /* ---------- Helper routines ---------- */

    void send_impulse(const GridPos &dest, Direction dir);

    /* ---------- Object interface ---------- */

    /**
     * The main object category name of the object that describes its gaming class.
     * All objects of a gaming class support the same attributes and messages. But
     * they may still be members of different kind subcategories. E.g. "st_panel"
     * is a class with "st_panel_n", "st_panel_s",... as kinds. A gaming object
     * class is often implemented as a separate C++ class. But sometimes several
     * gaming classes share a single C++ class. E.g. "st_panel", "st_bluesand" are
     * both implemented by the class "ClusterStone". In this case we talk about a
     * common "family".
     */
    virtual std::string getClass() const = 0;

    /**
     * The most specific object category name. Many objects change their kind during
     * their lifecycle. All attributes are evaluated to determine the current kind
     * of a gaming object.
     */
    std::string getKind() const;

    /**
     * Check the current kind of the object versus the given name.
     */
    bool isKind(const std::string &kind) const;

    bool validateMessage(std::string msg, Value arg);

    /**
     *
     */
    virtual Value message(const Message &m);

    /**
     * Store the value information for the given key with prior checking of
     * write allowance. All level code attribute settings should pass this
     * central call. If a set is granted the virtual unchecked setAttr is
     * executed.
     */
    void setAttrChecked(const std::string &key, const Value &val);

    /**
     * Store the value information for the given key. If it is a XML declared
     * system attribute or a user attribute it will be stored in the attribute
     * map. Otherwise an ivar may be set. Nothing happens if the attribute is
     * declared as read only. If the value is "nil" the attribute will be reset
     * to its declared default. If the default is "nil", too, or if it is a user
     * attribute, it will be deleted in the attribute map. In these cases a read
     * will return "nil" either way.
     */
    virtual void setAttr(const std::string &key, const Value &val);

    /**
     * Get an attribute or value for the given key with prior checking of
     * read allowance. All level code attribute reads should pass this central
     * call. If a read is granted the virtual unchecked getAttr is executed.
     */
    Value getAttrChecked(const std::string &key) const;

    /**
     * Get an attribute that has been set or that stands as a proxy for a
     * trait or ivar. Object itself will just return attribute values
     * that are stored in its attribute map. For not existing attributes
     * a XML declared standard default value will be returned. If the default
     * is "nil" type DEFAULT will be returned.
     *
     * Subclasses may override this method to supply values of traits or
     * ivars. This way levels can gain read access to attributes that can
     * not to be stored in the attribute map due to performance reasons.
     */
    virtual Value getAttr(const std::string &key) const;

    /**
     * Get an attribute or a special given default value. This method
     * gets attributes like the simple argumented getAttr method but
     * returns the given default value instead of a DEFAULT value if
     * no explicit attribute exists.
     */
    Value getDefaultedAttr(const std::string &key, Value defaultValue) const;

    virtual Object *clone() = 0;
    virtual void dispose() = 0;

    virtual void warning(const char *format, ...) const;
    virtual ObjectType getObjectType() const;
    void transferName(Object *target);
    virtual void transferIdentity(Object *target);
    virtual double squareDistance(const Object *other) const;
    virtual bool isSouthOrEastOf(const Object *other) const;

protected:
    /** This function is used by all triggers, switches etc. that
     * perform some particular action when activated (like opening
     * doors or switching lasers on and off). It interprets the
     * "action" and "target" attributes of `o'.
     */
    void performAction(const Value &val);

    virtual Value invertActionValue(const Value &val) const;

    /**
     * Evaluate multiple destinations described by tokenized destination
     * attribute where every token may be a group by itself. All valid
     * destinations are indexed in the sequence of tokens and group positions.
     * The position at the given index is returned as the destination position.
     * If the caller did index beyond the last valid position this convenience
     * method returns false, otherwise true to mark a valid position.
     * Objects with destinations like Vortex and Horse make use of this method.
     * @arg idx      requested index of destination starting with 0
     * @arg destpos  return value for position that is centered to a grid
     * @return       validity of position at given index
     */
    bool getDestinationByIndex(int idx, ecl::V2 &dstpos);

    /**
     * A central managed container for 32 single bit flags to be used by
     * subclasses. Object is reponsible for efficiently cloning, saving and
     * restoring these flags on demand. Subclasses use the bits as follows:
     *
     * - Bit 0-15 are reserved for classes Object to GridObject
     *
     * - Bit 16-23 are reserved for classes Item, Stone, Floor, Actor
     *
     * - Bit 24-31 are reserved for the final subclass
     *
     * See enumeration ObjectFlagsBits for Bitmasks of Bit 0-15. Note that
     * Bitmasks would not have been an alternative as the subclasses may
     * and will use the upper bits in a quite different manner.
     */
    uint32_t objFlags;

private:
    friend void InitWorld();  // for bootFinished() access

    static int next_id;
    static std::map<int, Object *> objects;

    int id;
    AttribMap attribs;

    void finalizeNearestObjectReferences(std::string attr);
    void finalizeNearestObjectReferences();

    static int getNextId(Object *obj, bool bootFinished);
    static void bootFinished();
    static void freeId(int id);
};

}  // namespace enigma

#define BOOT_REGISTER_START \
    namespace {             \
    bool do_boot_register() {

#define BOOT_REGISTER_END                             \
    return true;                                      \
    }                                                 \
    static bool boot_registered = do_boot_register(); \
    }

#define CLONEOBJ(TYPE)                        \
    TYPE *clone() override { return new TYPE(*this); } \
    void dispose() override { delete this; }

#define CLONEACTOR(TYPE)           \
    TYPE *clone() override {                \
        TYPE *o = new TYPE(*this); \
        o->init();                 \
        return o;                  \
    }                              \
    void dispose() override { delete this; }

#define INSTANCELISTOBJ(TYPE)                                            \
    typedef std::vector<TYPE *> InstanceList;                            \
    static InstanceList instances;                                       \
    TYPE *clone() override {                                                      \
        TYPE *o = new TYPE(*this);                                       \
        instances.push_back(o);                                          \
        return o;                                                        \
    }                                                                    \
    void dispose() override {                                                     \
        instances.erase(find(instances.begin(), instances.end(), this)); \
        delete this;                                                     \
    }

#endif  // ENIGMA_OBJECT_HH
