/*
 * server/Entity.cc:
 *
 * 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.
 * 
 */

#include "Entity.h"

#include "Character.h"
#include "Item.h"
#include "Zone.h"
#include "Sector.h"
#include "Sound.h"
#include "LightSource.h"
#include "ProtoEntity.h"
#include "Sprite.h"

#include "../util/VMath.h"
#include "../util/Persist.h"

#include <string>
#include <math.h>
#include <iostream>
#include <iomanip>
#include <cstdio>

#ifndef M_PI
#define M_PI           3.14159265358979323846  /* pi */
#endif

unsigned long Entity::LAST_SERIAL_NUMBER = 0;

void Entity::init( const char * name, const char * alias, float radius, int height, float angle )
{
        die = false;
        camera = 0;

        _illumination[0] = _illumination[1] = _illumination[2] = 255;
        _last_tick = 0;
        _owner = 0;
        _flags = 0;
        _light = 0;
        _weight = 0;
        _iconName = 0;
        _serialNumber = ++LAST_SERIAL_NUMBER;
        _name = 0;
        _alias = 0;
        _floorSector = 0;
        _ceilingSector = 0;
        _floorEntity = 0;
        _ceilingEntity = 0;
        _stepHeight = 0;
        _modelName = 0;
        _skinName = 0;
        _sprite = 0;
        _frame  = 0;
        _container = 0;

        if ( name )
                _name = strdup( name );
        if ( alias )
                _alias = strdup( alias );

        // entities start life as a singular point

        setBoundingRadius( radius );
        setHeight( height );
        setAngle( angle );
        setTilt( 0 );

        _pos[0] = _pos[1] = _pos[2] = 0;
        _model_trans[0] = _model_trans[1] = _model_trans[2] = 0;
        setVelocity( 0, 0, 0 );
        setSelfPropulsion( 0, 0, 0 );
}

Entity::Entity() {
        init( 0, 0, 0, 0, 0 );
}
 
/**
 * default rad = 0, height = 0, angle = 0
 */
Entity::Entity( const char * name, const char * alias, float radius, int height, float angle ) {
        
        init( name, alias, radius, height, angle );
        setIconName( name );
}

Entity::Entity( class ProtoEntity * proto ) {

        init( proto->name, proto->alias, proto->radius, proto->height, 0 );
        
        _weight = proto->weight;

        if ( * proto->model ) {
                setModelName( proto->model );
        }
        else {
                setModelName( 0 );
                _sprite = new Sprite( 1 );
                char buf[64];
                sprintf( buf, "icons/%.48s", proto->icon );
                _sprite->names[0] = strdup( buf );
        }
        
        char buf[512];
        
        sprintf( buf, "models/%.256s", proto->skin );
        setSkinName( buf );
        
        sprintf( buf, "icons/%.256s", proto->icon );
        setIconName( buf );

        _model_trans[0] = -proto->model_trans[0];
        _model_trans[1] = -proto->model_trans[1];

        _model_trans[2] = -proto->model_trans[2];
        
        _vnum = proto->vnum;
      
}
        
Entity::~Entity() {
        
        if ( _name ) {
                free( _name );
        }
        if ( _alias ) {
                free( _alias );
        }
        if ( _iconName ) {
                free( _iconName );
        }
        if ( _modelName ) {
                free( _modelName );
        }

        if ( _sprite ) {
                if ( _sprite->clientSprite )
                        cout << " :: WARNING: leaking clientSprite from Entity" << endl;
                delete _sprite;
        }

        if ( _light ) {
                _light->release();
        }

        _sounds.clear();

        for ( unsigned int i = 0; i < _contents.size(); ++i ) {
                delete _contents[i];
        }
}

bool Entity::emitSound( class Sound * s ) {
        return _sounds.addUnique( s );
}

