#ifdef HAVE_CONFIG_H
	#include "config.h"
#endif

#include <Eris/UIFactory.h>

#include <Atlas/Objects/Entity/UIEntity.h>
#include <Atlas/Objects/Entity/Frame.h>
#include <Atlas/Objects/Entity/Slot.h>

#include <sigc++/object_slot.h>

Eris::UI::Factory::Factory(const Atlas::Objects::Entity::UIEntity& entity,
			   const std::string& base_type,
			   const IDList& parents,
		           const Atlas::Message::Element::MapType& attrs,
			   BaseGen* gen) :
	_id(entity.getId()), _id_list(parents), _gen(gen),
	_persistent(false), _refcount(1), _attrs(attrs)
{
  _id_list.push_back(_id);
  _attrs["display_status"] = entity.getDisplayStatus();
  Atlas::Objects::Root::const_iterator I, last(entity, base_type);
  for(I = entity.begin(); I != last; ++I)
    _attrs[I->first] = I->second;
}

Eris::UI::Element*
Eris::UI::Factory::create(IDMap& idmap) const
{
  Element* elem = _gen->create(attrs());
  for(IDList::const_iterator I = idlist().begin(); I != idlist().end(); ++I)
    idmap[*I] = elem;
  return elem;
}

Eris::UI::Factory*
Eris::UI::Factory::parse(const Atlas::Message::Element::MapType& map,
			 const Bindings&) const
{
  Atlas::Objects::Entity::UIEntity entity;

  // FIXME isn't there a better way to do this?
  Atlas::Message::Element::MapType::const_iterator I = map.begin();
  while(I != map.end())
    entity.setAttr(I->first, I->second);

  return new Factory(entity, "u_i_entity", idlist(), attrs(), _gen);
}

Eris::UI::FrameFactory::~FrameFactory()
{
  for(ChildList::iterator I = _children.begin(); I != _children.end(); ++I)
    (*I)->unref();
  if(persistent()) // original frame factory, owns BaseGen
    delete _gen;
}

Eris::UI::Element*
Eris::UI::FrameFactory::create(IDMap& idmap) const
{
  FrameElement* frame = _gen->create(_valign, _halign, _rel_pos, attrs());

  // we don't want child slots connecting to anything
  // outside this frame, so we use a local idmap

  IDMap local;

  for(IDList::const_iterator I = idlist().begin(); I != idlist().end(); ++I)
    local[*I] = frame;

  for(ChildList::const_iterator I = _children.begin(); I != _children.end(); ++I) {
    Element* child = (*I)->create(local);
    SlotElement* slot = dynamic_cast<SlotElement*>(child);
    if(slot)
      frame->packSlot(slot);
    else
      frame->pack(child);
  }

  // FIXME if one insertion fails, do they all fail?
  idmap.insert(local.begin(), local.end());

  return frame;
}

Eris::UI::Factory*
Eris::UI::FrameFactory::parse(const Atlas::Message::Element::MapType& map,
			      const Bindings& factories) const
{
  Atlas::Objects::Entity::Frame frame;

  // FIXME isn't there a better way to do this?
  Atlas::Message::Element::MapType::const_iterator I = map.begin();
  while(I != map.end())
    frame.setAttr(I->first, I->second);

  FrameFactory* factory = new FrameFactory(frame, idlist(), factories, attrs(), _gen);

  // if 'contains' isn't set, then we're dealing with a child
  // of Frame being used as an opaque type, so we keep the same
  // children
  if(factory->_children.empty()) {
    factory->_valign = _valign;
    factory->_halign = _halign;
    factory->_rel_pos = _rel_pos;
    factory->_children = _children;
    for(ChildList::const_iterator I = _children.begin(); I != _children.end(); ++I)
      (*I)->ref();
    return factory;
  }

  return factory;
}

