class NPC{

	_npcID;

	
	/**
	 * @static
	 * @readonly
	 * @type {NPCsDict}
	 */
	static get DICT(){ return State.variables.npcs;}

	static _getHandler = function(target, prop){
		if(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(target), prop)?.get){
			return target[prop];
		}
		if(typeof target[prop] === 'function')
			return new Proxy(target[prop], {
				apply: (functionName, thisArg, argumentsList) =>  Reflect.apply(functionName, target, argumentsList)
			});
			
		return target.get(prop);
	}

	static handler = {
		get(target, prop) {
			if(prop.endsWith('_possessive')){
				prop = prop.replace('_possessive','');
				let value = NPC._getHandler(target,prop);
				if(value.endsWith('s'))
					return value+"'"; //https://dictionary.cambridge.org/grammar/british-grammar/possession-john-s-car-a-friend-of-mine
				return value + "'s";
			}
			return NPC._getHandler(target,prop);
			
		},
		set(target, prop, value, receiver){
			//console.warn(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(target), prop));
			if(Object.getOwnPropertyDescriptor(Object.getPrototypeOf(target), prop)?.set){
				target[prop] = value;
				return true;
			}
			target.set(prop,value);
			return true;
		}
	};

	get appearanceDemand(){return this.get('ad');}
	get age(){
        let dayOfBirth = this.get('dob');
        let day = dayOfBirth % 100;
        let month = Math.floor((dayOfBirth % 10000)/100);
        let year = Math.floor(dayOfBirth / 10000);
        return State.variables.time.ageOfDate(day,month,year);
	}
	set age(v){
		let birthday = State.variables.time.dateOfAge(v);
		this.birthday = birthday;
	}

	get birthday(){
		let dayOfBirth = this.get('dob');
		let day = dayOfBirth % 100;
		let month = Math.floor((dayOfBirth % 10000)/100);
		let year = Math.floor(dayOfBirth / 10000);
		return new Date(Date.UTC(year,month-1,day));
	}

	set birthday(v){
		let dob = v.getUTCFullYear()*10000 + (v.getUTCMonth()+1)*100 + v.getUTCDate();
		this.set('dob',dob);
	}

	get dick_girth(){
		let thdick = this.get('thdick');
		switch(thdick){
			case 'skinny':
				return 0;
			case 'slim':
				return 5;
			case 'well proportioned':
				return 10;
			case 'thicker than average':
				return 15;
			case 'thick':
				return 20;
			case 'massive':
				return 25;
			case 'monstrous':
				return 30;
			default :
				return 0;
		}
	}
	get dick_size(){ //Average: 3
		let ds_dick = this.get('dick',15);
		let ds_dick_girth = this.dick_girth;
		let ds_average = (ds_dick + ds_dick_girth) / 2;
		return Math.max(Math.ceil(ds_average / 5),0);
	}

	get fam(){
		return this.get('fam',0);
	}
	set fam(v){
		this.set('fam',v);
	}

	get fullname(){return this.get('firstname')+' '+this.get('lastname');}

	get location(){
		return NPC.DICT.location(this._npcID);
	}

	get title(){
		switch (this.get('gender')) {
			case 0:
				return 'Mr.'
			case 1:
				return 'Mrs.'
			default:
				return 'Mrx.'
		}
	}
	get titlename(){return `${this.title} ${this.get('lastname')}`;}
	get usedname(){
		const presetUsedName = this.get('usedname');
		if(presetUsedName)
			return presetUsedName;

		switch (this.get('nameScheme')) {
			case 'title_lastname':
				return this.titlename;
			case 'firstname':
				return this.get('firstname');
			default:
				return this.get('nickname') || this.get('firstname');
		}
	}

        

	get(key,def=undefined){
		return NPC.DICT.get(this._npcID,key,def);
	}


	set(key,v){
		return NPC.DICT.set(this._npcID,key,v);
	}


	endActivity(activity=undefined){
		console.warn('endActivity',this);
		return NPC.DICT.endActivity(this._npcID,activity);
	}

	constructor(npcId=undefined){
		if(npcId)
			this._npcID = npcId;
	}


    _init(nPCsDict){
        Object.keys(nPCsDict).forEach(function (pn) {
            this[pn] = clone(nPCsDict[pn]);
        }, this);

        return this;
    }

    clone = function () {
        return (new setup.NPC())._init(this);
    };

    toJSON = function () {
        var ownData = {};
        Object.keys(this).forEach(function (pn) {
            if(typeof this[pn] !== "function")
                ownData[pn] = clone(this[pn]);
        }, this);
        return JSON.reviveWrapper('(new setup.NPC())._init($ReviveData$)', ownData);
    };
}


setup.NPC = NPC;