
/// <reference path="../wardrobe/OutfitItem/OutfitItem.ts" />
/// <reference path="../wardrobe/OutfitItem/Clothes.ts" />
/// <reference path="../wardrobe/OutfitItem/Purse.ts" />
/// <reference path="../wardrobe/OutfitItem/Shoes.ts" />

const enum EWearableBMI{
	NOT_APPLICABLE = -2,
	ANY = -1,
	TOO_LOSE = 0,
	TOO_TIGHT = 1,
	ALMOST_TOO_LOSE = 2,
	ALMOST_TOO_TIGHT = 3,
	OKAY = 4
}

setup.wardrobeSettings = {
	bmiSizeMaxDifference: 2
}

setup.outfitsDefaults={
	clothes:{
		h:100,
		bmi:'$pc.bmi'
	}
}

setup.wardrobeWearableFunctions = {};

class Wardrobe{

	_ownedItems:{[itemId:string]:OutfitOwnerShipData} = {}

	
	/**
	 * Owned Item
	 * @param {string} itemId
	 */
	item(itemId:string):OutfitOwned{
		let item = OutfitItem.get(itemId) as OutfitItemClothes; // Trick Typescript
		let owneditem:OutfitOwned = Object.assign(item,{l:0,bmi:-2,h:-2},this._ownedItems[itemId]); // Item needs to be first, otherwise all its functions are broken
		return owneditem;
	}

	/**
	 * Owned Items
	 * @readonly
	 * @type {{[key:string]:OutfitItem}}
	 */
	get items():{[key:string]:OutfitOwned}{
		let result:{[key:string]:OutfitOwned} = {};
		for(const [itemId, itemData] of Object.entries(this._ownedItems)){
			//let item = OutfitItem.get(itemId);
			//Object.assign({l:0,bmi:-2,h:-2},item,itemData);
			result[itemId] = this.item(itemId);
		}
		return result;
	}

	itemsFiltered(filter:Filter){
		return setup.filterDict(this.items,filter);
	}

	//#region Worn Items
		_wornItems:WornOutfitDefinition = {
			bra: null,
			coat: null,
			clothes: null,
			panties: null,
			purse: null,
			shoes: null
		}

		get wornItems(){
			return {
				bra: this.bra,
				coat: this.coat,
				clothes: this.clothes,
				panties: this.panties,
				purse: this.purse,
				shoes: this.shoes
			}
		}

		get wornItemIds(){
			return this._wornItems;
		}

		get bra():OutfitItemBra{
			let braId = this.wornItemIds.bra;
			if(braId)
				return this.item(braId) as OutfitItemBra;
			return new OutfitItemBra();
		}

		get clothes():OutfitItemClothes|OutfitItemSwimwear{
			let clothesId = this.wornItemIds.clothes;
			if(clothesId)
				return this.item(clothesId) as OutfitItemClothes;
			return new OutfitItemClothes();
		}

		get coat():OutfitItemCoat{
			let coatId = this.wornItemIds.coat;
			if(coatId)
				return this.item(coatId) as OutfitItemCoat;
			return new OutfitItemCoat();
		}

		get panties():OutfitItemPanties{
			let pantiesId = this.wornItemIds.panties;
			if(pantiesId)
				return this.item(pantiesId) as OutfitItemPanties;
			return new OutfitItemPanties();
		}

		get purse():OutfitItemPurse{
			let purseId = this.wornItemIds.purse;
			if(purseId)
				return this.item(purseId) as OutfitItemPurse;
			return new OutfitItemPurse();
		}

		get shoes():OutfitItemShoes{
			let shoesId = this.wornItemIds.shoes;
			if(shoesId)
				return this.item(shoesId) as OutfitItemShoes;
			return new OutfitItemShoes();
		}
	//#endregion

	

	_lastWorn:{[key:string]:WornOutfitDefinition} = {
		last:{
			bra: null,
			coat: null,
			clothes: null,
			panties: null,
			purse: null,
			shoes: null
		}
	}

	_outfits:{[key:string]:WornOutfitDefinition} = {

	}

	outfitSports = '';
	outfitSchool = '';

