All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.rpgframework.genericrpg.data.ComplexDataItemValue Maven / Gradle / Ivy

The newest version!
package de.rpgframework.genericrpg.data;

import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.UUID;

import org.prelle.simplepersist.Attribute;
import org.prelle.simplepersist.ElementList;

import de.rpgframework.genericrpg.ModifyableNumericalValue;
import de.rpgframework.genericrpg.NumericalValue;
import de.rpgframework.genericrpg.Pool;
import de.rpgframework.genericrpg.modification.Modification;
import de.rpgframework.genericrpg.modification.ModifiedObjectType;
import de.rpgframework.genericrpg.modification.RelevanceModification;

/**
 * @author prelle
 *
 */
public  class ComplexDataItemValue extends DataItemValue implements NumericalValue, DecisionContainer {

	private final static Logger logger = System.getLogger(ComplexDataItemValue.class.getPackageName());

	@ElementList(entry = "decision", type = Decision.class, inline = true)
	protected List decisions;

	/** Optional flags added by the user, like "Cheap knock off" */
	@ElementList(entry = "flag", type = String.class, inline = true)
	protected List flags;

	@Attribute
	protected UUID uuid;

	/** Flags added automatically by item */
	protected transient List autoFlags;

	/**
	 * This is calculated by RPG implementations to follow their
	 * rules on limits and how things interoperate
	 */
	private transient Pool pool;

	//-------------------------------------------------------------------
	public ComplexDataItemValue() {
		decisions = new ArrayList<>();
		flags     = new ArrayList<>();
		autoFlags = new ArrayList<>();
	}

	//-------------------------------------------------------------------
	public ComplexDataItemValue(T data) {
		this();
		if (data.getId()==null) throw new IllegalArgumentException("Resolved's getId() returns NULL");
		this.ref = data.getId();
		this.resolved = data;
	}

	//-------------------------------------------------------------------
	public ComplexDataItemValue(T data, int val) {
		this(data);
		if (data.getId()==null) throw new IllegalArgumentException("Resolved's getId() returns NULL");
		this.ref = data.getId();
		value = val;
	}

	//-------------------------------------------------------------------
	/**
	 * If the referenced ComplexDataItem had choices, these are the
	 * decisions.
	 */
	public List getDecisions() {
		return decisions;
	}
	//-------------------------------------------------------------------
	public Decision[] getDecisionArray() {
		Decision[] ret = new Decision[decisions.size()];
		return decisions.toArray(ret);
	}

	//-------------------------------------------------------------------
	public void addDecision(Decision value) {
		for (Decision dec : decisions) {
			if (dec.getChoiceUUID().equals(value.getChoiceUUID()))
				return;
		}
		decisions.add(value);
	}

	//-------------------------------------------------------------------
	public void removeDecision(Decision value) {
		removeDecision(value.getChoiceUUID());
	}

	//-------------------------------------------------------------------
	@Override
	public void removeDecision(UUID choiceUUID) {
		for (Decision dec : decisions) {
			if (dec.getChoiceUUID().equals(choiceUUID)) {
				decisions.remove(dec);
				return;
			}
		}
	}

	//-------------------------------------------------------------------
	@Override
	public Decision getDecision(UUID uuid) {
		for (Decision dec : decisions) {
			if (dec==null) continue;
			if (dec.getChoiceUUID().equals(uuid)) {
				return dec;
			}
		}
		return null;
	}

	//-------------------------------------------------------------------
	public Decision getDecisionByRef(String name) {
		for (Decision dec : decisions) {
			Choice choice = getResolved().getChoice(dec.getChoiceUUID());
			if (choice!=null && choice.getTypeReference()!=null && choice.getTypeReference().equals(name)) {
				return dec;
			}
		}
		return null;
	}

	//-------------------------------------------------------------------
	public Decision getDecisionByType(ModifiedObjectType type) {
		for (Decision dec : decisions) {
			Choice choice = getResolved().getChoice(dec.getChoiceUUID());
			if (choice!=null && choice.getChooseFrom()!=null && choice.getChooseFrom()==type) {
				return dec;
			}
		}
		return null;
	}

	//-------------------------------------------------------------------
	public void updateDecision(UUID uuid, String value) {
		for (Decision dec : decisions) {
			if (dec.getChoiceUUID().equals(uuid)) {
				dec.setValue(value);
				return;
			}
		}
		throw new NoSuchElementException(uuid+" not in "+decisions);
	}

	//-------------------------------------------------------------------
	public String getDecisionString(Locale loc) {
		return getDecisionString(loc, character);
	}

