/*
 * Copyright (C) 1997-2001 Id Software, 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 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.
 */

package lwjake2.game;

import lwjake2.Defines;
import lwjake2.qcommon.Com;
import lwjake2.util.Lib;
import lwjake2.util.QuakeFile;

import java.io.IOException;

public class edict_t {

    /** Constructor. */
    public edict_t(int i) {
        s.number = i;
        index = i;
    }

    /** Used during level loading. */
    public void cleararealinks() {
        area = new link_t(this);
    }

    /** Integrated entity state. */
    public entity_state_t s = new entity_state_t(this);

    public boolean inuse;

    public int linkcount;

    /**
     * FIXME: move these fields to a server private sv_entity_t. linked to a
     * division node or leaf.
     */
    public link_t area = new link_t(this);

    /** if -1, use headnode instead. */
    public int num_clusters;

    public int clusternums[] = new int[Defines.MAX_ENT_CLUSTERS];

    /** unused if num_clusters != -1. */
    public int headnode;

    public int areanum, areanum2;

    //================================

    /** SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc. */
    public int svflags;

    public float[] mins = { 0, 0, 0 };

    public float[] maxs = { 0, 0, 0 };

    public float[] absmin = { 0, 0, 0 };

    public float[] absmax = { 0, 0, 0 };

    public float[] size = { 0, 0, 0 };

    public int solid;

    public int clipmask;

    //================================
    public int movetype;

    public int flags;

    public String model = null;

    /** sv.time when the object was freed. */
    public float freetime;

    //
    // only used locally in game, not by server
    //
    public String message = null;

    public String classname = "";

    public int spawnflags;

    public float timestamp;

    /** set in qe3, -1 = up, -2 = down */
    public float angle;

    public String target = null;

    public String targetname = null;

    public String killtarget = null;

    public String team = null;

    public String pathtarget = null;

    public String deathtarget = null;

    public String combattarget = null;

    public edict_t target_ent = null;

    public float speed, accel, decel;

    public float[] movedir = { 0, 0, 0 };

    public float[] pos1 = { 0, 0, 0 };

    public float[] pos2 = { 0, 0, 0 };

    public float[] velocity = { 0, 0, 0 };

    public float[] avelocity = { 0, 0, 0 };

    public int mass;

    public float air_finished;

    /** per entity gravity multiplier (1.0 is normal). */
    public float gravity;

    /** use for lowgrav artifact, flares. */

    public edict_t goalentity = null;

    public edict_t movetarget = null;

    public float yaw_speed;

    public float ideal_yaw;

    public float nextthink;

    public EntThinkAdapter prethink = null;

    public EntThinkAdapter think = null;

    public EntBlockedAdapter blocked = null;

    public EntTouchAdapter touch = null;

    public EntUseAdapter use = null;

    public EntPainAdapter pain = null;

    public EntDieAdapter die = null;

    /** Are all these legit? do we need more/less of them? */
    public float touch_debounce_time;

    public float pain_debounce_time;

    public float damage_debounce_time;

    /** Move to clientinfo. */
    public float fly_sound_debounce_time;

    public float last_move_time;

    public int health;

    public int max_health;

    public int gib_health;

    public int deadflag;

    public int show_hostile;

    public float powerarmor_time;

    /** target_changelevel. */
    public String map = null;

    /** Height above origin where eyesight is determined. */
    public int viewheight;

    public int takedamage;

    public int dmg;

    public int radius_dmg;

    public float dmg_radius;

    /** make this a spawntemp var? */
    public int sounds;

    public int count;

    public edict_t chain = null;

    public edict_t enemy = null;

    public edict_t oldenemy = null;

    public edict_t activator = null;

    public edict_t groundentity = null;

    public int groundentity_linkcount;

    public edict_t teamchain = null;

    public edict_t teammaster = null;

    /** can go in client only. */
    public edict_t mynoise = null;

    public edict_t mynoise2 = null;

    public int noise_index;

    public int noise_index2;

    public float volume;

    public float attenuation;

    /** Timing variables. */
    public float wait;

    /** before firing targets... */
    public float delay;

    public float random;

    public float teleport_time;

    public int watertype;

