package joshie.harvest.core;
import com.google.common.collect.Lists;
import joshie.harvest.api.HFApi;
import joshie.harvest.api.npc.task.HFTask;
import joshie.harvest.api.npc.task.TaskElement;
import joshie.harvest.api.quests.HFQuest;
import joshie.harvest.api.quests.Quest;
import joshie.harvest.core.commands.CommandManager;
import joshie.harvest.core.commands.HFCommand;
import joshie.harvest.core.commands.HFDebugCommand;
import joshie.harvest.core.network.Packet;
import joshie.harvest.core.util.annotations.HFApiImplementation;
import joshie.harvest.core.util.annotations.HFEvents;
import net.minecraft.command.ICommand;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.discovery.ASMDataTable;
import net.minecraftforge.fml.common.discovery.ASMDataTable.ASMData;
import net.minecraftforge.fml.common.discovery.asm.ModAnnotation;
import net.minecraftforge.fml.common.registry.IForgeRegistry;
import net.minecraftforge.fml.common.registry.IForgeRegistryEntry.Impl;
import net.minecraftforge.fml.relauncher.ReflectionHelper;
import net.minecraftforge.fml.relauncher.Side;
import javax.annotation.Nonnull;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import static joshie.harvest.core.lib.HFModInfo.MODID;
import static joshie.harvest.core.network.PacketHandler.registerPacket;
public class HFApiLoader {
public static void init(@Nonnull ASMDataTable table) {
Set<ASMData> datas = new HashSet<>(table.getAll(HFApiImplementation.class.getCanonicalName()));
for (ASMDataTable.ASMData data : datas) {
try {
Class clazz = Class.forName(data.getClassName());
Object instance = clazz.getField("INSTANCE").get(null);
Class[] interfaces = clazz.getInterfaces();
if (interfaces != null && interfaces.length > 0) {
for (Class inter: interfaces) {
for (Field f: HFApi.class.getFields()) {
if (f.getType().equals(inter)) {
f.set(null, instance);
}
}
}
}
} catch (Exception e) { e.printStackTrace(); }
}
}
@SuppressWarnings("unchecked")
private static <I extends Impl> void load(Class type, IForgeRegistry registry, ResourceLocation resource, Class<I> clazz) {
try {
if (type.isAssignableFrom(clazz)) {
registry.register(clazz.newInstance().setRegistryName(resource));
}
} catch (Exception e) { /**/}
}
public static void load(@Nonnull ASMDataTable asm, boolean isClient) {
registerQuests(asm);
registerEvents(asm, isClient);
registerPackets(asm);
registerCommands(asm);
registerTasks(asm);
if (HFCore.DEBUG_MODE && isClient) {
registerDebugCommand(asm);
}
}
private static void registerQuests(@Nonnull ASMDataTable asm) {
String annotationClassName = HFQuest.class.getCanonicalName();
Set<ASMData> asmDatas = new HashSet<>(asm.getAll(annotationClassName));
for (ASMDataTable.ASMData asmData : asmDatas) {
try {
Map<String, Object> data = asmData.getAnnotationInfo();
Class clazz = Class.forName(asmData.getClassName());
String extra = data.get("value") != null ? (String) data.get("value") : "";
String domain = data.get("mod") != null ? (String) data.get("mod") : "harvestfestival";
ResourceLocation resource = new ResourceLocation(domain, extra);
load(Quest.class, Quest.REGISTRY, resource, clazz);
Quest.REGISTRY.getValue(resource).onRegistered();
} catch (Exception e) { e.printStackTrace(); }
}
}
private static void registerTasks(@Nonnull ASMDataTable asm) {
String annotationClassName = HFTask.class.getCanonicalName();
Set<ASMData> asmDatas = new HashSet<>(asm.getAll(annotationClassName));
for (ASMDataTable.ASMData asmData : asmDatas) {
try {
Map<String, Object> data = asmData.getAnnotationInfo();
Class clazz = Class.forName(asmData.getClassName());
String value = data.get("value") != null ? (String) data.get("value") : "";
if (!value.equals("")) {
ResourceLocation resource = value.contains(":") ? new ResourceLocation(value): new ResourceLocation(MODID, value);
TaskElement.REGISTRY.put(resource, clazz);
}
} catch (Exception e) { e.printStackTrace(); }
}
}
private static void registerCommands(@Nonnull ASMDataTable asm) {
String annotationClassName = HFCommand.class.getCanonicalName();
Set<ASMData> asmDatas = new HashSet<>(asm.getAll(annotationClassName));
for (ASMDataTable.ASMData asmData : asmDatas) {
try {
Class clazz = Class.forName(asmData.getClassName());
if (ICommand.class.isAssignableFrom(clazz)) {
CommandManager.INSTANCE.addSubcommand((ICommand) clazz.newInstance());
}
} catch (Exception e) { e.printStackTrace(); }
}
}
@SuppressWarnings("unchecked")
private static void registerDebugCommand(@Nonnull ASMDataTable asm) {
//Debug commands
String annotationClassName = HFDebugCommand.class.getCanonicalName();
Set<ASMData> asmDatas = new HashSet<>(asm.getAll(annotationClassName));
for (ASMDataTable.ASMData asmData : asmDatas) {
try {
Map<String, Object> data = asmData.getAnnotationInfo();
Boolean sub = data.get("value") != null ? (Boolean) data.get("value") : false;
Class clazz = Class.forName(asmData.getClassName());
if (!sub) {
if (ICommand.class.isAssignableFrom(clazz)) {
CommandManager.INSTANCE.addSubcommand((ICommand) clazz.newInstance());
}
} else {
List list = (List) Class.forName("joshie.harvest.debug.CommandExportBuilder").getField("commands").get(null);
list.add(clazz.newInstance());
}
} catch (Exception e) { e.printStackTrace(); }
}
}
private static void registerEvents(@Nonnull ASMDataTable asmDataTable, boolean isClient) {
String annotationClassName = HFEvents.class.getCanonicalName();
Set<ASMData> asmDatas = new HashSet<>(asmDataTable.getAll(annotationClassName));
for (ASMDataTable.ASMData asmData : asmDatas) {
try {
Map<String, Object> data = asmData.getAnnotationInfo();
String side = data.get("value") != null ? ReflectionHelper.getPrivateValue(ModAnnotation.EnumHolder.class, (ModAnnotation.EnumHolder) data.get("value"), "value") : "";
if ((side.equals("CLIENT") && isClient) || side.equals("")) {
Class clazz = Class.forName(asmData.getClassName());
Method register = getMethod(clazz, "register");
if (register == null || ((Boolean)register.invoke(null))) {
Field INSTANCE = getField(clazz, "INSTANCE");
if (INSTANCE == null) MinecraftForge.EVENT_BUS.register(clazz.newInstance());
else MinecraftForge.EVENT_BUS.register(INSTANCE.get(null));
}
}
} catch (Exception e) {/**/}
}
}
@SuppressWarnings("unchecked")
private static void registerPackets(@Nonnull ASMDataTable asmDataTable) {
String annotationClassName = Packet.class.getCanonicalName();
Set<ASMData> asmDatas = new HashSet<>(asmDataTable.getAll(annotationClassName));
Map<String, Side> sidedPackets = new HashMap();
Map<String, String> unsidedPackets = new HashMap<>();
for (ASMDataTable.ASMData asmData : asmDatas) {
try {
Map<String, Object> data = asmData.getAnnotationInfo();
String s = data.get("value") != null ? ReflectionHelper.getPrivateValue(ModAnnotation.EnumHolder.class, (ModAnnotation.EnumHolder) data.get("value"), "value") : "BOTH";
Side side = s.equals("CLIENT") ? Side.CLIENT : s.equals("SERVER") ? Side.SERVER: null;
if (side == null) unsidedPackets.put(asmData.getClassName(), asmData.getClassName());
else sidedPackets.put(asmData.getClassName(), side);
} catch (Exception e) { e.printStackTrace(); }
}
//Sort the packet alphabetically so they get registered the same on server and client side
Comparator<String> alphabetical = (str1, str2) -> {
int res = String.CASE_INSENSITIVE_ORDER.compare(str1, str2);
if (res == 0) {
res = str1.compareTo(str2);
}
return res;
};
//Sort the sided and unsided packets
List<String> namesSided = Lists.newArrayList(sidedPackets.keySet());
Collections.sort(namesSided, alphabetical);
List<String> namesUnsided = Lists.newArrayList(unsidedPackets.keySet());
Collections.sort(namesUnsided, alphabetical);
//Register sided packets
for (String sided: namesSided) {
Side side = sidedPackets.get(sided);
try {
Class<?> asmClass = Class.forName(sided);
registerPacket(asmClass, side);
} catch (Exception e) {}
}
//Register unsided packets
for (String unsided: namesUnsided) {
try {
Class<?> asmClass = Class.forName(unsided);
registerPacket(asmClass);
} catch (Exception e) { e.printStackTrace(); }
}
}
@SuppressWarnings("unchecked")
private static Method getMethod(Class clazz, String method) {
try {
return clazz.getMethod(method);
} catch (NoSuchMethodException e) { return null; }
}
private static Field getField(Class clazz, String field) {
try {
return clazz.getField(field);
} catch (NoSuchFieldException e) { return null; }
}
}