int Entity::purgeDeadSounds() {
        int num = 0;
        
        for ( bool found = true; found; ) {
                found = false;
                for ( Vector< class Sound * >::iterator iter = _sounds.begin();
                      iter != _sounds.end(); ++iter ) {
                        if ( (*iter)->die ) {

                                if ( (*iter)->internal_delete ) {
                                        delete *iter;
                                }

                                _sounds.erase( iter );
                                found = true;
                                ++num;
                                break;
                        }
                }
        }
        return num;
}


const char * Entity::setString( char ** str, const char * name ) {
        if ( *str )
                free( *str );
        return ( *str = name ? strdup( name ) : 0 );
}

bool Entity::addContent( class Entity * ent ) {
        
        ent->_container = this;
        _contents.push_back( ent );
        _containedWeight += ent->getTotalWeight();

        return true;
}

bool Entity::removeContent( class Entity * ent ) {

        if ( _contents.remove( ent ) ) {
                ent->_container = 0;
                _containedWeight -= ent->getTotalWeight();
                return true;
        }
        
        return false;
}

void Entity::setWeight( int weight ) {
        if ( _container )
                _container->setWeight( _container->getWeight() + weight - _weight );

        _weight = weight;
}

const int Entity::setBoundingRadius( int rad ) {
        _boundingRadius = rad;
        return _boundingRadius;
}

bool Entity::setPosition( float x, float y, float z ) {
        _pos[0] = x;
        _pos[1] = y;
        _pos[2] = z;
        return true;
}

bool Entity::setPosition( const float * pos ) {
        _pos[0] = pos[0];
        _pos[1] = pos[1];
        _pos[2] = pos[2];
        return true;
}


class Entity * Entity::getContentAt( size_t index ) const {
        if ( index >= 0 && index < _contents.size() ) {
                return _contents[ index ];
        }

        return 0;
}

class Entity * Entity::removeContentAt( size_t index ) {
        class Entity * ent = getContentAt( index );
        if ( ent && removeContent( ent ) ) {
                return ent;
        }
        
        return 0;
}
        
class Character * Entity::getCarriedBy() const {
        
        if ( _container )
                return dynamic_cast<Character*> (_container);

        return 0;
}

class Item * Entity::getContainerItem() const {
        
        if ( _container )
                return dynamic_cast<Item*> (_container);

        return 0;
}
                
class Zone * Entity::getContainerZone() const {
        if ( _container )
                return dynamic_cast<Zone*> ( _container );
        return 0;
}

bool Entity::isIntersecting( const class Entity * other, bool reverse, int x, int y, int z ) const {
        
        //
        // testing against the interior of a bounding box
        //
        
        if ( reverse ) {
        }
        return false;
}

bool Entity::rotate( float angle, float tilt ) {
        if ( angle ) setAngle( _angle + angle );
        if ( tilt ) setTilt( _tilt_angle + tilt );
        return true;

}

/**
 *
 * Find the floor and ceiling entities for ent.
 * The 'this' object is the container.
 */
void Entity::setFloorCeilingEntities( class Entity * ent ) const {
        
        ent->_floorEntity = ent->_ceilingEntity = 0;

        Vector< class Entity * >::const_iterator end = _contents.end();
        for ( Vector< class Entity * >::const_iterator iter = _contents.begin();
              iter != end; ++iter ) {
                class Entity *other = *iter;
                
                if ( ent == other->_owner || 
                     other == ent->_owner || 
                     ( ent->_owner && other->_owner == ent->_owner ) )
                        continue;

                if ( ent->isIntersecting( other, 2 ) ) {

                        //
                        // check floor
                        //

                        if ( ent->_pos[2] >= ( other->_pos[2] + other->_height ) ) {
                                
                                if ( ! ent->_floorEntity || ( other->_pos[2] > ent->_floorEntity->_pos[2] ) )
                                        ent->_floorEntity = other;
                        }

                        //
                        // check ceiling
                        //

                        if ( other->_pos[2] >= ( ent->_pos[2] + ent->_height ) ) {
                                
                                if ( ! ent->_ceilingEntity || ( other->_pos[2] < ent->_ceilingEntity->_pos[2] ) )
                                        ent->_ceilingEntity = other;
                        }
                }
        }
}

