/*
 * server/Entity.h:
 *
 * Copyright (C) 2000 John Watson, jwatson@tempusmud.com
 * 
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 */

class Entity;
#ifndef __Entity_h__
#define __Entity_h__

#include "../util/Vector.h"

class Sound;
class ostream;
class istream;

class Entity {

        char * _name;
        char * _alias;

        /** total weight of contents 
         * unit grams
         */
        int _containedWeight;
            
        int _boundingRadius;
        
        int _height;

        /**
         * the codename of the icon representing this entity
         */
        char * _iconName;

        /**
         * @label container 
         */
        class Entity * _container;
        
        static unsigned long LAST_SERIAL_NUMBER;

        unsigned long _serialNumber;

        void init( const char * name, const char * alias, float radius, int height, float angle );

        unsigned short _stepHeight;

        Vector<class Sound *> _sounds;

        unsigned char _illumination[3];
        
protected:
        
        /** 
         * weight of base object
         * unit grams
         */
        int _weight;
        
        float _model_trans[3];
        
        char * _modelName;
        char * _skinName;

        class Sprite * _sprite;
        
        class Entity * _owner;

        int _flags;
        
        /** 
         * angle zero is along the positive x axis 
         * units are degrees
         */

        float _angle;

        /**
         * angle zero is horizontal, -90 is down, +90 is up
         */

        float _tilt_angle;

        /** the position of this Entity inside its container 
         * This does not have meaning for all containers, e.g. a character's contents
         */
        float _pos[3];
        
        /**
         * units of cm/sec
         */
        float _velocity[3];

        /**
         * the sector that is the floor for this entity
         */
        class Sector * _floorSector;
        class Sector * _ceilingSector;
        
        class Sector * _currentSector;

        class LightSource * _light;
        
        /**
         * the entity upon which this entity is standing or resting
         */
        class Entity * _floorEntity;
        class Entity * _ceilingEntity;

        void setFloorCeilingEntities( class Entity * ent ) const;

        class Vector< class Entity * > _contents;
        
        static const char * setString( char ** str, const char * name );
        
        inline const char * setIconName( const char * name ) { return setString( &_iconName, name ); }
        inline const char * setName( const char * name ) {     return setString( &_name, name ); }
        inline const char * setAlias( const char * alias ) {   return setString( &_alias, alias ); }

        bool addContent( class Entity * ent );
                
        bool removeContent( class Entity * ent );

        class Entity * getContentAt( size_t index ) const;

        class Entity * removeContentAt( size_t index );

        float _selfPropulsion[3];

        int _frame;
        unsigned int _last_tick;

        int _vnum;

public:

        inline int getVnum() const { return _vnum; }

        class Camera * camera;
        inline const char * setModelName( const char * name ) { return setString( &_modelName, name ); }
        inline const char * setSkinName( const char * name ) { return setString( &_skinName, name ); }

        inline int getFrame() const { return _frame; }

        mutable bool die;
        
        inline const class Sprite * getSprite() const { return _sprite; }

        static const int FLYING     = ( 1 << 0 );
        static const int LEVITATING = ( 1 << 1 );

        inline void setFlag( int flag ) { _flags |= flag; }
        inline void removeFlag( int flag ) { _flags &= ~flag; }
        inline void toggleFlag( int flag ) { _flags = _flags ^ flag; }
        inline bool isFlagSet( int flag ) const { return _flags & flag; }
        inline int getFlags() const { return _flags; }

        inline class LightSource * getLight() const { return _light; }
        void setLight( class LightSource * lt );

        bool emitSound( class Sound * s );
        int purgeDeadSounds();

        const Vector< class Sound * > & getSounds() const { return _sounds; }
        
        static const int DEFAULT_FLOOR_HEIGHT = 0;
        static const int DEFAULT_CEILING_HEIGHT = 512;

        inline unsigned short getStepHeight() const { return _stepHeight; }
        inline void setStepHeight( short s ) { _stepHeight = s; }
        
