package fucoin;

import akka.actor.ActorSystem;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import fucoin.configurations.AbstractConfiguration;
import fucoin.setup.NetworkInterfaceReader;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

import javax.swing.*;
import java.io.File;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;


public class Main {
    
    private static ActorSystem cSystem;

    static {
        String hostname = NetworkInterfaceReader.readDefaultHostname();

        //Load configuration from current directory or from resources directory of jar
        File file = new File("application.conf");
        Config config = ConfigFactory.parseFile(file);
        if (!file.exists()) {
            System.out.println("Load default application.conf");
            config = ConfigFactory.parseResources("application.conf");
        } else {
            System.out.println("Load local application.conf");
        }

        //Init System Actor System
        cSystem = ActorSystem.create("Core", ConfigFactory.parseString("akka.remote.netty.tcp.hostname=" + hostname).withFallback(config));
    }

    public static void main(String[] args) throws InterruptedException, IllegalAccessException, InstantiationException {
        List<AbstractConfiguration> configurations = getAbstractConfigurations();

        AbstractConfiguration[] configs = new AbstractConfiguration[configurations.size()];
        configurations.toArray(configs);

        AbstractConfiguration selectedConfig = (AbstractConfiguration) JOptionPane.showInputDialog(null, "Select a configuration to run", "Configuration Selection", JOptionPane.QUESTION_MESSAGE, null, configs, configurations.get(0));
        if (selectedConfig != null) {
            selectedConfig.setSystem(cSystem);
            selectedConfig.run();
        } else {
            cSystem.terminate();
        }
    }

    private static List<AbstractConfiguration> getAbstractConfigurations() throws InstantiationException, IllegalAccessException {
        List<ClassLoader> classLoadersList = new LinkedList<>();
        classLoadersList.add(ClasspathHelper.contextClassLoader());
        classLoadersList.add(ClasspathHelper.staticClassLoader());

        Reflections reflections = new Reflections(new ConfigurationBuilder()
                .setScanners(new SubTypesScanner(false), new ResourcesScanner())
                .setUrls(ClasspathHelper.forClassLoader(classLoadersList.toArray(new ClassLoader[0])))
                .filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix("fucoin.configurations"))));

        Set<Class<? extends Object>> allClasses =
                reflections.getSubTypesOf(Object.class);

        List<AbstractConfiguration> configurations = new ArrayList<>();

        for (Class<? extends Object> oneClass: allClasses){
            if (!Modifier.isAbstract(oneClass.getModifiers())) {
                AbstractConfiguration cfg = (AbstractConfiguration)oneClass.newInstance();
                cfg.setSystem(cSystem);
                configurations.add(cfg);
            }
        }
        return configurations;
    }
}