/
codebase/src/net/sourceforge/pain/admin/console/command/
codebase/src/net/sourceforge/pain/data/role/
codebase/src/net/sourceforge/pain/network/console/telnet/
codebase/src/net/sourceforge/pain/network/guitool/
codebase/src/net/sourceforge/pain/plugin/
codebase/src/net/sourceforge/pain/util/
db/src/net/sourceforge/pain/util/
gui/
gui/lib/
gui/src/net/sourceforge/pain/tools/guitool/dbbrowse/
gui/src/net/sourceforge/pain/tools/guitool/dialog/
gui/src/net/sourceforge/pain/tools/guitool/menu/
gui/src/net/sourceforge/pain/tools/guitool/resources/
gui/src/net/sourceforge/pain/tools/guitool/resources/images/
gui/src/net/sourceforge/pain/tools/guitool/resources/images/explorer/
mudlibs/tinylib/
mudlibs/tinylib/area/
mudlibs/tinylib/etc/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/data/affect/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/data/prototype/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/data/trigger/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/affect/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/deploy/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/guitool/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/event/guitool/event/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/fn/util/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/trigger/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/logic/trigger/impl/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/command/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/reset/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/shutdown/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/plugin/social/
mudlibs/tinylib/src/net/sourceforge/pain/tinylib/util/
tests/
tests/src/
tests/src/net/sourceforge/pain/db/data/
package net.sourceforge.pain;


import net.sourceforge.pain.data.*;
import net.sourceforge.pain.data.role.*;
import net.sourceforge.pain.db.*;
import net.sourceforge.pain.logic.*;
import net.sourceforge.pain.network.console.*;
import net.sourceforge.pain.network.guitool.*;
import net.sourceforge.pain.plugin.*;
import net.sourceforge.pain.util.*;

import java.io.*;
import java.util.*;


/**
 * Class that reads codebase startup settings, starts codebase and mudlib
 * Codebase properties sample file:
<pre>
 # NETWORK PARAMS

 # Path to database file
 pain.database_file=pain.db

 # port for administrators service
 pain.admin_server_port=7775

 # Mud port
 pain.mud_server_port=7777

 # Port for GUITool service
 pain.guitool_server_port=5555



 # PLUGINS LAYER

 # All plugins should be placed under the same package.
 # This allows PAiN codebase to separate classloaders
 # for static,logic and plugins code
 # (every plugin is loaded in separate classloader)
 pain.plugins_package_root=net.sourceforge.pain.tinylib.plugin

 # Path to dir with compiled plugins classes
 pain.plugins_classes_dir=../tinylib/classes



 # LOGIC LAYER

 # Set of comma-separated package names.
 # All classes under these packages are treated as 'logic'
 # This allows PAiN codebase to separate classloaders
 # for static,logic and plugins code
 # Logic classes are loaded by single 'logic' classloader
 pain.logic_packages=net.sourceforge.pain.tinylib.logic

 # Path to dir with compiled logic classes
 pain.logic_classes_dir=../tinylib/classes

 # All Affect impls should be located under the same package
 # This allows to save in DB only short  affects class name suffix
 # Affects package should be child of one of the logic packages
 pain.affects_logic_package=net.sourceforge.pain.tinylib.logic.affect

 # All Trigger impls should be located under the same package
 # This allows to save in DB only short trigger class name suffix
 # Trigger package should be child of one of the logic packages
 pain.triggers_logic_package=net.sourceforge.pain.tinylib.logic.trigger

 # Objects instances are constructed from Prototypes
 # by static factory methods (see ObjectFactory javadoc).
 # Object factory name for specified <ROLE >are constructed
 # from 'object_factory_package'+<ROLE>+"Factory"
 # Object factories packahe should be child of one of the logic packages
 pain.object_factory_package=net.sourceforge.pain.tinylib.logic.factory


 # Codebase emits events (console events, system events) to mudlib using class
 # name suffix. Corresponding mudlib-specific event impl class name
 # constructed by concatinating 'logic_events_package' with such suffix
 pain.logic_events_package=net.sourceforge.pain.tinylib.logic.event




 #MUDLIB


 #(optional)
 # Mudlib could override(extend) user console class
 # (add more field/fuctionality)
 # default value: net.sourceforge.pain.network.console.BasicConsole
 pain.user_console_factory=net.sourceforge.pain.tinylib.TinylibConsoleFactory


 # Full class name of mudlib initializer impl
 # After codebase environment is ready Mudlib initializer is called
 # (see net.sourceforge.pain.MudlibInitializer javadoc)
 pain.mudlib_initializer=net.sourceforge.pain.tinylib.TinyLibInitializer

 tinylib.etc_dir=./etc
 </pre>
*/