float Entity::getMinZ() const {
        float h = _floorSector ? _floorSector->getFloorHeight() : DEFAULT_FLOOR_HEIGHT;
        
        if ( _floorEntity ) {
                h = max( h, ( _floorEntity ? ( _floorEntity->_pos[2] + _floorEntity->_height )  : DEFAULT_FLOOR_HEIGHT ) );
        }
        return h;
}

float Entity::getMaxZ() const {
        float h = _ceilingSector ? ( _ceilingSector->getCeilingHeight() ) : DEFAULT_CEILING_HEIGHT;
                
        if ( _ceilingEntity )
                h = max( h, ( _ceilingEntity ? ( _ceilingEntity->_pos[2] )  : DEFAULT_CEILING_HEIGHT ) );
        
        h -= _height;
        
        return h;
}

bool Entity::moveRelative( float x, float y, float z ) {
        
        return false;
}

bool Entity::moveAbsolute( float x, float y, float z ) {
        float oldpos[3] = { _pos[0], _pos[1], _pos[2] };
        _pos[0] += x;
        _pos[1] += y;
        _pos[2] += z;
        return _pos[0] != oldpos[0] || _pos[1] != oldpos[1] || _pos[2] != oldpos[2];
}

bool Entity::isName( const char * keyword ) const {
        
        return ! strcasecmp( _alias, keyword );
}

const Vector<class Entity*> & Entity::getContents() const {
        return _contents;
}


void Entity::setAngle( float a ) {
        _angle = VMath::clampAngle360( a );
}

void Entity::setTilt( float t ) {
        _tilt_angle = max( min( t, 90.0F ), -90.0F );
}

void Entity::setVelocity( float x, float y, float z ) {
        _velocity[0] = x;
        _velocity[1] = y;
        _velocity[2] = z;
}

void Entity::setRelativeVelocity( float x, float y, float z ) {
        _velocity[2] = z;

        double theta        = (double) _angle * M_PI / 180;
        
        _velocity[0] = ( x * cos( theta ) - y * sin( theta ) );
        _velocity[1] = ( x * sin( theta ) + y * cos( theta ) );

        if ( _velocity[0] > 0 )
                _velocity[0] = floor( _velocity[0] * 100 ) / 100;
        else
                _velocity[0] = ceil( _velocity[0] * 100 ) / 100;

        if ( _velocity[1] > 0 )
                _velocity[1] = floor( _velocity[1] * 100 ) / 100;
        else
                _velocity[1] = ceil( _velocity[1] * 100 ) / 100;
}

void Entity::setRelativeSelfPropulsion( float x, float y, float z ) {
        _selfPropulsion[2] = z;

        double theta        = (double) _angle * M_PI / 180;
        
        _selfPropulsion[0] = ( x * cos( theta ) - y * sin( theta ) );
        _selfPropulsion[1] = ( x * sin( theta ) + y * cos( theta ) );

        if ( _selfPropulsion[0] > 0 )
                _selfPropulsion[0] = floor( _selfPropulsion[0] * 100 ) / 100;
        else
                _selfPropulsion[0] = ceil( _selfPropulsion[0] * 100 ) / 100;

        if ( _selfPropulsion[1] > 0 )
                _selfPropulsion[1] = floor( _selfPropulsion[1] * 100 ) / 100;
        else
                _selfPropulsion[1] = ceil( _selfPropulsion[1] * 100 ) / 100;
}