    public int waterlevel;

    public float[] move_origin = { 0, 0, 0 };

    public float[] move_angles = { 0, 0, 0 };

    /** move this to clientinfo? . */
    public int light_level;

    /** also used as areaportal number. */
    public int style;

    public gitem_t item; // for bonus items

    /** common integrated data blocks. */
    public moveinfo_t moveinfo = new moveinfo_t();

    public monsterinfo_t monsterinfo = new monsterinfo_t();

    public gclient_t client;

    public edict_t owner;

    /** Introduced by rst. */
    public int index;

    /////////////////////////////////////////////////

    public boolean setField(String key, String value) {

        if (key.equals("classname")) {
            classname = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),

        if (key.equals("model")) {
            model = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),

        if (key.equals("spawnflags")) {
            spawnflags = Lib.atoi(value);
            return true;
        } // F_INT),

        if (key.equals("speed")) {
            speed = Lib.atof(value);
            return true;
        } // F_FLOAT),

        if (key.equals("accel")) {
            accel = Lib.atof(value);
            return true;
        } // F_FLOAT),

        if (key.equals("decel")) {
            decel = Lib.atof(value);
            return true;
        } // F_FLOAT),

        if (key.equals("target")) {
            target = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),

        if (key.equals("targetname")) {
            targetname = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),

        if (key.equals("pathtarget")) {
            pathtarget = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),

        if (key.equals("deathtarget")) {
            deathtarget = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),
        if (key.equals("killtarget")) {
            killtarget = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),

        if (key.equals("combattarget")) {
            combattarget = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),

        if (key.equals("message")) {
            message = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),

        if (key.equals("team")) {
            team = GameSpawn.ED_NewString(value);
            Com.dprintln("Monster Team:" + team);
            return true;
        } // F_LSTRING),

        if (key.equals("wait")) {
            wait = Lib.atof(value);
            return true;
        } // F_FLOAT),

        if (key.equals("delay")) {
            delay = Lib.atof(value);
            return true;
        } // F_FLOAT),

        if (key.equals("random")) {
            random = Lib.atof(value);
            return true;
        } // F_FLOAT),

        if (key.equals("move_origin")) {
            move_origin = Lib.atov(value);
            return true;
        } // F_VECTOR),

        if (key.equals("move_angles")) {
            move_angles = Lib.atov(value);
            return true;
        } // F_VECTOR),

        if (key.equals("style")) {
            style = Lib.atoi(value);
            return true;
        } // F_INT),

        if (key.equals("count")) {
            count = Lib.atoi(value);
            return true;
        } // F_INT),

        if (key.equals("health")) {
            health = Lib.atoi(value);
            return true;
        } // F_INT),

        if (key.equals("sounds")) {
            sounds = Lib.atoi(value);
            return true;
        } // F_INT),

        if (key.equals("light")) {
            return true;
        } // F_IGNORE),

        if (key.equals("dmg")) {
            dmg = Lib.atoi(value);
            return true;
        } // F_INT),

        if (key.equals("mass")) {
            mass = Lib.atoi(value);
            return true;
        } // F_INT),

        if (key.equals("volume")) {
            volume = Lib.atof(value);
            return true;
        } // F_FLOAT),

        if (key.equals("attenuation")) {
            attenuation = Lib.atof(value);
            return true;
        } // F_FLOAT),

        if (key.equals("map")) {
            map = GameSpawn.ED_NewString(value);
            return true;
        } // F_LSTRING),

        if (key.equals("origin")) {
            s.origin = Lib.atov(value);
            return true;
        } // F_VECTOR),

        if (key.equals("angles")) {
            s.angles = Lib.atov(value);
            return true;
        } // F_VECTOR),

        if (key.equals("angle")) {
            s.angles = new float[] { 0, Lib.atof(value), 0 };
            return true;
        } // F_ANGLEHACK),

        if (key.equals("item")) {
            GameBase.gi.error("ent.set(\"item\") called.");
            return true;
        } // F_ITEM)

        return false;
    }

