#include "scene.h"
#include "sceneobjectfabric.h"
#include "config.h"
#include <iostream>
#include "sceneobject.h"
#include "video_int.h"
#include "video.h"
#include "event.h"
#include <stdio.h>
#include <assert.h>
#define LOG_SCENE std::cerr<<"SCENE:"

Storage<std::string, SceneFabric*> SceneFabric::_fabrics_;
SceneFabrics<Scene> GenericSceneFabric;

Scene::Scene( ) :
        INIT_SLOT ( Scene, this, _on_mouse_press ),
        INIT_SLOT ( Scene, this, _on_activate ),
        INIT_SLOT ( Scene, this, _on_deactivate ),
        _loaded_ ( false ),
        _active_object_ ( 0 )
{}


Scene::~Scene()
{}


SceneFabric::SceneFabric()
{}


SceneFabric::~SceneFabric()
{}

Scene * SceneFabric::operator [ ] ( std::string name )
{
    if ( !_fabrics_.exist ( name ) ) return 0;
    return ( *_fabrics_.get ( name ) ) ();
}
void SceneFabric::add_fabric ( std::string name, SceneFabric* fabric )
{
    if ( !_fabrics_.exist ( name ) ) _fabrics_.insert ( name, fabric );
}


bool Scene::load_scene( )
{
    LOG_SCENE << "Loading scene from:" << _storage_file_ << std::endl;
    if ( _video_name_ == "" || _storage_file_ == "" )
    {
        LOG_SCENE << __FILE__ << ":" << __LINE__ << ": Scene wasn't correctly inited!!!" << std::endl;
    }
    Video_Interface *vid = video_get_interface ( _video_name_ );
    if ( !vid ) return false;

    if ( !config_create ( "SceneLoader", "libtextconfig", 0, 0, true ) )
    {
        LOG_SCENE << "Error while trying to create config from libtextconfig: " << config_error() << std::endl;
        return false;
    }
    Config_Interface *intf = config_get_interface ( "SceneLoader" );
    if ( !intf )
    {
        LOG_SCENE << "Error with config";
        return false;
    }
    std::string mas[2];
    mas[0] = _storage_file_;
    char number[9] = "";
    intf->_load ( mas, 1 );

    _name_ = intf->_get_value ( "Scene->name", "" );
    _show_mouse_cursor_ = atol ( intf->_get_value ( "Scene->show_mouse_cursor", "1" ).c_str() ) > 0;
    init_signals();//Зададим имена сигналам

    std::string prefix = "Scene->Objects->Object";
    std::string obj_prefix = "Objects->";
    int z_level;
    std::string name;
    SceneObject *obj = 0;
    SceneObjectFabric fabric;
    for ( int a = 0;a < 1000000;a++ )
    {
        snprintf ( number, 8, "%d->", a );
        name = intf->_get_value ( prefix + number + "name", "" );
        if ( name.length() == 0 ) break;
        z_level = atol ( intf->_get_value ( prefix + number + "z_level", "0" ).c_str() );
        if ( _objects_by_name.exist ( name ) )
        {
            obj = _objects_by_name.get ( name );
            delete obj;
            _objects_by_name.remove ( name );
            _objects_z.remove ( name );
        }
        std::string type;
        std::string cur_pref = obj_prefix + name + "->";
        type = intf->_get_value ( cur_pref + "type", "" );
        LOG_SCENE << "Loading object \"" << name << "\" with type \"" << type << "\"" << std::endl;

        obj = fabric[type];
        if ( !obj )
        {
            LOG_SCENE << "Can't load object\"" << name << "\" from file \"" << _storage_file_ << "\"" << std::endl;
            LOG_SCENE << "No object of type \"" << type << "\"" << std::endl;
            return false;
        }

        obj->set_video_name ( _video_name_ );
        if ( obj->load ( "SceneLoader", cur_pref, _name_, name ) )
        {
            _objects_by_name.insert ( name, obj );
            _objects_z.insert ( name, z_level );
            _object_list.push_back ( name );
        }
        else
        {
            LOG_SCENE << "Can't load object \"" << name << "\" from file \"" << _storage_file_ << "\"" << std::endl;
            LOG_SCENE << "Error: " << obj->_get_error() << std::endl;
            return false;
        }
    }
    config_remove ( "SceneLoader" );
    _loaded_ = true;

    vid->_enable_mouse_button_event ( true );
    vid->_enable_keyboard_event ( true );
    vid->_enable_char_event ( true );
    return true;
}
bool Scene::draw( )
{
    _new_cycle_.rise();
    _view_queue.clear();
    std::vector<std::string>::iterator object_it;
    for ( object_it = _object_list.begin();object_it != _object_list.end();++object_it )
    {
        int z;
        z = _objects_z.get ( *object_it );
        _view_queue.insert ( make_pair ( z, *object_it ) );
    }
    std::multimap<int, std::string>::iterator view_it;
    Video_Interface *vid = video_get_interface ( _video_name_ );
    if ( !vid ) return false;
    vid->_enable_mouse_cursor ( _show_mouse_cursor_ );
    for ( view_it = _view_queue.begin();view_it != _view_queue.end(); ++view_it )
    {
        SceneObject *obj;
        obj = _objects_by_name.get ( view_it->second );
        obj->draw();
    }
    vid->_draw();
    return true;
}

DEFINE_SLOT ( Scene, _on_mouse_press )
{
    Video_Interface *vid = video_get_interface ( _video_name_ );
    if ( !vid ) return false;

    SceneObject *obj;

    float x, y;
    MOUSE_BUTTON mb;
    try
    {
        x = args->float_args.at ( 0 );
        y = args->float_args.at ( 1 );
        mb = ( MOUSE_BUTTON ) args->int_args.at ( 0 );
    }
    catch ( ... )
    {
        return false;
    }
    VideoRect rect;
    std::multimap<int, std::string>::reverse_iterator view_it;
    for ( view_it = _view_queue.rbegin();view_it != _view_queue.rend(); ++view_it )
    {
        obj = _objects_by_name.get ( view_it->second );
        obj->_get_active_area_rect ( &rect );
        if ( rect.x_s <= x && rect.x_e >= x && rect.y_s <= y && rect.y_e >= y ) //Мы нашли нужный объект
        {
            std::cerr << "Found an object:" << obj->_get_name() << std::endl;
            if ( _active_object_ ) 
            {
//                _active_object_->dis();
            }
            _active_object_ = obj;
            _active_object_->connect_signals();
            _activate_signal_.add_params ( x );
            _activate_signal_.add_params ( y );
            _activate_signal_.add_params ( ( int64_t ) mb );
            _activate_signal_.rise();
            break;
        }
    }
    return true;
}
DEFINE_SLOT ( Scene, _on_activate )
{
    init_signals();
    init_slots();
    return true;
}
DEFINE_SLOT ( Scene, _on_deactivate )
{
    deinit_slots();
    return true;
}

void Scene::init_signals()
{
    _new_cycle_.set_name ( _name_ + "->new_cycle" );
    _activate_signal_.set_name ( _name_ + "->activate" );
    _reinit_signal_.set_name( _name_ + "->reinit" );
}
template <typename A>
        struct InitSlotsFunctor
{
    void operator() ( A object )
    {
        std::cerr<<"Initing object:"<<object.first<<std::endl;
        object.second->register_slots();
    }
};

void Scene::init_slots()
{
    register_slot ( _name_ + "->" + "on_mouse_press", &_on_mouse_press );
    connect ( _video_name_ + "->mouse_press", _name_ + "->on_mouse_press" );
    
    //Зарегистрируем слоты объектов
    InitSlotsFunctor<std::pair<std::string, SceneObject*> > initer;
    _objects_by_name.for_each(initer);
}

template <typename A>
        struct DeinitSlotsFunctor
{
    void operator() ( A object )
    {
        std::cerr<<"Deiniting object:"<<object.first<<std::endl;
        object.second->unregister_slots();
    }
};
void Scene::deinit_slots()
{
    disconnect ( _video_name_ + "->mouse_press", _name_ + "->on_mouse_press" );
    unregister_slot ( _name_ + "->" + "on_mouse_press" );
    
    //Отсоединим все сигналы
    std::cerr<<"Disconnecting all signals"<<std::endl;
    DeinitSlotsFunctor<std::pair<std::string, SceneObject*> > cleaner;
    _objects_by_name.for_each(cleaner);
}

bool Scene::add_object ( const std::string &object_name, SceneObject * obj, int z_level )
{
    _objects_by_name.insert ( object_name, obj );
    _objects_z.insert ( object_name, z_level );
    _object_list.push_back ( object_name );
    return true;
}

bool Scene::remove_object ( const std::string & object_name )
{
    SceneObject *obj = _objects_by_name.get ( object_name );
    obj->disconnect_signals();
    obj->unregister_slots();
    _objects_by_name.remove ( object_name );
    _objects_z.remove ( object_name );
    _object_list.erase ( std::find ( _object_list.begin(), _object_list.end(), object_name ) );
    return true;
}

void Scene::reinit( )
{
	std::cerr<<__FUNCTION__<<std::endl;
	_reinit_signal_.rise();
}