        inline const class Sector * getSector() const { return _currentSector; }
        inline const class Sector * getFloorSector() const { return _floorSector; }
        inline const class Sector * getCeilingSector() const { return _ceilingSector; }
        inline const class Entity * getFloorEntity() const { return _floorEntity; }
        inline const class Entity * getCeilingEntity() const { return _ceilingEntity; }

        inline const unsigned char * getIllumination() const { return _illumination; }
        inline void setIllumination( unsigned char r, unsigned char g, unsigned char b ) {
                _illumination[0] = r;
                _illumination[1] = g;
                _illumination[2] = b;
        }

        float getMinZ() const;
        float getMaxZ() const;
                            
        Entity();
        Entity( class ProtoEntity * proto );
        Entity( const char * name, const char * alias, float radius = 0, int height = 0, float angle = 0 );
        virtual ~Entity();

        inline const int getBoundingRadius() const { return _boundingRadius; }
        inline const int setBoundingRadius( int rad );

        inline void setHeight( int h ) { _height = h; }
        inline const int getHeight() const { return _height; }

        inline const float getAngle() const         { return _angle; }
        inline const float getTilt() const { return _tilt_angle; }

        inline void setAngle( float a );
        inline void setTilt( float t );

        /** the base weight of this container */
        inline int getWeight() const              { return _weight; }
        /** the total weight of the contents of this container */
        inline int getContainedWeight() const     { return _containedWeight; }
        /** the sum of base weight and contents weight */
        virtual inline int getTotalWeight() const { return _weight + _containedWeight; }
        
        inline class Entity * getContainer() const { return _container; }

        /** how many entities does this one hold */
        inline size_t getNumContents() const      { return _contents.size(); }
        
        const Vector<class Entity *> & getContents() const;

        inline const char * getIconName() const   { return _iconName; }

        inline const float * getModelTrans() const { return _model_trans; }
        inline const char * getModelName() const { return _modelName; }
        inline const char * getSkinName() const { return _skinName; }

        inline const char * getName() const       { return _name; }
        inline const char * getAlias() const      { return _alias; }

        bool isName( const char * keyword ) const;

        /** recursively traverses containers, modifying weight for each */
        virtual void setWeight( int weight );

        bool setPosition( float x, float y, float z );
        bool setPosition( const float * pos );

        bool moveRelative( float x, float y, float z );
        bool moveAbsolute( float x, float y, float z );

        bool rotate( float angle, float tilt );

        inline const float * getPos() const { return _pos; }
        inline const float * getVelocity() const { return _velocity; }

        class Character * getCarriedBy() const;
        class Item      * getContainerItem() const;
        class Zone      * getContainerZone() const;

        bool isIntersecting( const class Entity * other, bool reverse, int x, int y, int z ) const;

        //
        // check intersection of a rectangle
        //
        
        bool isIntersecting( double length, int width, int origin[2], double theta );

        //
        // check intersection of another entity
        //
        
        bool isIntersecting( const class Entity * other, int dimensions );
        
        //
        // check the intersection with an arbitrary cylinder in 3d
        //

        bool isIntersecting( const float otherPos[3], int rad, int height );
        
        //
        // units of cm/sec
        //

        void setVelocity( float x, float y, float z );
        void setRelativeVelocity( float x, float y, float z );
        void setRelativeSelfPropulsion( float x, float y, float z );
        void setSelfPropulsion( float x, float y, float z );
        void addRelativeSelfPropulsion( float x, float y, float z );

        const float * getSelfPropulsion() const { return _selfPropulsion; };

        virtual void physicsUpdateContents();
        
        class Entity * findCollider( const Vector<class Entity*> vec, float * scale );//result_x, float * result_y );

        virtual void loadEntity( istream & is );
        virtual void writeEntity( ostream & os );

        friend ostream & operator<<( ostream & os, const Entity & ent );

        friend class Zone;

        virtual bool collideWith( class Entity * other, const float * normal, unsigned int tick );
        virtual bool think( unsigned int tick );
        virtual bool takeDamage( int type, int location, int amount );
};

#endif