	//-------------------------------------------------------------------
	public String getPerDecisionString(Choice choice, Object obj, Decision dec, Locale loc) {
		DataItem item = null;
		DataItemValue itemV = null;
		if (obj instanceof DataItem) {
			item = (DataItem)obj;
		} else if (obj instanceof DataItemValue) {
			itemV = (DataItemValue)obj;
		} else if (obj instanceof String) {
			return (String)obj;
		} else if (obj!=null) {
			try {
				Method getName = obj.getClass().getMethod("getName", Locale.class);
				return String.valueOf( getName.invoke(obj, loc) );
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		// If there are sub-choices, add them
		if (choice.getSubOptions() != null && !choice.getSubOptions().isEmpty()) {
			return (resolved.getChoiceOptionStrings(choice, choice.getSubOption(dec.getValue()), loc)[0]);
		} else {
			if (item!=null) {
				return item.getName(loc);
			} else if (itemV!=null) {
				return itemV.getNameWithoutRating(loc);
			} else if (obj instanceof IAttribute) {
				return  ((IAttribute)obj).getName(loc);
			} else  {
				return dec.getValue();
			}
		}
	}

	//-------------------------------------------------------------------
	public String getDecisionString(Locale loc, CommonCharacter model) {
		List elem = new ArrayList<>();
		try {
		if (resolved!=null) {
			/*
			 * Initialize a list of all choices, recursive too and initialize
			 * it with the the high level choices
			 */
			Map allChoices = getChoiceMapRecursivly(model);

			for (Decision dec : decisions) {
				if (logger.isLoggable(Level.TRACE))
					logger.log(Level.TRACE, "getDecisionString: dec="+dec);
				Choice choice = allChoices.get(dec.getChoiceUUID());
				if (choice==null) {
					choice = resolved.getHardcodedChoice(dec.getChoiceUUID());
				}

//				if ("standard".equals(dec.getValue().toLowerCase()))
//					continue;
				if (choice==null) {
					if (!dec.getChoiceUUID().toString().equals("c2d17c87-1cfe-4355-9877-a20fe09c170d")) {
						logger.log(Level.WARNING, "No choice found for decision "+dec+" of "+this);
					}
					elem.add(dec.getValue());
					continue;
				}

				try {
					// Determine the selction result
					Object obj = null;
					if (choice.getChooseFrom().toString().equals("SUBSELECT")) {
						obj = choice.getSubOption(dec.getValue());
					} else if (choice.getChooseFrom().toString().equals("CARRIED")) {
						if (model!=null) {
							obj = model.getCarriedItem(dec.getValueAsUUID());
							if (obj==null) {
								logger.log(Level.ERROR, "Item {0} has a CARRIED decision {1} which is no known carried item in the character", getKey(), dec.getValue());
							}
						} else if (character!=null) {
							obj = character.getCarriedItem(dec.getValueAsUUID());
							if (obj==null) {
								logger.log(Level.ERROR, "Item {0} has a CARRIED decision {1} which is no known carried item in the character", getKey(), dec.getValue());
							}
						} else {
							logger.log(Level.ERROR, "Item {0} has a CARRIED decision {1}, but the character has not been given", getKey(), dec.getValue());
							throw new RuntimeException("Cannot resolve CARRIED:"+dec.getValue()+" without character");
						}
						// Select a numeric rating for an attribute
						// 
						// Select an enum value
						// 
					} else if (choice.getChooseFrom().toString().equals("CONTACT")) {
						continue; //model.getContact(dec.getValue());
					} else if (choice.getTypeReference()!=null) {
						if (choice.getTypeReference().equals("CHOICE")) {
							obj = choice.getChooseFrom().resolve(dec.getValue());
						} else {
//							try {
//								obj = choice.getChooseFrom().resolve(dec.getValue());
//							} catch (ReferenceException e) {
//								logger.log(Level.ERROR, "Error getting decision string",e);
//								logger.log(Level.ERROR, "Error in object {0} for choice {1} with decision {2}", this, choice,dec);
//								System.exit(1);
//							}
							if (obj==null)
								obj = dec.getValue();
						}
					} else {
						obj = choice.getChooseFrom().resolve(dec.getValue());
					}
					if (obj==null) {
						System.err.println("Could not resolve "+dec.getValue()+" for choice "+choice);
					}

					// Add a string representation
					String toAdd = getPerDecisionString(choice, obj, dec, loc);
					elem.add(toAdd );
				} catch (ReferenceException e) {
					logger.log(Level.ERROR, "Error resolving ''{0}'' from instance of {1}: "+e.getMessage(), dec.getValue(), getModifyable());
				}
			}
		} else {
			logger.log(Level.ERROR, " is null");
		}

		if (elem.isEmpty()) return "";
		return String.join(", ", elem);
		} finally {
			logger.log(Level.TRACE, "getDecisionString: {0}",elem);
		}
	}

	//-------------------------------------------------------------------
	private static void replaceOrAdd(StringBuffer haystack, String needle, String replace, List names) {
		int from = haystack.indexOf("("+needle+")");
		int to = from + needle.length()+2;
		if (from>=0) {
			haystack.replace(from, to, replace);
		} else
			names.add(replace);
	}

	//-------------------------------------------------------------------
	public String getNameWithoutRating(Locale loc) {
		StringBuffer ret = new StringBuffer(super.getNameWithoutRating(loc));
//		if (decisions!=null && !decisions.isEmpty() && resolved!=null) {
//			List names = new ArrayList<>();
//			for (Decision dec : decisions) {
//				Choice choice = resolved.getChoice(dec.getChoiceUUID());
//				if (choice==null && resolved.hasLevel) {
////					names.add(dec.getValue());
//					continue;
//				}
//				if (choice==null || choice.getChooseFrom()==null)
//					continue;
//				String needle = resolved.getChoiceName(choice, loc);
//				Object brr = null;
//				if ("CARRIED".equals(String.valueOf(choice.getChooseFrom()))) {
//					brr = character.getCarriedItem(UUID.fromString(dec.getValue()));
//				} else
//					brr = choice.getChooseFrom().resolve(dec.getValue());
//				if (brr instanceof DataItem) {
//					replaceOrAdd(ret,needle, ((DataItem)brr).getName(loc), names);
//				} else if (brr instanceof IItemAttribute) {
//					replaceOrAdd(ret,needle, ((IItemAttribute)brr).getName(loc), names);
//				} else if (brr instanceof IAttribute) {
//					replaceOrAdd(ret,needle, ((IAttribute)brr).getName(loc), names);
//				} else
//					names.add( resolved.getChoiceName(choice, loc) );
//			}
//			if (!names.isEmpty()) {
//				ret.append("(");
//				ret.append(String.join(", ", names));
//				ret.append(")");
//			}
//		}
		return ret.toString();
	}

	//-------------------------------------------------------------------
	public String getNameWithoutRating() {
		return getNameWithoutRating(Locale.getDefault());
	}

	//-------------------------------------------------------------------
	public String getNameWithRating(Locale loc) {
		String decString = decisions.isEmpty()?"":getDecisionString(loc);
		if (resolved==null)
			return ref+" "+value+" "+decString;
		if (decisions.isEmpty() && !resolved.getChoices().isEmpty()) {
			decString = "Undecided";
		}
		if (value==0)
			return resolved.getName(loc)+" "+decString;
		return resolved.getName(loc)+" "+value+" "+decString;
	}

	//-------------------------------------------------------------------
	/**
	 * Get the modifications this DataItemValue provides to others,
	 * after taking all decisions into account
	 */
	@SuppressWarnings("rawtypes")
	public void updateOutgoingModificiations(CommonCharacter model) {
		outgoingModifications.clear();

		for (Modification tmp : resolved.getOutgoingModifications()) {
			if (tmp.getReferenceType()==null) {
				if (!(tmp instanceof RelevanceModification))
					logger.log(Level.WARNING, "{0} has a modification without type", this.getKey());
				outgoingModifications.add(tmp);
			} else {
				if (this instanceof ModifyableNumericalValue) {
					int lvl = ((ModifyableNumericalValue)this).getModifiedValue();
					try {
						Modification mod = tmp.getReferenceType().instantiateModification(tmp, this, lvl, model);
						outgoingModifications.add(mod);
					} catch (Exception e) {
						logger.log(Level.ERROR, "Error instantiating mod "+tmp+" from "+tmp.getSource(),e);
					}
				} else {
					Modification mod = tmp.getReferenceType().instantiateModification(tmp, this, getDistributed(), model);
					if (mod==null) {
						logger.log(Level.DEBUG, "No instantiated {0} will be returned", tmp);
					} else {
						outgoingModifications.add(mod);
					}
				}
			}
		}

		for (Choice choice : resolved.getChoices()) {
			if (choice.getSubOptions().isEmpty())
				continue;
			Decision dec = getDecision(choice.getUUID());
			if (dec==null) continue;
			ChoiceOption sub = choice.getSubOption(dec.getValue());
			if (sub!=null) {
				for (Modification tmp : sub.getOutgoingModifications()) {
					if (tmp.getReferenceType()==null) {
						if (!(tmp instanceof RelevanceModification))
							logger.log(Level.WARNING, "{0} has a modification without type", this.getKey());
						outgoingModifications.add(tmp);
					} else {
						if (this instanceof ModifyableNumericalValue) {
							int lvl = ((ModifyableNumericalValue)this).getModifiedValue();
							try {
								Modification mod = tmp.getReferenceType().instantiateModification(tmp, this, lvl, model);
								outgoingModifications.add(mod);
							} catch (Exception e) {
								logger.log(Level.ERROR, "Error instantiating mod "+tmp+" from "+tmp.getSource(),e);
							}
						} else {
							Modification mod = tmp.getReferenceType().instantiateModification(tmp, this, getDistributed(), model);
							if (mod==null) {
								logger.log(Level.DEBUG, "No instantiated {0} will be returned", tmp);
							} else {
								outgoingModifications.add(mod);
							}
						}
					}
				}
			}
		}
	}

	//-------------------------------------------------------------------
	public void addFlag(Enum flag) {
		if (!flags.contains(flag.name()))
			flags.add(flag.name());
	}

	//-------------------------------------------------------------------
	public void addAutoFlag(Enum flag) {
		if (!autoFlags.contains(flag.name()))
			autoFlags.add(flag.name());
	}

	//-------------------------------------------------------------------
	public void removeFlag(Enum flag) {
		flags.remove(flag.name());
	}

	//-------------------------------------------------------------------
	public void removeAutoFlag(Enum flag) {
		autoFlags.remove(flag.name());
	}

	//-------------------------------------------------------------------
	public void addAutoFlag(String flag) {
		if (!autoFlags.contains(flag))
			autoFlags.add(flag);
	}

	//-------------------------------------------------------------------
	public void clearEmptyFlags() {
		for (String tmp : new ArrayList<>(flags)) {
			if (tmp==null || tmp.isBlank())
				flags.remove(tmp);
		}
	}

	//-------------------------------------------------------------------
	public  List getFlags(Class enumClass) {
		List ret = new ArrayList<>();
		outer:
		for (String name : flags) {
			inner:
			for (E tmp : enumClass.getEnumConstants()) {
				if (tmp.name().equals(name)) {
					ret.add(tmp);
					continue outer;
				}
			}
			logger.log(Level.WARNING, "Unknown flag {0} in {1} for item {2}", name, enumClass,getKey());
		}
		return Collections.unmodifiableList(ret) ;
	}

	//-------------------------------------------------------------------
	public  List getAutoFlags(Class enumClass) {
		List ret = new ArrayList<>();
		outer:
		for (String name : autoFlags) {
			for (E tmp : enumClass.getEnumConstants()) {
				if (tmp.name().equals(name)) {
					ret.add(tmp);
					continue outer;
				}
			}
			logger.log(Level.WARNING, "Unknown flag {0} in class {1} for item {2}", name, enumClass,getKey());
		}
		return Collections.unmodifiableList(ret) ;
	}

	//-------------------------------------------------------------------
	public boolean hasFlag(Enum value) {
		return flags.contains(value.name()) || autoFlags.contains(value.name());
	}

	//-------------------------------------------------------------------
	public boolean hasFlag(String value) {
		return flags.contains(value) || autoFlags.contains(value);
	}

	//-------------------------------------------------------------------
	public void clearAutoFlags() {
		autoFlags.clear();
	}

	//-------------------------------------------------------------------
	public boolean hasAutoFlag(Enum value) {
		return autoFlags.contains(value.name());
	}

	//-------------------------------------------------------------------
	public void setFlag(Enum flag, boolean set) {
		if (set) {
			if (!flags.contains(flag.name()))
				flags.add(flag.name());
		} else {
			flags.remove(flag.name());
		}
	}

	//-------------------------------------------------------------------
	public void setAutoFlag(Enum flag, boolean set) {
		if (set) {
			if (!autoFlags.contains(flag.name()))
				autoFlags.add(flag.name());
		} else {
			autoFlags.remove(flag.name());
		}
	}

	//-------------------------------------------------------------------
	public void setAutoFlag(String flag, boolean set) {
		if (set) {
			if (!autoFlags.contains(flag))
				autoFlags.add(flag);
		} else {
			autoFlags.remove(flag);
		}
	}

	//-------------------------------------------------------------------
	public Map getChoiceMapRecursivly(CommonCharacter model) {
		Map allChoices = new HashMap<>();
		resolved.getChoices().forEach(c -> allChoices.put(c.getUUID(), c));

		for (Decision dec : decisions) {
			if (logger.isLoggable(Level.TRACE))
				logger.log(Level.TRACE, "getDecisionString: dec="+dec);
			Choice choice = allChoices.get(dec.getChoiceUUID());
			if ("standard".equals(dec.getValue().toLowerCase()))
				continue;
			if (choice==null && resolved.hasLevel && dec.getChoiceUUID().toString().equals("c2d17c87-1cfe-4355-9877-a20fe09c170d")) {
				continue;
			}
			if (choice==null) {
				logger.log(Level.WARNING, "No choice found for decision "+dec+" of "+this);
				continue;
			}

			try {
				Object obj = null;
				if ("CHOICE".equals(choice.getTypeReference())) {
					if ("CARRIED".equals(String.valueOf(choice.getChooseFrom())) && model!=null) {
						obj = model.getCarriedItem(UUID.fromString(dec.getValue()));
						if (obj==null) {
							logger.log(Level.ERROR, "No such CarriedItem {0} in character", dec.getValue());
						}
					} else {
						obj = choice.getChooseFrom().resolve(dec.getValue());
					}
				} else {
					if ("CARRIED".equals(String.valueOf(choice.getChooseFrom()))) {
						if (model==null) {
							logger.log(Level.ERROR, "Cannot resolve CARRIED:{0}, since model of {1} is NULL", dec.getValue(),this);
						}
						obj = model.getCarriedItem(UUID.fromString(dec.getValue()));
						if (obj==null) {
							logger.log(Level.ERROR, "No such CarriedItem {0} in character", dec.getValue());
						}
					} else {
						// Select a numeric rating for an attribute
						// 
						// Select an enum value
						// 
						if (choice.getTypeReference()!=null) {
							// Expect choice.getChooseFrom to be some kind of attribute or item reference
							// Select a numeric rating for an attribute
							if ("CARRIED".equals(String.valueOf(choice.getChooseFrom()))) {
								if (model==null) {
									logger.log(Level.ERROR, "Cannot resolve CARRIED:{0}, since model of {1} is NULL", dec.getValue(),this);
								}
								obj = model.getCarriedItem(UUID.fromString(dec.getValue()));
								if (obj==null) {
									logger.log(Level.ERROR, "No such CarriedItem {0} in character", dec.getValue());
								}
							} else {
								obj = choice.getChooseFrom().resolve(choice.getTypeReference());
								if (obj==null) {
									logger.log(Level.DEBUG, "Failed to resolve {0} from {1} ", dec.getValue(), choice.getChooseFrom());
									obj = choice.getChooseFrom().resolve(dec.getValue());
								}
							}
						} else {
							// Select an enum value
							obj = choice.getChooseFrom().resolve(dec.getValue());
						}
					}
				}
				DataItem item = null;
				if (obj instanceof DataItem) {
					item = (DataItem)obj;
//					item = choice.getChooseFrom().resolveAsDataItem(dec.getValue());
						logger.log(Level.DEBUG, "resolved {0} to {1}", dec.getValue(), item);
						if (item != null && item instanceof ComplexDataItem) {
							// Add choices from resolved
							logger.log(Level.DEBUG, "Choices from {0} are {1}", dec.getValue(),
									((ComplexDataItem) item).getChoices());
							((ComplexDataItem) item).getChoices().forEach(c -> {
								logger.log(Level.DEBUG, "Add choice " + c + " for resolution");
								allChoices.put(c.getUUID(), c);
							});
						}
				} else if (obj instanceof DataItemValue) {
					DataItemValue itemV = (DataItemValue)obj;
					item = itemV.getResolved();
					logger.log(Level.DEBUG, "resolved {0} to {1}", dec.getValue(), item);
					if (itemV instanceof ComplexDataItemValue) {
						allChoices.put(dec.getChoiceUUID(), choice);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
				logger.log(Level.ERROR, "Failed resolving ''{0}'': {1}", dec.getValue(), e.toString());
			}
		}
		return allChoices;
	}

	//-------------------------------------------------------------------
	public UUID getUuid() {
		return uuid;
	}

	//-------------------------------------------------------------------
	public void setUuid(UUID uuid) {
		this.uuid = uuid;
	}

	//-------------------------------------------------------------------
	/**
	 * Remove all incoming and outgoing modifications
	 */
	 public void reset() {
		clearIncomingModifications();
	}

		//-------------------------------------------------------------------
		/**
		 * @see de.rpgframework.genericrpg.ModifyableNumericalValue#getPool()
		 */
		public Pool getPool() {
			return pool;
		}

		//-------------------------------------------------------------------
		public void setPool(Pool pool) {
			this.pool = pool;
		}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy