package com.github.czyzby.lml.uedi.assets.impl;
import java.lang.reflect.Member;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.ObjectMap;
import com.github.czyzby.kiwi.util.common.Exceptions;
import com.github.czyzby.kiwi.util.common.Strings;
import com.github.czyzby.kiwi.util.gdx.collection.GdxMaps;
import com.github.czyzby.lml.uedi.assets.Loaded;
import com.github.czyzby.lml.util.Lml;
import com.github.czyzby.uedi.reflection.impl.FieldMember;
import com.github.czyzby.uedi.stereotype.Provider;
/** Abstract base for asset providers.
*
* @author MJ
*
* @param <Asset> type of provided assets. */
public abstract class AbstractAssetProvider<Asset> implements Provider<Asset> {
private final InjectingAssetManager assetManager;
private final ObjectMap<String, String> idsToPaths = GdxMaps.newObjectMap();
/** @param assetManager will be used to load the assets and schedule field injections. */
public AbstractAssetProvider(final InjectingAssetManager assetManager) {
this.assetManager = assetManager;
}
@Override
public Asset provide(final Object target, final Member member) {
if (member == null) {
throwUnknownPathException();
}
final String id = member.getName();
if (Strings.isEmpty(id)) {
throwUnknownPathException();
}
final Asset asset = getOrLoad(id);
if (member instanceof FieldMember) {
if (asset == null) {
assetManager.addInjection( // Delayed asset injection - field will be filled when assets are loaded:
new AssetInjection(idsToPaths.get(id), getType(), ((FieldMember) member).getField(), target));
} else if (target instanceof Loaded) {
((Loaded) target).onLoad(idsToPaths.get(id), getType(), asset);
}
} else if (asset == null) {
Gdx.app.log(Lml.LOGGER_TAG, "Warn: requested instance of unloaded asset: " + id);
}
return asset;
}
/** @return internal map used to store file paths mapped to their asset IDs. */
protected ObjectMap<String, String> getIdsToPaths() {
return idsToPaths;
}
/** @throws GdxRuntimeException on each call. */
protected void throwUnknownPathException() {
throw new GdxRuntimeException("Unable to determine asset path.");
}
/** @param id name of the asset file (without extension or folder). "_" are replaced with "/" to support nested
* folders. Cannot be empty or null.
* @return an instance of the asset (if its already loaded) or null if it was scheduled for loading.
* @throws GdxRuntimeException if ID is invalid. */
public Asset getOrLoad(final String id) {
final String path = determinePath(id);
if (assetManager.isLoaded(path)) {
return assetManager.get(path, getType());
}
assetManager.load(path, getType());
return null;
}
/** @param id name of the asset file (without extension or folder). "_" are replaced with "/" to support nested
* folders. Cannot be empty or null.
* @return a cached path assigned to the ID or a new determined path to an existing file.
* @throws GdxRuntimeException if unable to find existing file. */
protected String determinePath(final String id) {
String path = idsToPaths.get(id);
if (path != null) {
return path;
}
final String fileName = getFileName(getFolder(), id);
for (final String extension : getExtensions()) {
path = fileName + extension;
try {
final FileHandle file = Gdx.files.internal(path);
if (file.exists()) {
idsToPaths.put(id, path);
return path;
}
} catch (final Exception exception) {
Exceptions.ignore(exception); // Invalid FileHandle implementation, throws errors for missing files.
}
}
throw new GdxRuntimeException("Unable to find file in folder: '" + getFolder() + "' matching name: '" + id
+ "' with any of the supported extensions: " + Strings.join(", ", (Object[]) getExtensions()));
}
/** @param folder can contain the asset.
* @param id might contain '_', which should be converted to '/' to support nested paths.
* @return file name (supporting nested paths) proceeded with a slash and with a dot appended at the end. */
protected String getFileName(final String folder, final String id) {
final StringBuilder builder = new StringBuilder(folder.length() + 1 + id.length() + 1);
builder.append(folder);
builder.append('/');
builder.append(id);
Strings.replace(builder, '_', '/');
builder.append('.');
return builder.toString();
}
/** @return an array of supported file extensions. */
protected abstract String[] getExtensions();
/** @return the expected path of the asset. */
protected abstract String getFolder();
/** @return instance of {@link InjectingAssetManager} managing the assets. */
protected InjectingAssetManager getAssetManager() {
return assetManager;
}
}