Eris::UI::FrameFactory::FrameFactory(const Atlas::Objects::Entity::Frame& frame,
				     const IDList& parents, const Bindings& factories,
			             const Atlas::Message::Element::MapType& attrs,
				     BaseGen* gen)
	: Factory(frame, "frame", parents, attrs, 0),
	  _valign(frame.getValign()), _halign(frame.getHalign()),
	  _rel_pos(frame.getRelPos()), _gen(gen)
{
  const Atlas::Message::Element::ListType& contains = frame.getContains();

  Atlas::Message::Element::ListType::const_iterator I;

  for(I = contains.begin(); I != contains.end(); ++I) {
    Factory* child = factories.findFactory(*I);
    if(child) {
      _children.push_back(child);
      child->ref();
    }
  }
}

Eris::UI::SlotFactory::SlotFactory(const Atlas::Objects::Entity::Slot& slot,
				   const IDList& parents,
				   const Atlas::Message::Element::MapType& attrs,
				   BaseGen* gen)
	: Factory(slot, "slot", parents, attrs, gen), _target(slot.getTarget())
{

}

Eris::UI::Element*
Eris::UI::SlotFactory::create(IDMap& idmap) const
{
  SlotElement* slot = dynamic_cast<SlotElement*>(gen()->create(attrs()));
  assert(slot);
  Atlas::Message::Element::ListType::const_iterator T;
  for(T = _target.begin(); T != _target.end(); ++T) {
    if(!T->isString())
      continue;
    IDMap::iterator I = idmap.find(T->asString());
    if(I != idmap.end())
      I->second->PropertiesChanged.connect(SigC::slot(*slot, &SlotElement::action));
  }
  for(IDList::const_iterator I = idlist().begin(); I != idlist().end(); ++I)
    idmap[*I] = slot;
  return slot;
}

Eris::UI::Factory*
Eris::UI::SlotFactory::parse(const Atlas::Message::Element::MapType& map,
			     const Bindings&) const
{
  Atlas::Objects::Entity::Slot slot;

  // FIXME isn't there a better way to do this?
  Atlas::Message::Element::MapType::const_iterator I = map.begin();
  while(I != map.end())
    slot.setAttr(I->first, I->second);

  if(slot.getTarget().empty())
    return 0;

  return new SlotFactory(slot, idlist(), attrs(), gen());
}

Eris::UI::Bindings::Bindings()
{
  // FIXME bind slot factories here
}

Eris::UI::Bindings::~Bindings()
{
  for(FactoryMap::iterator I = _factory_map.begin(); I != _factory_map.end(); ++I)
    I->second->unref();
}

void
Eris::UI::Bindings::parse(const Atlas::Message::Element& obj)
{
  if(!obj.isMap())
    return;

  const Atlas::Message::Element::MapType& map = obj.asMap();

  Atlas::Message::Element::MapType::const_iterator J = map.find("display_status");

  if(J != map.end() && J->second.isString() && J->second.asString() == "console") {
    Atlas::Objects::Entity::UIEntity entity;

    // FIXME isn't there a better way to do this?
    Atlas::Message::Element::MapType::const_iterator I = map.begin();
    while(I != map.end())
      entity.setAttr(I->first, I->second);

    createConsoleElement(entity);
    return;
  }

  Atlas::Message::Element::MapType::const_iterator parents = map.find("parents");

  if(parents == map.end() || !parents->second.isList())
    return;

  J = map.find("objtype");

  if(J == map.end() || !J->second.isString())
    return;

  const std::string& objtype = J->second.asString();

  if(objtype != "class" && objtype != "object")
    return;

  Factory* factory = 0;

  Atlas::Message::Element::ListType::const_iterator I;
  for(I = parents->second.asList().begin(); I != parents->second.asList().end(); ++I) {
    Factory* parent = findFactory(*I);
    if(parent) {
      factory = parent->parse(obj.asMap(), *this);
      break;
    }
  }

  if(!factory)
    return;

  if(objtype == "class") {
    if(!bind(factory))
      factory->unref();
  }
  else { // objtype == "object"
    Factory::IDMap idmap;
    display(factory->create(idmap), factory->id());
    factory->unref();
  }
}

void
Eris::UI::Bindings::clear()
{
  FactoryMap keepers;

  for(FactoryMap::iterator I = _factory_map.begin(); I != _factory_map.end(); ++I) {
    if(I->second->persistent())
      keepers.insert(*I);
    else
      I->second->unref();
  }

  _factory_map = keepers;
}


syntax highlighted by Code2HTML, v. 0.9.1