	get outfits(){
		return this._outfits;
	}


	clothingDecreaseHealth(v){
		let clothesId = this._wornItems.clothes;
		if(clothesId in this._ownedItems){
			if(this._ownedItems[clothesId].h > 0){ //Health -1 is save
				this._ownedItems[clothesId].h = Math.max(0,this._ownedItems[clothesId].h - v);
			}
		}
	}

	/*get clothingIsNude(){
		return (!this._wornItems.clothes);
	}*/

	/*get clothingWorn(){
		return this._wornItems.clothes;
	}*/

	/*get clothingworntype(){
		if(!this._wornItems.clothes)
			return 'nude';
		let identificator = this.itemData(this._wornItems.clothes) as OutfitDefinitionClothes;
		return identificator.vendor+'_'+identificator.subtype;
	}*/
	/*
	get clothingwornnumber(){
		if(!this._wornItems.clothes)
			return 0;
		let identificator = this.itemData(this._wornItems.clothes) as OutfitDefinitionClothes;
		return identificator.index;
	}*/

	/*get coatworntype(){
		if(!this._wornItems.coat)
			return 'none';
		let identificator = this.itemData(this._wornItems.coat);
		return identificator.vendor;
	}*/
	/*get coatwornnumber(){
		if(!this._wornItems.coat)
			return 0;
		let identificator = this.itemData(this._wornItems.coat);
		return identificator.index;
	}*/

	/*get isWearingBra(){
		return (!(!this._wornItems.bra));
	}*/

	/*get isWearingPanties(){
		return (!(!this._wornItems.panties));
	}*/

	get isNaked(){
		return (!this.clothes.isValidItem && !this.bra.isValidItem && !this.panties.isValidItem);		
	}

	/*get PCloQuality(){
		if(this.clothingIsNude){
			let bmi = State.variables.pc.bmi;
			if(bmi >= 19 && bmi < 30)
				return 3;
			return 1;
		}
		return this.wornItemData('clothes','quality',0)
	}
	get PCloThinness(){return this.wornItemData('clothes','thinness',0)}
	get PCloTopCut(){
		const pc = State.variables.pc;
		let wiD = this.wornItemData('clothes','CloTopCut',0);
		if(wiD > 1){
			if(pc.tits == 2 || pc.tits == 3)
				wiD += 1;
			else if(pc.tits >= 4)
				wiD += 2;

			if(wiD > 2 && pc.tits >= 6)
				wiD += 1;
		}

		return wiD;
	}
	get PCloBra(){return this.wornItemData('clothes','includesBra',0)}
	get PCloOnePiece(){return this.wornItemData('clothes','isOnepiece',0)}
	get PCloPants(){return this.wornItemData('clothes','pantsShortness',0)}
	get PCloSkirt(){return this.wornItemData('clothes','skirtShortness',0)}
	get skirtLength(){return this.PCloSkirt;}
	get PCloPanties(){return this.wornItemData('clothes','includesPanties',0)}
	get PCloDress(){return this.wornItemData('clothes','isDress',0)}
	get PCloStyle(){return this.wornItemData('clothes','style',0)}
	get PCloStyle2(){return this.wornItemData('clothes','style2',0)}
	get PCloStyle3(){return this.wornItemData('clothes','style3',0)}
	get PCloInhibit(){return this.wornItemData('clothes','inhibition',0)}
	get PCloBimbo(){return this.wornItemData('clothes','bimbo',0)}
	get PCloCoverTop(){return this.wornItemData('clothes','coverTop',4)}
	get PCloCoverBack(){return this.wornItemData('clothes','coverBack',4)}
	get PCloCoverFront(){return this.wornItemData('clothes','coverFront',4)}
	//get PCloCoverFront(){return this.wornItemData('clothes','coverFront',0)}

	*/

	/*get PXCloThinness(){
		let PCloThinness = this.PCloThinness;
		switch(PCloThinness){
			case 1: return 150;
			case 2: return 200;
			case 3: return 250;
			case 4: return 300;
			case 5: return 350;
			case 6: return 400;
		}
		if(PCloThinness > 6)
			return 400;
		return 0;
	}*/