    /** Writes the entity to the file. */
    public void write(QuakeFile f) throws IOException {

        s.write(f);
        f.writeBoolean(inuse);
        f.writeInt(linkcount);
        f.writeInt(num_clusters);

        f.writeInt(9999);

        if (clusternums == null)
            f.writeInt(-1);
        else {
            f.writeInt(Defines.MAX_ENT_CLUSTERS);
            for (int n = 0; n < Defines.MAX_ENT_CLUSTERS; n++)
                f.writeInt(clusternums[n]);

        }
        f.writeInt(headnode);
        f.writeInt(areanum);
        f.writeInt(areanum2);
        f.writeInt(svflags);
        f.writeVector(mins);
        f.writeVector(maxs);
        f.writeVector(absmin);
        f.writeVector(absmax);
        f.writeVector(size);
        f.writeInt(solid);
        f.writeInt(clipmask);

        f.writeInt(movetype);
        f.writeInt(flags);

        f.writeString(model);
        f.writeFloat(freetime);
        f.writeString(message);
        f.writeString(classname);
        f.writeInt(spawnflags);
        f.writeFloat(timestamp);

        f.writeFloat(angle);

        f.writeString(target);
        f.writeString(targetname);
        f.writeString(killtarget);
        f.writeString(team);
        f.writeString(pathtarget);
        f.writeString(deathtarget);
        f.writeString(combattarget);

        f.writeEdictRef(target_ent);

        f.writeFloat(speed);
        f.writeFloat(accel);
        f.writeFloat(decel);

        f.writeVector(movedir);

        f.writeVector(pos1);
        f.writeVector(pos2);

        f.writeVector(velocity);
        f.writeVector(avelocity);

        f.writeInt(mass);
        f.writeFloat(air_finished);

        f.writeFloat(gravity);

        f.writeEdictRef(goalentity);
        f.writeEdictRef(movetarget);

        f.writeFloat(yaw_speed);
        f.writeFloat(ideal_yaw);

        f.writeFloat(nextthink);

        f.writeAdapter(prethink);
        f.writeAdapter(think);
        f.writeAdapter(blocked);

        f.writeAdapter(touch);
        f.writeAdapter(use);
        f.writeAdapter(pain);
        f.writeAdapter(die);

        f.writeFloat(touch_debounce_time);
        f.writeFloat(pain_debounce_time);
        f.writeFloat(damage_debounce_time);

        f.writeFloat(fly_sound_debounce_time);
        f.writeFloat(last_move_time);

        f.writeInt(health);
        f.writeInt(max_health);

        f.writeInt(gib_health);
        f.writeInt(deadflag);
        f.writeInt(show_hostile);

        f.writeFloat(powerarmor_time);

        f.writeString(map);

        f.writeInt(viewheight);
        f.writeInt(takedamage);
        f.writeInt(dmg);
        f.writeInt(radius_dmg);
        f.writeFloat(dmg_radius);

        f.writeInt(sounds);
        f.writeInt(count);

        f.writeEdictRef(chain);
        f.writeEdictRef(enemy);
        f.writeEdictRef(oldenemy);
        f.writeEdictRef(activator);
        f.writeEdictRef(groundentity);
        f.writeInt(groundentity_linkcount);
        f.writeEdictRef(teamchain);
        f.writeEdictRef(teammaster);

        f.writeEdictRef(mynoise);
        f.writeEdictRef(mynoise2);

        f.writeInt(noise_index);
        f.writeInt(noise_index2);

        f.writeFloat(volume);
        f.writeFloat(attenuation);
        f.writeFloat(wait);
        f.writeFloat(delay);
        f.writeFloat(random);

        f.writeFloat(teleport_time);

        f.writeInt(watertype);
        f.writeInt(waterlevel);
        f.writeVector(move_origin);
        f.writeVector(move_angles);

        f.writeInt(light_level);
        f.writeInt(style);

        f.writeItem(item);

        moveinfo.write(f);
        monsterinfo.write(f);
        if (client == null)
            f.writeInt(-1);
        else
            f.writeInt(client.index);

        f.writeEdictRef(owner);

        // rst's checker :-)
        f.writeInt(9876);
    }