void Entity::addRelativeSelfPropulsion( float x, float y, float z ) {
        _selfPropulsion[2] += z;

        double theta        = (double) _angle * M_PI / 180;
        
        _selfPropulsion[0] += ( x * cos( theta ) - y * sin( theta ) );
        _selfPropulsion[1] += ( x * sin( theta ) + y * cos( theta ) );
        
        if ( _selfPropulsion[0] > 0 )
                _selfPropulsion[0] = floor( _selfPropulsion[0] * 100 ) / 100;
        else
                _selfPropulsion[0] = ceil( _selfPropulsion[0] * 100 ) / 100;

        if ( _selfPropulsion[1] > 0 )
                _selfPropulsion[1] = floor( _selfPropulsion[1] * 100 ) / 100;
        else
                _selfPropulsion[1] = ceil( _selfPropulsion[1] * 100 ) / 100;
}

void Entity::setSelfPropulsion( float x, float y, float z ) {
        _selfPropulsion[0] = x;
        _selfPropulsion[1] = y;
        _selfPropulsion[2] = z;
}

static bool circlesIntersect( const float aPos[2], const float bPos[2], int ar, int br ) {
        float dist = VMath::distance2d( aPos, bPos );
        return ( dist < ( ar + br ) );
}


bool Entity::isIntersecting( const class Entity * other, int dimensions ) {

        if ( dimensions == 2 ) {

                float bound_limit = _boundingRadius + other->_boundingRadius;
                float diff;

                diff = fabs( _pos[0] - other->_pos[0] );

                if ( diff > bound_limit )
                        return false;
                
                diff = fabs( _pos[1] - other->_pos[1] );

                if ( diff > bound_limit )
                        return false;

                return circlesIntersect( _pos, other->_pos, _boundingRadius, other->_boundingRadius );
        }
        
        return false;
}

bool Entity::isIntersecting( const float otherPos[3], int rad, int height ) {

        return false;
}

bool Entity::isIntersecting( double length, int width, int origin[2], double theta ) {
        
        return false;
}
       
/**
 *
 * @param
 */
inline bool pointInsideRect( const float point[2], 
                             const float rectOrigin[2],
                             const float rectVector[2], 
                             const int rectHalfWidth, 
                             const float magnitude )
{
        
        float relPos[2] = {
                point[0] - rectOrigin[0],
                point[1] - rectOrigin[1]
        };

        float dotProd = VMath::dotProduct2d( rectVector, relPos );
        
        if ( dotProd <= 0 ) {
                return false;
        }
        
        float displacement = VMath::length2d( relPos );
        
        if ( displacement == 0 ) {
                return false;
        }

        float adjacentLen = dotProd / magnitude;
                
        if ( adjacentLen > magnitude ) {
                return false;
        }
        
        float oppositeLen = VMath::triangleSide( adjacentLen, displacement );
        
        if ( oppositeLen >= rectHalfWidth ) {
                return false;
        }

        return true;
        
                
}

class Entity * Entity::findCollider(  const Vector<class Entity*> vec, float * scale ) { //float * result_x, float * result_y ) {
                                     
        *scale = 1;

        float magnitude = VMath::length2d( _velocity );
        
        if ( magnitude < 1 ) {
                return 0;
        }

        class Entity * collider = 0;

        float collider_dist = 0;
        
        float dst[2] = {
                _pos[0] + _velocity[0],
                _pos[1] + _velocity[1]
        };