public class CodebaseLauncher {

    private static PropertiesReader props;
    private static CodebaseSettings settings;
    private static MudlibInitializer initer;

    /**
     * Usage: CodebaseLauncher <codebase_properties_file>
     */
    public static void main(String[] args) throws Exception {
        if (args.length == 0) {
            System.err.println("Usage: CodebaseLauncher <codebase_properties_file>");
        }
        props = new PropertiesReader(args[0]);
        settings = new CodebaseSettings(props);
        init();
    }

    private static void init() throws Exception {
        try {
            initer = (MudlibInitializer) settings.mudlibInitializer.newInstance();
            initer.readSetting(props);//reading settings before database start
            initDb();
            runCoreTasksAdminConsoleService();
            initMudlibAndUserServer();
            runGUIToolService();
            runUsersConsoleService();
            //free mem
            props = null;
            settings = null;
            runPulse();
        } catch (Exception e) {
            try {
                Codebase.getPulse().stopPulse();
            } catch (Exception e1) {
                e.printStackTrace();
            }
            try {
                final UserConsoleManager userConsoleManager = Codebase.getUserConsoleManager();
                if (userConsoleManager != null && userConsoleManager.isStarted()) {
                    userConsoleManager.stop();
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            try {
                final AdminConsoleManager adminConsoleManager = Codebase.getAdminConsoleManager();
                if (adminConsoleManager != null && adminConsoleManager.isStarted()) {
                    adminConsoleManager.stop();
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
            PainDB db = Codebase.getDB();
            if (db != null) {
                try {
                    db.close();
                } catch (RuntimeException e2) {
                    e2.printStackTrace();
                }
            }
            e.printStackTrace();
        }
    }


    private static void initDb() throws Exception {
        Log.info("initializing database :" + settings.dbFile.getAbsolutePath());
        assert Codebase.getDB() == null;
        PainDB db = new PainDB(settings.dbFile.getAbsolutePath());
        if (db.isDatabaseEmpty()) {
            prepareDefaultCodebaseDB(db);
        }
        Codebase.setDB(db);
        Log.info("database INITED");
    }


    private static void runCoreTasksAdminConsoleService() throws Exception {
        Log.info("initializing plugins and logic loaders..");

        PluginManager plm = new PluginManager();
        plm.init(settings.pluginsPackagePrefix, settings.pluginsClassesDir);
        Codebase.setPluginManager(plm);

        LogicLoader llm = new LogicLoader(settings.logicClassesDir);
        llm.setEventsPackagePrefix(settings.logicEventsPackage + ".");
        for (int i = 0; i < settings.logicPackages.length; i++) {
            llm.addLogicPackage(settings.logicPackages[i]);
        }
        llm.addLogicPackage(settings.affectsLogicPackage);

        Codebase.setLogicLoader(llm);
        Log.info("initializing plugins and logic loaders DONE");

        Log.info("Creating affect processor");
        Codebase.getPulse().addListener(new AffectProcessor(Codebase.getCodebaseData().getAffectsQueue()));
        Log.info("Creating affect processor OK");


        Log.info("starting admin console service (port:" + settings.adminServerPort + ")..");
        AdminConsoleManager acm = new AdminConsoleManager(settings.adminServerPort);
        Codebase.setAdminConsoleManager(acm);
        acm.start();
        Log.info("admin console service STARTED");
    }

    private static void initMudlibAndUserServer() throws Exception {
        Codebase.OBJECT_FACTORY_PACKAGE_PREFIX = settings.objectFactoryPackage + ".";
        Codebase.AFFECTS_LOGIC_PACKAGE_PREFIX = settings.affectsLogicPackage + ".";
        Codebase.TRIGGERS_LOGIC_PACKAGE_PREFIX = settings.triggersLogicPackage + ".";

        Codebase.setUserConsoleManager(new UserConsoleManager(settings.userServerPort, (ConsoleFactory) settings.userConsoleFactoryClass.newInstance()));
        Codebase.getPulse().addListener(new Flusher());

        Log.info("initializing mudlib:" + settings.mudlibInitializer.getName());
        initer.init();
        Log.info("initializing mudlib: DONE");
    }


    private static void runGUIToolService() throws Exception {
        Log.info("Starting GUITool service (port:" + settings.guiToolServicePort + ")...");
        Codebase.setGuiToolSessionManager(new GuiToolSessionManager(settings.guiToolServicePort));
        Log.info("GUITool service STARTED");
    }

    private static void runUsersConsoleService() throws Exception {
        Log.info("Starting user console service (port:" + settings.userServerPort + ")...");
        Codebase.getUserConsoleManager().start();
        Log.info("User console service STARTED");
    }

    private static void runPulse() {
        Log.info("starting codebase pulse...");
        new Thread(Codebase.getPulse()).start();
        Log.info("Pulse STARTED");
    }

    private static class CodebaseSettings {
        final File dbFile;
        final int adminServerPort;
        final int userServerPort;
        final int guiToolServicePort;
        final Class mudlibInitializer;
        final String pluginsPackagePrefix;
        final String pluginsClassesDir;
        final String[] logicPackages;
        final String logicClassesDir;
        final String logicEventsPackage;
        final public String affectsLogicPackage;
        final String objectFactoryPackage;
        final Class userConsoleFactoryClass;
        final String triggersLogicPackage;

        CodebaseSettings(PropertiesReader pr) throws Exception {
            dbFile = new File(pr.get("pain.database_file"));
            adminServerPort = pr.getInt("pain.admin_server_port", 0);
            userServerPort = pr.getInt("pain.mud_server_port", 0);
            guiToolServicePort = pr.getInt("pain.guitool_server_port", 0);
            if (adminServerPort == userServerPort || userServerPort == guiToolServicePort || guiToolServicePort == adminServerPort) {
                throw new RuntimeException("Illegal ports");
            }
            mudlibInitializer = Class.forName(pr.get("pain.mudlib_initializer"));
            if (!MudlibInitializer.class.isAssignableFrom(mudlibInitializer)) {
                throw new RuntimeException("class MudlibInitializer is not assignable from  :" + mudlibInitializer.getName());
            }
            mudlibInitializer.getConstructor(null);//check default constr;

            final String tmp = props.get("pain.plugins_package_root");
            pluginsPackagePrefix = tmp.endsWith(".") ? tmp : tmp + ".";
            pluginsClassesDir = props.get("pain.plugins_classes_dir");

            affectsLogicPackage = props.get("pain.affects_logic_package");
            triggersLogicPackage = props.get("pain.triggers_logic_package");
            objectFactoryPackage = props.get("pain.object_factory_package");
            logicEventsPackage = props.get("pain.logic_events_package");

            String logicPackagesStr = props.get("pain.logic_packages");
            StringTokenizer st = new StringTokenizer(logicPackagesStr, ",");
            logicPackages = new String[st.countTokens()];
            boolean affectsOK = false;
            boolean triggersOK = false;
            boolean factoryOK = false;
            boolean eventsOK = false;
            for (int i = 0; st.hasMoreTokens(); i++) {
                final String lp = st.nextToken();
                if (lp.startsWith(pluginsPackagePrefix)) {
                    throw new RuntimeException("Logic vs Plugins package collission:" + lp);
                }
                if (affectsLogicPackage.startsWith(lp)) {
                    affectsOK = true;
                }
                if (triggersLogicPackage.startsWith(lp)) {
                    triggersOK = true;
                }
                if (objectFactoryPackage.startsWith(lp)) {
                    factoryOK = true;
                }
                if (logicEventsPackage.startsWith(lp)) {
                    eventsOK = true;
                }
                logicPackages[i] = lp;
            }
            if (!affectsOK) {
                throw new RuntimeException("Affects impls package is not under one of the logic packages:" + affectsLogicPackage);
            }
            if (!triggersOK) {
                throw new RuntimeException("Triggers impls package is not under one of the logic packages:" + triggersLogicPackage);
            }
            if (!factoryOK) {
                throw new RuntimeException("Objects factories impls package is not under one of the logic packages:" + objectFactoryPackage);
            }

            if (!eventsOK) {
                throw new RuntimeException("Codebase events impls package is not under one of the logic packages:" + logicEventsPackage);
            }

            logicClassesDir = props.get("pain.logic_classes_dir");

            String userConsoleFactoryClassStr = props.get("pain.user_console_factory", false);
            if (userConsoleFactoryClassStr == null) {
                userConsoleFactoryClass = DefaultConsoleFactory.class;
            } else {
                userConsoleFactoryClass = Class.forName(userConsoleFactoryClassStr);
                if (!ConsoleFactory.class.isAssignableFrom(userConsoleFactoryClass)) {
                    throw new RuntimeException("class  ConsoleFactory is not assignable from :" + userConsoleFactoryClass.getName());
                }
            }
        }
    }

    private static void prepareDefaultCodebaseDB(PainDB db) throws Exception {
        CodebaseData data = new CodebaseData(db, Codebase.CODEBASE_VERSION);
        Administrator admin = new Administrator(db);
        admin.setLogin("root");
        admin.setPassword("root");
        data.getAdmins().put(admin.getLogin(), admin);
        db.setRoot(data);
        db.flush();
    }

}