#include "scenemanager.h"
#include <map>
#include <string>
#include <utility>
#include <stdio.h>
#include "storage.h"
#include <iostream>
#include "scene.h"
#include "config.h"
#include "sceneimage.h"
#include "sceneswitcher.h"
#include "textedit.h"
#include "numeric.h"
#include "video.h"
#include "fpscounter.h"
#include "timer.h"

#include "me_signal.h"

Signal _mouse_press_signal_;
Signal _mouse_release_signal_;
Signal _mouse_move_signal_;
Signal _activate_signal_;
Signal _deactivate_signal_;
Signal _keyboard_press_signal_;
Signal _keyboard_release_signal_;
Signal _character_signal_;


static std::string _cur_scene;
static std::string _next_scene;
std::string _video_name_;

static Storage<std::string, Scene*> _scenes;
//	static Storage<std::string,

bool load_scenes ( std::string storage_file, std::string video_name )
{
    std::cerr << "Loading scenes from file:" << storage_file << std::endl;
    if ( !config_create ( "ScenesLoader", "libtextconfig", 0, 0 ) )
    {
        std::cerr << "Error while trying to create config from libtextconfig: " << config_error() << std::endl;
        return false;
    }
    Config_Interface *intf = config_get_interface ( "ScenesLoader" );
    if ( !intf )
    {
        std::cerr << "Erro with config";
        return false;
    }
    std::string mas[1];
    mas[0] = storage_file;
    char number[7] = "";
    intf->_load ( mas, 1 );

    std::string prefix = "Scenes->Scene";
    std::string name;
    std::string file;
    Scene *scn = 0;
    for ( int a = 0;a < 1000000;a++ )
    {
        snprintf ( number, 6, "%d", a );
        name = intf->_get_value ( prefix + number + "->name", "" );
        if ( name.length() == 0 ) break;
        file = intf->_get_value ( prefix + number + "->file", "" );
        bool preload = false;
        preload = ( intf->_get_value ( prefix + number + "->preload", "false" ) == "true" ) ? true : false;
        std::cerr << "Loading scene \"" << name << "\" from file \"" << file << std::endl;
        std::string scene_type = intf->_get_value ( prefix + number + "->type", "generic" );
        if ( _scenes.exist ( name ) )
        {
            scn = _scenes.get ( name );
            delete scn;
            _scenes.remove ( name );
        }
        SceneFabric fabric;
        std::cerr << "Scene type: " << scene_type << std::endl;
        scn = fabric[scene_type];
        if ( !scn )
        {
            std::cerr << "Can't create scene of type \"" << scene_type << "\"" << std::endl;
            return false;
        }
        scn->set_video_name ( video_name );
        scn->set_storage_file ( file );
        if ( preload )
        {
            std::cerr << "Scene will be preloaded" << std::endl;
            if ( scn->load_scene( ) )
            {
                _scenes.insert ( name, scn );
            }
            else
            {
                std::cerr << "Can't load scene \"" << name << "\" from file \"" << file << "\"" << std::endl;
                return false;
            }
        }
        else
        {
            std::cerr << "Scene willn't be preloaded" << std::endl;
            _scenes.insert ( name, scn );
        }
    }
    std::cerr << "All scenes loaded" << std::endl;
    return true;
}