	/*get PXCloTopCut(){
		if(this.PCloBra == 1) return 400;
		if(this.PCloBra == 2) return 500;
		let PCloTopCut = this.PCloTopCut;
		switch(PCloTopCut){
			case 1: return 100;
			case 2: return 150;
			case 3: return 200;
			case 4: return 250;
			case 5: return 300;
			case 6: return 350;
			case 7: return 400;
		}
		if(PCloTopCut > 7)
			return 400;
		return 0;
	}*/

	/*get PXCloBottomShortness(){
		if(this.PCloPanties == 1) return 400;
		let PCloSkirt = this.PCloSkirt;
		if(PCloSkirt > 0){
			switch(PCloSkirt){
				case 1: return 100;
				case 2: return 150;
				case 3: return 200;
				case 4: return 250;
				case 5: return 300;
				case 6: return 350;
				case 7: return 400;
			}
			if(this.PCloThinness > 7)
				return 400;
		}
		let PCloPants = this.PCloPants;
		if(PCloPants > 0){
			switch(PCloPants){
				case 1: return 100;
				case 2: return 150;
				case 3: return 200;
				case 4: return 250;
				case 5: return 300;
				case 6: return 350;
				case 7: return 400;
			}
			if(PCloPants > 7)
				return 400;
		}
		return 0;
	}*/

	/*get PCloswimwear(){
		if(!this._wornItems.clothes)
			return 0;
		let identificator = this.itemData(this._wornItems.clothes) as OutfitDefinitionClothes;
		if(identificator.subtype == 'bikini' || identificator.subtype == 'swimsuit' || identificator.subtype == 'swim_one' || identificator.subtype == 'swim_two')
			return 1;
		return 0;
	}*/

	/*get isWearingSwimwear(){
		return !(!this.PCloswimwear);
	}*/

	/*get CoverTop(){return Math.max(this.PCloCoverTop + this.PBraCover - 4,0);}
	get CoverBack(){return Math.max(this.PCloCoverBack + this.PPanCoverBack - 4,0);}
	get CoverFront(){return Math.max(this.PCloCoverFront + this.PPanCoverFront - 4,0);}

	
	get isSchoolOutfit(){
		return this.wornItemData('clothes','subtype','') === 'school';
	}
	get isSportsOutfit(){
		return this.clothingworntype === 'danilovich_outfit';
	}*/


	// ----- Bra -----

	

	/*get braworntype(){
		if(!this._wornItems.bra)
			return 'none';
		let identificator = this.itemData(this._wornItems.bra);
		return identificator.vendor;
	}

	get brawornnumber(){
		if(!this._wornItems.bra)
			return 0;
		let identificator = this.itemData(this._wornItems.bra);
		return identificator.index;
	}

	get PBraMaterial(){return this.wornItemData('bra','material',0)}
	get PBraType(){return this.wornItemData('bra','subtype',0)}
	get PBraFun(){return this.wornItemData('bra','fun',0)}
	get PBraQuality(){return this.wornItemData('bra','quality',0)}
	get PBraThinness(){return this.wornItemData('bra','thinness',0)}
	get PBraCover(){return this.wornItemData('bra','cover',0)}


	get bra_appearanceBonus(){
		//Todo: Should be saved in const data
		switch(this.braworntype){
			case 'lusso': return 4;
			case 'fashionista': return 2;
		}
		return 0;
	}*/

	// ----- Panties -----

	/*

	get pantyworntype(){ //<<set $pantyworntype = $location_var[$here][1]>>
		if(!this._wornItems.panties)
			return 'none';
		let identificator = this.itemData(this._wornItems.panties);
		return identificator.vendor;
	}

	get pantywornnumber(){ //<<set $pantywornnumber = $location_var[$here][2]>>
		if(!this._wornItems.panties)
			return 0;
		let identificator = this.itemData(this._wornItems.panties);
		return identificator.index;
	}

	get PPanMaterial(){return this.wornItemData('panties','material',0)}
	get PPantyFun(){return this.wornItemData('panties','fun',0)}
	get PPanQuality(){return this.wornItemData('panties','quality',0)}
	get PPanThinness(){return this.wornItemData('panties','thinness',0)}
	get PPanCoverFront(){return this.wornItemData('panties','coverFront',0)}
	get PPanCoverBack(){return this.wornItemData('panties','coverBack',0)}
	get PPanType(){return this.wornItemData('panties','subtype',0)}

	get panties_appearanceBonus(){
		//Todo: Should be saved in const data
		switch(this.pantyworntype){
			case 'lusso': return 4;
			case 'fashionista': return 2;
		}
		return 0;
	}*/