    /** Reads the entity from the file. */
    public void read(QuakeFile f) throws IOException {
        s.read(f);
        inuse = f.readBoolean();
        linkcount = f.readInt();
        num_clusters = f.readInt();

        if (f.readInt() != 9999)
            new Throwable("wrong read pos!").printStackTrace();

        int len = f.readInt();

        if (len == -1)
            clusternums = null;
        else {
            clusternums = new int[Defines.MAX_ENT_CLUSTERS];
            for (int n = 0; n < Defines.MAX_ENT_CLUSTERS; n++)
                clusternums[n] = f.readInt();
        }

        headnode = f.readInt();
        areanum = f.readInt();
        areanum2 = f.readInt();
        svflags = f.readInt();
        mins = f.readVector();
        maxs = f.readVector();
        absmin = f.readVector();
        absmax = f.readVector();
        size = f.readVector();
        solid = f.readInt();
        clipmask = f.readInt();

        movetype = f.readInt();
        flags = f.readInt();

        model = f.readString();
        freetime = f.readFloat();
        message = f.readString();
        classname = f.readString();
        spawnflags = f.readInt();
        timestamp = f.readFloat();

        angle = f.readFloat();

        target = f.readString();
        targetname = f.readString();
        killtarget = f.readString();
        team = f.readString();
        pathtarget = f.readString();
        deathtarget = f.readString();
        combattarget = f.readString();

        target_ent = f.readEdictRef();

        speed = f.readFloat();
        accel = f.readFloat();
        decel = f.readFloat();

        movedir = f.readVector();

        pos1 = f.readVector();
        pos2 = f.readVector();

        velocity = f.readVector();
        avelocity = f.readVector();

        mass = f.readInt();
        air_finished = f.readFloat();

        gravity = f.readFloat();

        goalentity = f.readEdictRef();
        movetarget = f.readEdictRef();

        yaw_speed = f.readFloat();
        ideal_yaw = f.readFloat();

        nextthink = f.readFloat();

        prethink = (EntThinkAdapter) f.readAdapter();
        think = (EntThinkAdapter) f.readAdapter();
        blocked = (EntBlockedAdapter) f.readAdapter();

        touch = (EntTouchAdapter) f.readAdapter();
        use = (EntUseAdapter) f.readAdapter();
        pain = (EntPainAdapter) f.readAdapter();
        die = (EntDieAdapter) f.readAdapter();

        touch_debounce_time = f.readFloat();
        pain_debounce_time = f.readFloat();
        damage_debounce_time = f.readFloat();

        fly_sound_debounce_time = f.readFloat();
        last_move_time = f.readFloat();

        health = f.readInt();
        max_health = f.readInt();

        gib_health = f.readInt();
        deadflag = f.readInt();
        show_hostile = f.readInt();

        powerarmor_time = f.readFloat();

        map = f.readString();

        viewheight = f.readInt();
        takedamage = f.readInt();
        dmg = f.readInt();
        radius_dmg = f.readInt();
        dmg_radius = f.readFloat();

        sounds = f.readInt();
        count = f.readInt();

        chain = f.readEdictRef();
        enemy = f.readEdictRef();

        oldenemy = f.readEdictRef();
        activator = f.readEdictRef();
        groundentity = f.readEdictRef();

        groundentity_linkcount = f.readInt();
        teamchain = f.readEdictRef();
        teammaster = f.readEdictRef();

        mynoise = f.readEdictRef();
        mynoise2 = f.readEdictRef();

        noise_index = f.readInt();
        noise_index2 = f.readInt();

        volume = f.readFloat();
        attenuation = f.readFloat();
        wait = f.readFloat();
        delay = f.readFloat();
        random = f.readFloat();

        teleport_time = f.readFloat();

        watertype = f.readInt();
        waterlevel = f.readInt();
        move_origin = f.readVector();
        move_angles = f.readVector();

        light_level = f.readInt();
        style = f.readInt();

        item = f.readItem();

        moveinfo.read(f);
        monsterinfo.read(f);

        int ndx = f.readInt();
        if (ndx == -1)
            client = null;
        else
            client = GameBase.game.clients[ndx];

        owner = f.readEdictRef();

        // rst's checker :-)
        if (f.readInt() != 9876)
            System.err.println("ent load check failed for num " + index);
    }
}