Scene* get_scene ( std::string scene_name )
{
    if ( !_scenes.exist ( scene_name ) ) return 0;
    return _scenes.get ( scene_name );
}
void process_events ( int max_events_at_time )
{
    Video_Interface *vid = video_get_interface ( _video_name_ );
    if ( !vid ) return;
    Event ev;
    for ( int a=0; a<max_events_at_time; ++a )
    {
        ev = vid->_get_event();
        switch ( ev._get_type() )
        {
            case KEYBOARD_PRESS:
                _keyboard_press_signal_.clear_params();
                _keyboard_press_signal_.add_params ( ( int64_t ) ev._get_keyboard_state() );
                _keyboard_press_signal_.rise();
                break;
            case KEYBOARD_RELEASE:
                _keyboard_release_signal_.clear_params();
                _keyboard_release_signal_.add_params ( ( int64_t ) ev._get_keyboard_state() );
                _keyboard_release_signal_.rise();
                break;
            case MOUSE_PRESS:
            {
                _mouse_press_signal_.clear_params();
                _mouse_press_signal_.add_params ( ev._x );
                _mouse_press_signal_.add_params ( ev._y );
                _mouse_press_signal_.add_params ( ev._dx );
                _mouse_press_signal_.add_params ( ev._dy );
                _mouse_press_signal_.add_params ( ( int64_t ) ev._button);
                _mouse_press_signal_.rise();
                break;
            }
            case MOUSE_RELEASE:
            {
                _mouse_release_signal_.clear_params();
                _mouse_release_signal_.add_params ( ev._x );
                _mouse_release_signal_.add_params ( ev._y );
                _mouse_release_signal_.add_params ( ev._dx );
                _mouse_release_signal_.add_params ( ev._dy );
                _mouse_release_signal_.add_params ( ( int64_t ) ev._button );
                _mouse_release_signal_.rise();
                break;
            }
            case MOUSE_MOVE:
            {
                _mouse_move_signal_.clear_params();
                _mouse_move_signal_.add_params ( ev._x );
                _mouse_move_signal_.add_params ( ev._y );
                _mouse_move_signal_.add_params ( ev._dx );
                _mouse_move_signal_.add_params ( ev._dy );
                _mouse_move_signal_.add_params ( ( int64_t ) ev._button );
                _mouse_move_signal_.rise();
                break;
            }
            case CHARACTER:
            {
                _character_signal_.clear_params();
                _character_signal_.add_params ( ( int64_t ) ev._get_char() );
                std::cerr<<"CHARACTER SIGNAL"<<std::endl;
                _character_signal_.rise();
                break;
            }
            case EXIT:
            {
                set_next_scene ( "exit" );
                return;
            }
            default: break;

        }
    }
}
void run_scene()
{
    Scene *scn;
    if ( !_scenes.exist ( _cur_scene ) ) scn = 0;
    else scn = _scenes.get ( _cur_scene );
    if ( !scn )
    {
        std::cerr << "No such scene \"" << _cur_scene << "\"" << std::endl;
        return;
    }
    scn->slot__on_activate ( 0 );
    std::cerr << "Processing scene \"" << _cur_scene << "\"" << std::endl;
    if ( !scn->scene_loaded() ) //Если сцена не загружена и ее не удалось загрузить то выходим
        if ( !scn->load_scene() ) return;


    for ( ;; )
    {
        if ( !scn->draw() ) return; //Если во время рисования возникли ошибки - выходим
        process_events ( MAX_EVENTS_AT_TIME );

        if ( _cur_scene != _next_scene )
        {
            if ( _next_scene == "exit" )
            {
                std::cerr<<"Exitting"<<std::endl;
                return; //Спец сцена при переходе на которую мы выходим. Фигня которую надо удалить
            }
                //Смена сцены. Деактивируем старую и активируем новую
                scn->slot__on_deactivate ( 0 );

                if ( _scenes.exist ( _next_scene ) ) _cur_scene = _next_scene;
                else _next_scene = _cur_scene;
                if ( !_scenes.exist ( _cur_scene ) ) scn = 0;
                else scn = _scenes.get ( _cur_scene );
                if ( !scn )
                {
                    std::cerr << "No such scene \"" << _cur_scene << "\"" << std::endl;
                    return;
                }
                std::cerr << "Processing scene \"" << _cur_scene << "\"" << std::endl;
                if ( !scn->scene_loaded() ) //Если сцена не загружена и ее не удалось загрузить то выходим
                    if ( !scn->load_scene() ) return;

                scn->slot__on_activate ( 0 );
		scn->reinit();
        }
        }
    }

    void process_scene ( std::string start_scene )
    {
        _cur_scene = start_scene;
        _next_scene = start_scene;
        run_scene();
    }
    void set_next_scene ( std::string next_scene )
    {
        _next_scene = next_scene;
    }
    void init ( std::string video_name )
    {
        SceneObjectFabric::add_fabric ( "image", &SceneImageFabric );
        SceneObjectFabric::add_fabric ( "scene_switch", &SceneSwitcherFabric );
        SceneObjectFabric::add_fabric ( "text_object", &TextObjectFabric );
        SceneObjectFabric::add_fabric ( "textedit", &TextEditFabric );
        SceneObjectFabric::add_fabric ( "numeric", &NumericObjectFabric );
        SceneObjectFabric::add_fabric ( "FPSCounter", &FPSCounterFabric );
        SceneObjectFabric::add_fabric ( "timer", &TimerFabric );

        SceneFabric::add_fabric ( "generic", &GenericSceneFabric );
        _activate_signal_.set_name ( video_name+"->activate" );
        _deactivate_signal_.set_name ( video_name+"->deactivate" );
        _mouse_press_signal_.set_name ( video_name+"->mouse_press" );
        _mouse_release_signal_.set_name ( video_name+"->mouse_release" );
        _mouse_move_signal_.set_name ( video_name+"->mouse_move" );
        _keyboard_press_signal_.set_name ( video_name+"->keyboard_press" );
        _keyboard_release_signal_.set_name ( video_name+"->keyboard_release" );
        _character_signal_.set_name ( video_name+"->character" );

        _video_name_ = video_name;
        Video_Interface *vid = video_get_interface ( _video_name_ );
        if ( !vid ) return;
        vid->_enable_keyboard_event ( true );
        vid->_enable_mouse_button_event ( true );
        vid->_enable_char_event ( true );
        vid->_enable_mouse_move_event ( true );
    }