	// ----- Shoes -----

	
	/**
	 * Is the character currently wearing shoes? They could be taken manually or autoatically, because the character is inside.
	 * @type {boolean}
	 */
	get isWearingShoes(){
		//return !(!this._wornItems.shoes);
		if(!this.shoes.isValidItem)
			return false;

		if(!State.variables.settings.shoes_keepOnPrivateIndoors && !State.variables.location.isPublic && !State.variables.location.isOutdoors)
			return false;

		return true;
	}

	itemImage(itemId:string):string{
		return OutfitItem.get(itemId).image;
	}

	/*get shoeworntype(){ //<<set $pantyworntype = $location_var[$here][1]>>
		if(!this._wornItems.shoes)
			return 'none';
		let identificator = this.itemData(this._wornItems.shoes);
		return identificator.vendor;
	}

	get shoewornnumber(){ //<<set $pantywornnumber = $location_var[$here][2]>>
		if(!this._wornItems.shoes)
			return 0;
		let identificator = this.itemData(this._wornItems.shoes);
		return identificator.index;
	}

	get PShoQuaility(){
		if(this.isWearingShoes)
			return this.wornItemData('shoes','quality',0)
		return 0;
	}
	get PShoCut(){
		if(this.isWearingShoes)
			return this.wornItemData('shoes','cut',0)
		return 0;
	}
	get PShoHeels(){
		if(this.isWearingShoes)
			return this.wornItemData('shoes','heels',0)
		return 0;
	}
	get PShoStyle(){
		if(this.isWearingShoes)
			return this.wornItemData('shoes','style',0)
		return 0;
	}*/


	/**
	 * Return heel height of worn shoes in mm.
	 * @readonly
	 * @type {(0 | 60 | 100 | 150 | 200 | 240 | 280 | 300)}
	 */
	/*get shoesHeight(){
		switch(this.PShoHeels){
			case 0: return 0;
			case 1: return 0;
			case 2: return 60;
			case 3: return 100;
			case 4: return 150;
			case 5: return 200;
			case 6: return 240;
			case 7: return 280;
			default: return 300;
		}

	}*/

	/**
	 * Appearance Bonus of worn shoes.
	 * @date 6/16/2023 - 7:05:32 AM
	 *
	 * @readonly
	 * @type {(25 | 50 | 100 | 150 | 200 | 300 | 400 | 0)}
	 */
	/*get PXShoHeels(){
		let PShoHeels = this.PShoHeels;
		if(PShoHeels > 0){
			switch(PShoHeels){
				case 1: return 25;
				case 2: return 50;
				case 3: return 100;
				case 4: return 150;
				case 5: return 200;
				case 6: return 300;
				case 7: return 400;
			}
			if(PShoHeels > 7)
				return 400;
		}
		return 0;
	}*/

	/*get shoes_appearanceBonus(){
		return this.PShoQuaility;
	}*/

	// ----- Coats -----
	/*get PCoatWarm(){return this.wornItemData('coat','warm',0)}
	get PCoatQuality(){return this.wornItemData('coat','quality',0)}

	get coat_appearanceBonus(){
		let location = State.variables.location;
		if(location.isOutdoors)
			return this.PCoatQuality - 2;
		return 0;
	}*/

	// ----- Purses -----
	/*get purseEquipped(){
		return (this._wornItems.purse) ? true : false;
	}
	*/
	get outfitQuality(){
		if(this.isWearingShoes)
			return Math.floor((this.clothes.quality * 2 + this.shoes.quality) / 3);
		return this.clothes.quality;
	}

