/// <reference path="../Thing.ts" />
/// <reference path="../AI.ts" />
/// <reference path="Person/Attribute.ts" />
/// <reference path="Person/Skill.ts" />
/// <reference path="../Container/Corpse.ts" />
/**
 * A person is a thing that happens to be "alive" in some significant way, not necessarily human.
 * A robot is a person.
 * A dog is a person.
 *
 * Fuck your standards.
 */
class Person extends Thing implements AttributeBearer, SkillBearer {
    public AI = new AI({});
    public animated = true;

    public soreness = 0;
    public lastHealthUpdate = 0;

    public stamina = 10;
    public lastStaminaUpdate = 0;
    public staminaPerTurn = 1;
    public static MAX_STAMINA = 10;
    public static STRENGTH_SORENESS_MULTIPLIER = 4;

    public constructor (options : ThingOptions) {
        super(options);

        this.addGetAlterations((person : Person) => {
            // Let's not do this for NPCs. It can break patches.
            if (person.isPlayer()) {
                return {
                    Stats: this.attributeValue,
                    Skills: this.skillValue
                }
            }
        });

        this.addSetAlterations((person : Person, changes) => {
            // Let's not do this for NPCs. It can break patches.
            if (person.isPlayer()) {
                if (changes.Stats != undefined) {
                    for (let name in changes.Stats) {
                        let attr = Attribute.getAttribute(name);
                        if (attr != undefined) {
                            this.setStat(attr, changes.Stats[name]);
                        }
                    }
                }
                if (changes.Skills != undefined) {
                    for (let name in changes.Skills) {
                        let attr = Skill.getSkill(name);
                        if (attr != undefined) {
                            this.setSkill(attr, changes.Skills[name]);
                        }
                    }
                }
            }
        });
    }

    public changeHealth (n : number) {
        let bodyparts = <Array<Bodypart>> this.getParts(Bodypart);
        for (let i = 0; i < bodyparts.length; i++) {
            bodyparts[i].changeSoreness(n);
        }
        this.updateHealth();
    }

    /**
     * Returns health as a number from 0 to 10.
     */
    public getHealthOnScale () {
        return Math.round(
            ((this.getHealth() * 10) / (this.getStat(Attributes.Strength) * 2))
        );
    }

    /**
     * Returns current health.
     * Important = force update, otherwise use sufficiently accurate current value. to prevent looping too much.
     * @param {boolean} important
     * @returns {number}
     */
    public getHealth (important? : boolean) {
        if (important === true || this.lastHealthUpdate != WorldState.getCurrentTurn()) {
            this.updateHealth();
        }
        return this.soreness / (this.getStat(Attributes.Strength) * Person.STRENGTH_SORENESS_MULTIPLIER)
    }

    /**
     * Lazy updates allow us to count Health/Stamina for NPCs without overloading the Every Turn rulebook.
     */
    public updateHealth () {
        let health = 0;
        let bodyparts = this.getParts(Bodypart);
        for (let i = 0; i < bodyparts.length; i++) {
            health += bodyparts[i].getWeightedSoreness();
        }
        this.soreness = health;
        this.lastHealthUpdate = WorldState.getCurrentTurn();
    }

    public changeStamina (n : number) {
        this.updateStamina();
        this.stamina += n;
        if (this.stamina > Person.MAX_STAMINA) {
            this.stamina = Person.MAX_STAMINA;
        } else if (this.stamina < 0) {
            this.stamina = 0;
        }
    }

    /**
     * Returns stamina as a number from 0 to 10.
     */
    public getStaminaOnScale () {
        return Math.round(
            ((this.stamina * 10) / Person.MAX_STAMINA)
        );
    }

    public updateStamina () {
        var nTurns = WorldState.getCurrentTurn() - this.lastStaminaUpdate;
        this.stamina += this.staminaPerTurn * nTurns;
        if (this.stamina > Person.MAX_STAMINA) {
            this.stamina = Person.MAX_STAMINA;
        }
    }

    public isPlayer () {
        return (<any> this) == WorldState.player;
    }

    protected attributeValue : {[id : string] : number} = {};
    protected skillValue : {[id : string] : number} = {};

    public getStat (stat : Attribute) {
        if (this.attributeValue[stat.id] == undefined) {
            this.attributeValue[stat.id] = stat.defaultValue;
        }
        return this.attributeValue[stat.id];
    }

    public setStat (stat : Attribute, value : number) {
        this.attributeValue[stat.id] = value;
    }

    public getSkill (stat : Skill) {
        if (this.skillValue[stat.id] == undefined) {
            this.skillValue[stat.id] = stat.defaultValue;
        }
        return this.skillValue[stat.id];
    }

    public setSkill (stat : Skill, value : number) {
        this.skillValue[stat.id] = value;
    }

    public die () {
        let corpse = new Corpse({
            name : this.name + "'s corpse",
            unique : false,
            description : new Say("The lifeless body of ", this, ". May ", new SayHeSheIt(this), " rest in peace.")
        });

        corpse.put(...Thing.CarryRelation.getRight(this));
        corpse.put(...Thing.WearRelation.getRight(this));

        this.getRoom().place(corpse);
        OutOfPlay.removeFromPlay(this);
    }
}