        Vector< class Entity * >::const_iterator end = vec.end();
        for ( Vector< class Entity * >::const_iterator iter = vec.begin();
              iter != end; ++iter ) {
                
                // TODO: skip entities that are BEHIND us

                class Entity *other = *iter;

                //
                // skip self
                //

                if ( other == this )
                        continue;

                if ( this == other->_owner || 
                     other == _owner || 
                     ( _owner && other->_owner == _owner ) )
                        continue;                

                //
                // skip entities we are already above/below
                //

                if ( other == this->_floorEntity || other == this->_ceilingEntity )
                        continue;

                if ( this->_pos[2] >= ( other->_pos[2] + other->_height ) ||
                     other->_pos[2] >= ( this->_pos[2] + this->_height ) )
                        continue;
                
                //
                // check if the minimal rect is intersected
                //

                if ( pointInsideRect( other->_pos,
                                      _pos,
                                      _velocity,
                                      _boundingRadius,
                                      magnitude ) ) {

                        float my_dist = 
                                VMath::distance2d( other->_pos, _pos ) - _boundingRadius - other->_boundingRadius;
                        if ( ! collider || ( my_dist < collider_dist ) ) {
                                collider_dist = my_dist;
                                collider = other;
                        }

                        continue;
                }
                
                if ( circlesIntersect( dst, other->_pos, _boundingRadius, other->_boundingRadius ) ) {

                        float my_dist = 
                                VMath::distance2d( other->_pos, _pos ) - _boundingRadius - other->_boundingRadius;
                        if ( ! collider || ( my_dist < collider_dist ) ) {
                                collider_dist = my_dist;
                                collider = other;
                        }
                        continue;
                }
        }

        if ( collider ) {
                
                float disp = VMath::distance2d( _pos, collider->_pos );

                float op_tmp[2] = {
                        collider->_pos[0] - _pos[0],
                        collider->_pos[1] - _pos[1]
                };

                float dotProd = floor( VMath::dotProduct2d( _velocity, op_tmp ) );
                
                //
                // this is a sanity check.  this also ensures that we can escape when embedded
                // inside another entity ( an error condition )
                //

                if ( dotProd <= 0 ) {
                        // scale is still 1
                }
                else {

                        float adj  = dotProd / magnitude;
                
                        float opp  = VMath::triangleSide( adj, disp );

                        float rad_sum = _boundingRadius + collider->_boundingRadius;
                
                        float nogo = VMath::triangleSide( opp, rad_sum );

                        //
                        // not sure why this positive clamp is necessary
                        //
                        
                        float ok_mag = adj - nogo;
                        
                        ok_mag = floor( ok_mag ) / magnitude;
                        
                        *scale = ok_mag;
                        //                        *result_x = _velocity[0] * ok_mag;
                        //                        *result_y = _velocity[1] * ok_mag;
                }
        }
        
        return collider;
}

class Entity * hasIntersector( Vector<class Entity *> vec, class Entity * ent ) {
        
        Vector< class Entity * >::const_iterator end = vec.end();
        for ( Vector< class Entity * >::const_iterator iter = vec.begin();
              iter != end; ++iter ) {    

                class Entity *other = *iter;

                if ( ent == other )
                        continue;

                if ( ent->isIntersecting( other, 2 ) )
                        return other;
        }
        
        return 0;
}

void Entity::physicsUpdateContents() {
        // TODO: replace with fixed code
}

void Entity::loadEntity( istream & is ) {
        char buf[1024];
        
        setName(     Persist::readString( is, buf, 1024 ) );
        setAlias(    Persist::readString( is, buf, 1024 ) );
        setIconName( Persist::readString( is, buf, 1024 ) );
}

void Entity::writeEntity( ostream & os ) {
        
        Persist::writeString( os, _name );
        Persist::writeString( os, _alias );
        Persist::writeString( os, _iconName );
}

void Entity::setLight( class LightSource * lt ) {
        if ( _light )
                _light->release();
        _light = lt;

}

ostream & operator<<( ostream & os, const Entity & ent ) {
        os << " Entity: [name='" << ent._name << "', "
           << "alias='" << ent._alias << "', "
           << "icon='" << ent._iconName << ", "
           << "sprite='" << ent._sprite << "']";
        return os;
}

bool Entity::collideWith( class Entity * other, const float * normal, unsigned int tick ) {
        return false;
}


bool Entity::think( unsigned int tick ) {
        return false;
}

bool Entity::takeDamage( int type, int location, int amount ) {
        return false;
}