	get outfitIndecency(){
		return setup.func('outfitIndecency');
	}

	
	/**
	 * Low-Level adds an item to the wardrobe.
	 * @param {string} itemId
	 * @param {{}} [metadata={}]
	 */
	add(itemId:string,metadata: {}={}){
		let constData = this.itemConstantData(itemId);

		let type_defaults = setup.outfitsDefaults[constData.type] || {};
		let vendor_defaults = setup.outfitsDefaults[constData.vendor] || {};

		metadata = Object.assign({},type_defaults,vendor_defaults,metadata);

		let iterateObject = clone(metadata);
		for (const [key, value] of Object.entries(iterateObject)) {
			if(typeof value === "string" && value.startsWith('$'))
				metadata[key] = State.getVar(value);
		}

		this._ownedItems[itemId] = metadata;
		console.log("ITEM ADDED",itemId,metadata);
	}

	
	/**
	 * Low-Level removes an item from the wardrobe.
	 * @param {string} itemId
	 */
	delete(itemId:string){
		delete this._ownedItems[itemId];
	}

	deleteOutfit(outfitId:string){
		return delete this.outfits[outfitId];
	}

	dispose(itemId:string){
		this.delete(itemId);
	}

	disposeWorn(type:string){
		let itemId = this._wornItems[type];
		if(itemId){
			this.strip(type);
			this.dispose(itemId);
		}
	}
	
	/**
	 * All defined items.
	 */
	get allItems(): {[key: string]: OutfitDefinition}{
		return setup.outfits;
	}

	allItemsFiltered(filter:Filter){
		return setup.filterDict(this.allItems,filter);
	}

	allItemIdsFiltered(filter:Filter,count=undefined,randomOrder=false){
		let itemIds = Object.keys(this.allItemsFiltered(filter));
		if(randomOrder)
			itemIds.shuffle();
		if(count)
			itemIds = itemIds.slice(0,count);
		return itemIds;
	}



	/**
	 * Returns all items (owned or not) that match filters.
	 * @param {*} filters
	 * @returns
	 */
	/*getItems(filters={}):{[key:string]:OutfitDefinition}{
		let items:{[key:string]:OutfitDefinition} = this.filterItems(this.allItems,filters);
		return items;
	}*/

	/*getItemIds(filters={},count=undefined,randomOrder=false){
		let itemIds = Object.keys(this.getItems(filters))
		if(randomOrder)
			itemIds.shuffle();
		if(count)
			itemIds = itemIds.slice(0,count);
		return itemIds;
	}*/

	/*filterItems(items:{[key:string]:OutfitDefinition},filters:Filter):{[key:string]:OutfitDefinition}{
		return setup.filterDict(items,filters);
	}*/

	// Gets a list of owned items
	/*getList(filters={}){
		let results = [];
		itemloop: for (const [item_id, item_data] of Object.entries(this._ownedItems)) {
			let itemData = this.get(item_id);
			let vendor = itemData.vendor;
			let type = itemData.type;
			let subtype = itemData.subtype;
			let index = itemData.index;

			filterloop: for (const [filter_key, filter_value] of Object.entries(filters)) {
				if(filter_value === null || filter_value === undefined)
					continue filterloop;

				if(filter_key == 'type'){
					if(type != filter_value)
						continue itemloop;
					continue filterloop;
				}else if(filter_key == 'vendor'){
					if(vendor != filter_value)
						continue itemloop;
					continue filterloop;
				}else if(filter_key == 'subtype'){
					if(subtype != filter_value)
						continue itemloop;
					continue filterloop;
				}else if(filter_key == 'index'){
					if(index != filter_value)
						continue itemloop;
					continue filterloop;
				}else if(filter_key == 'location'){
					if(Array.isArray(filter_value)){
						if(!filter_value.includes(itemData[filter_key]))
							continue itemloop;
						continue filterloop;
					}

					if(filter_value == -1)
						continue filterloop;
					if(filter_value == 0){
						if(!("location" in itemData) || itemData.location == 0)
							continue filterloop;
						continue itemloop;
					}

					if(!("location" in itemData))
						continue itemloop;
					if(itemData.location != filter_value)
						continue itemloop;
					continue filterloop;
				}else{
					if(Array.isArray(filter_value)){
						if(!filter_value.includes(itemData[filter_key] || 0))
							continue itemloop;
						continue filterloop;
					}else if(['string','number'].includes(typeof filter_value)){
						if(itemData[filter_key] != filter_value)
							continue itemloop;
						continue filterloop;
					}else{
						console.warn('Unreckognized filter format in $wardrobe.getlist():',filter_value);
					}
				}
			}
			let result = {
				id: item_id,
				type: type,
				vendor: vendor,
				subtype: subtype,
				index: index,
				image: this.itemImage(item_id),
				location: ("l" in item_data) ? item_data.l : 0,
				health: ("h" in item_data) ? item_data.h : -1,
			}
			results.push(result);
		}
		console.log("wardrobe.getList",filters,results);
		return results;
	}*/

