#ifndef ENGINE_H
#define ENGINE_H

#include <astring.h>
#include <objectsystem.h>

class Module;
class System;

class Actor;
class Scene;
class ResourceSystem;
class RenderSystem;
class Resource;
class World;
class PlatformAdaptor;
class NativeBehaviour;

#if defined(SHARED_DEFINE) && defined(_WIN32)
    #ifdef ENGINE_LIBRARY
        #define ENGINE_EXPORT __declspec(dllexport)
    #else
        #define ENGINE_EXPORT __declspec(dllimport)
    #endif
#else
    #define ENGINE_EXPORT
#endif

class ENGINE_EXPORT Engine : public ObjectSystem {
public:
    Engine();
    ~Engine();

/*
    Main cycle
*/
    static bool init();

    static bool start();

    static void update();

/*
    Settings
*/
    static Variant value(const TString &key, const Variant &defaultValue = Variant());

    static void setValue(const TString &key, const Variant &value);

    static void syncValues();

/*
    Product info
*/
    static TString organizationName();

    static void setOrganizationName(const TString &name);

    static TString applicationName();

    static void setApplicationName(const TString &name);

    static TString applicationVersion();

    static void setApplicationVersion(const TString &version);

/*
    Resource management
*/
    static Resource *loadResource(const TString &path);
    static Resource *loadResourceAsync(const TString &path);

    static void unloadResource(const TString &path);
    static void unloadResource(Resource *resource);

    static void reloadResource(const TString &path);

    template<typename T>
    static T *loadResource(const TString &path) {
        return dynamic_cast<T *>(loadResource(path));
    }

    template<typename T>
    static T *loadResourceAsync(const TString &path) {
        return dynamic_cast<T *>(loadResourceAsync(path));
    }

    static bool isResourceExist(const TString &path);

    static TString reference(Object *object);

    static bool reloadBundle();

    static ResourceSystem *resourceSystem();

    static RenderSystem *renderSystem();

/*
    Scene management
*/
    static World *world();

    static Scene *loadScene(const TString &path, bool additive);

    static void unloadScene(Scene *scene);

    static void unloadAllScenes();

/*
    Misc
*/
    static bool isGameMode();

    static void setGameMode(bool flag);

    static TString locationAppConfig();

    static bool loadTranslator(const TString &table);

    static TString translate(const TString &source);

    static void addModule(Module *module);

    static bool setPlatformAdaptor(PlatformAdaptor *platform);

    static Actor *composeActor(const TString &component, const TString &name, Object *parent = nullptr);

    template<typename T>
    static Actor *composeActor(const TString &name, Object *parent = nullptr) {
        return composeActor(T::metaClass()->name(), name, parent);
    }

    Object::ObjectList getAllObjectsByType(const TString &type) const override;

private:
    friend class System;
    friend class NativeBehaviour;

    bool event(Event *event) override;

    static void addSystem(System *system);
    static void removeSystem(System *system);

    static void addNativeBehaviour(NativeBehaviour *native);
    static void removeNativeBehaviour(NativeBehaviour *native);
};

#endif // ENGINE_H