	has(itemId){
		return (itemId in this._ownedItems);
	}

	inhibition(itemId:string){
		return OutfitItem.get(itemId).inhibition;
	}

	isWearing(itemId:string):boolean{
		for(const wornItemId of Object.values(this._wornItems)){
			if(itemId == wornItemId)
				return true;
		}
		return false;
	}

	isWeargingItemAtSlot(type){
		return(!(this._wornItems[type] == null));
	}

	
	/**
	 * @deprecated
	 * @param {string} itemId
	 * @returns {OutfitDefinition}
	 */
	itemConstantData(itemId:string):OutfitDefinition{
		if(!setup.outfits[itemId])
			return null;

		let raw = setup.outfits[itemId];

		

		return raw;
	}

	/**
	 * Return the constant data of an item
	 * @deprecated
	 * @param {string} itemId 
	 * @returns 
	 */
	itemData(itemId:string):OutfitDefinition{
		let constantData = this.itemConstantData(itemId);
		return constantData;
	}


	/*itemImage(itemId){
		let identificator = this.itemData(itemId);

		let template = setup.outfitItemImagePath[identificator.type];
		if(!template)
			console.error("ERROR: template not found",identificator);
		if(typeof template === "string")
			return template.formatUnicorn({vendor:identificator.vendor,id:identificator.index});
		if(!template[identificator.vendor]?.[identificator.subtype])
			console.error("ERROR: item-image-template is undefined",identificator,template[identificator.vendor]);
		return template[identificator.vendor][identificator.subtype].formatUnicorn({vendor:identificator.vendor,id:identificator.index});
	}*/
	/*
	itemImageAtSlot(slotId){
		return this.itemImage(this._wornItems[slotId]);
	}*/

	itemPrice(itemId,baseprice,pricemod=0){
		let itemData = this.itemData(itemId);
		let type = itemData.type;

		if(type != 'shoes' && type != 'clothes')
			return baseprice;

		let quality = itemData.quality;

		let price_raw = (baseprice * ((5 * quality) + 100) / 100) * 1000 / (1250 - pricemod) * 3 / 2;
		let price_rounded = Math.round(price_raw / 50) * 50;
		return price_rounded;
	}

	lastWornSet(key){
		this._lastWorn[key] = clone(this._wornItems);
	}

	//#region Outfit
		outfitAdd(key, outfitData=null){
			if(!this._outfits)
				this._outfits = {};
			if(outfitData)
				this._outfits[key] = clone(outfitData);
			else
				this._outfits[key] = clone(this._wornItems);
		}

		outfitIsWearable(key){
			if(!(key in this._outfits)){
				return false;
			}

			let slots = ['bra','clothes','panties','shoes'];
			for(let slot of slots){
				let wearable = this.wearable(this._outfits[key][slot]);
				if(!wearable.total){
					return false;
				}
			}
			return true;
		}
	//#endregion

	get isWearingSwimwear():boolean{
		return (this.clothes.type == 'swimwear');
	}

	get ownsSwimmingItem():boolean{
		//return (this.getList({subtype:'bikini'}).length + this.getList({subtype:'swimsuit'}).length > 0) ? true : false;
		return (Object.keys(this.itemsFiltered({type:'swimwear'})).length > 0);
	}

	/**
	 * Sends an item to the specified location.
	 * Side effect: adds the item to the inventory if it isn't already in it.
	 * @param {*} itemIdentification
	 * @param {number} location
	 */
	sendToLocation(itemId,location){
		let set = {l:location};
		this.setMetadata(itemId,set);
	}

	setMetadata(itemId,metadata){
		let temp = Object.assign({}, this._ownedItems[itemId], metadata);
		console.log("Item METADATA SET:",itemId,metadata,this._ownedItems[itemId],temp);
		this._ownedItems[itemId] = temp;
	}

	strip(type:string){
		let item:OutfitItem = this[type];

		if(!item.isValidItem)
			return;

		for(const slot of item.slots)
			this._wornItems[slot] = null;
	}

	stripAll(){
		this.strip('bra');
		this.strip('clothes');
		this.strip('panties');
		this.strip('shoes');
	}

	wearable(itemId:string){
		let result = {
			owned: true,
			health: true,
			inhibition: true,
			hips: true,
			various:[],
			total: true
		}
		var itemData:OutfitOwned;
		if(!this.has(itemId)){
			result.owned = false;
			result.health = false;
			result.hips = false;
			result.total = false;
			return result;
		}else{
			itemData = this.item(itemId);
			if(itemData.h == 0)
				result.health = false;
			if(itemData.bmi > 0){
				result.hips = !([EWearableBMI.TOO_LOSE,EWearableBMI.TOO_TIGHT].includes(this.wearableBMI(itemId)));
			}

		}

		if(!this.wearbleInhibition(itemId))
			result.inhibition = false;
		result.total = (result.owned && result.health && result.inhibition && result.hips);

		for(const wardrobeWearableFunction of Object.values(setup.wardrobeWearableFunctions)){
			let functionValue = wardrobeWearableFunction(itemData);
			if(functionValue){
				result.total = false;
				result.various.push(functionValue);
			}
		}

		return result;
	}

	wearableBMI(itemId:string):EWearableBMI{
		const itemData = this.item(itemId);
		if(itemData.bmi === undefined)
			return EWearableBMI.NOT_APPLICABLE;
		if(!itemData.bmi)
			return EWearableBMI.ANY;
		if(itemData.bmi < 0)
			return EWearableBMI.NOT_APPLICABLE;

		const pcbmi = State.variables.pc.bmi;
		if(itemData.bmi - pcbmi > setup.wardrobeSettings.bmiSizeMaxDifference)
			return EWearableBMI.TOO_TIGHT;
		if(pcbmi - itemData.bmi > setup.wardrobeSettings.bmiSizeMaxDifference)
			return EWearableBMI.TOO_LOSE;

		if(itemData.bmi - pcbmi > setup.wardrobeSettings.bmiSizeMaxDifference * 0.75)
			return EWearableBMI.ALMOST_TOO_TIGHT;
		if(pcbmi - itemData.bmi > setup.wardrobeSettings.bmiSizeMaxDifference * 0.75)
			return EWearableBMI.ALMOST_TOO_LOSE;

		return EWearableBMI.OKAY;
	}

	//Return 0 if inhibition blocks, 1 if exciting and 2 in every other case
	wearbleInhibition(itemId){
		const inhibition = this.inhibition(itemId);
		let playerInhib = State.variables.pc.pcs_inhib;
		if(inhibition <= playerInhib)
			return 2;
		if(inhibition - 10 <= playerInhib)
			return 1;
		return 0;

	}

	/**
	 * Wear the specified item. Send it to the default wardrobe section. Adds the item to the inventory, if it isn't in it.
	 * @param {*} itemIdentification
	 * @param {boolean} [replace=true] replace what's currently worn? If false only adds the item to the inventory.
	 */
	wear(itemId,replace=true):boolean{
		if(!this.has(itemId))
			this.add(itemId);

		//let itemType = this.itemData(itemId).type;
		if(!replace){
			let item = OutfitItem.get(itemId);
			for(const slot of item.slots){
				let itemAtSlot:OutfitItem = this[slot];
				if(itemAtSlot.isValidItem)
					return false;
			}
		}

		
		this.sendToLocation(itemId,0);
		console.log("WEARING NOW",itemId);
		this._wear(itemId);
		
		return true;
	}

	wearTemporary(itemId){
		let itemType = this.itemData(itemId).type;
		this.strip(itemType);
		console.log("WEARING NOW",itemId,itemType);
		this._wear(itemId);
	}


	/*wear_clothes_legacy(type,vendorAndSubtype,index){
		let parts = vendorAndSubtype.split('_',2);
		this.wear([type,parts[0],index,parts[1]]);
	}*/

	//Caution: Unlike wear, this will not change the location of the worn item.
	_wear(itemOrId:string|OutfitItem){
		let item:OutfitItem;
		if(typeof itemOrId == "string")
			item = OutfitItem.get(itemOrId);
		else
			item = itemOrId;

		for(const slot of item.slots){
			this.strip(slot);
			this._wornItems[slot] = item.id;
			this._lastWorn['last'][slot] = item.id;
		}
		/*let itemType = this.itemData(itemId).type;
		this._wornItems[itemType] = itemId;
		this._lastWorn['last'][itemType] = itemId;*/
		/*if(this.PCloBra == 1)
			this.strip('bra');
			*/
	}

	/*wear_last(type){
		this._wear(this._lastWorn['last'][type]);
	}*/

	hasLastWorn(key){
		return !(!this._lastWorn[key]);
	}

	wearLastWorn(key,consume=false){
		if(!this._lastWorn[key]){
			console.error("The last item set with key "+key+" has not been set!");
		}
		this._wornItems = clone(this._lastWorn[key]);
		if(consume)
			delete this._lastWorn[key];
	}

	wearOutfit(key){
		this._wornItems = clone(this._outfits[key]);
	}

	wornItemData(type,index,def:any=0){
		if(!this._wornItems[type])
			return def;

		let id = this._wornItems[type];
		let data = this.itemData(id);

		let returnValue = data[index]
		if(returnValue == undefined || returnValue == null)
			return def;
		return returnValue;
	}

	_shopitems = {} //{shopid:{expireday:day,pricemod:pricemod,list:[]}}
	shopItemIds(shopid:string,filters,count=30,expireday=undefined):string[]
	{
		if(shopid in this._shopitems){
			if(this._shopitems[shopid].expireday >= State.variables.time.daystart){
				let cachedList = this._shopitems[shopid].list;
				console.warn('Returning cached list',cachedList);
				return cachedList;
			}
		}

		let pricemod = rand(0,500);
		expireday ??= State.variables.time.daystart;

		let entrys:string[] = this.allItemIdsFiltered(filters,count,true);
		entrys = entrys.filter(itemId=>!this.has(itemId));

		this.shopItemListSet(shopid,expireday,pricemod,entrys);
		return entrys;
	}


	shopItemList(shopid,filters,baseprice,count=30,expireday=null):{[itemId:string]:OutfitItem}{

		let result:{[itemId:string]:OutfitItem} = {};
		for(const itemId of this.shopItemIds(shopid,filters,count,expireday)){
			let item = OutfitItem.get(itemId);
			Object.assign(item,{id: itemId,price: this.itemPrice(itemId,baseprice)});
			result[itemId] = item;
		}

		return result;
	}

	shopItemListSet(shopid,expireday,pricemod,items){
		this._shopitems[shopid] = {
			expireday: expireday,
			pricemod:pricemod,
			list: items
		}
	}

	filters = {
			shop_cost:true,
			shop_owned:false,
			shop_inhib_toohigh:false,
			shop_inhib_high:true,
			shop_inhib_low:true
		};

	

	/*execute_every_1_hour(){
	}

	execute_every_1_day(){
	}*/

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

		return this;
	}

	clone = function () {
		return (new Wardrobe())._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 Wardrobe())._init($ReviveData$)', ownData);
	};
}

window.Wardrobe = Wardrobe;

setup.outfits = {};