void
SectorBuilder::parse_body(const FileReader& reader)
{
  { // check version
    int version = 1;
    if (!reader.get("version", version))
      std::cerr << "Warning no version specified in levelformat.\n";

    if (version > 3)
      std::cerr << "Warning: format version is newer than game.\n";
  }

  //reader.get("name",          name);
  //reader.get("music",         music);
  //reader.get("init-script",   init_script);

  Color ambient_light;
  if (reader.get("ambient-color", ambient_light))
  {
    m_sector.set_ambient_light(ambient_light);
  }
    
  parse_objects(reader);
  parse_navgraph(reader);
}
Trigger::Trigger(const FileReader& props) :
  area(),
  callback(),
  triggered(false), 
  last_trigger(),
  one_time_trigger(false)
{
  float x = -1;
  float y = -1;
  float width = -1;
  float height = -1;
  
  props.get("x", x);
  props.get("y", y);
  props.get("width", width);
  props.get("height", height);
  props.get("callback", callback);
  props.get("one-time-trigger", one_time_trigger);
  
  if(x < 0 || y < 0 || width < 0 || height < 0)
    throw std::runtime_error("Invalid or missing area in Trigger object");
 
  area.left = x;
  area.top = y;
  area.right = area.left + width;
  area.bottom = area.top + height;
}
void
SectorBuilder::parse_objects(const FileReader& reader)
{
  FileReader objects_reader;

  // FIXME: try both for backward compatibility
  if (!reader.get("objects", objects_reader) &&
      !reader.get("layers", objects_reader))
  {
    throw std::runtime_error("No objects specified");
  }
  else
  {
    std::vector<FileReader> objects_readers = objects_reader.get_sections();
    for(std::vector<FileReader>::iterator i = objects_readers.begin(); i != objects_readers.end(); ++i)
    {
      parse_object(*i);
    }

    // Set the parents properly
    for(std::map<GameObjectHandle, std::string>::iterator i = parent_table.begin(); i != parent_table.end(); ++i)
    {
      std::map<std::string, GameObjectHandle>::iterator j = id_table.find(i->second);
      if (j == id_table.end())
      {
        std::cout << "Error: Couldn't resolve 'id': " << i->second << std::endl;
      }
      else
      {
        i->first->set_parent(j->second);
      }
    }
  }
}
VRDummy::VRDummy(FileReader& props)
{
  props.get("name", name);
  props.get("pos",  pos);
  props.print_unused_warnings("VRDummy");
  
  sprite = Sprite3D("models/characters/vrdummy/vrdummy.wsprite");
  rotation = 0;

  highlight = Sprite("images/hedgehog_highlight.sprite");
  highlight.set_blend_func(GL_SRC_ALPHA, GL_ONE);
  
  jump_time = 0;
}
TileDescription::TileDescription(FileReader& props)
  : width(0), height(0)
{
  props.get("ids",    ids);
  props.get("image",  filename);
  props.get("colmap", colmap);
  props.get("width",  width);
  props.get("height", height);
    
  props.print_unused_warnings("tiles");

  if(filename == "")
    throw std::runtime_error("Missing color-image");
}
Hedgehog::Hedgehog(FileReader& props)
  : sprite("images/hedgehog.sprite"),
    die_sprite("images/hedgehog_die1.sprite"),
    light("images/hedgehog_light.sprite"),
    highlight("images/hedgehog_highlight.sprite")
{
  props.get("name", name);
  props.get("pos",  pos);
  props.print_unused_warnings("hedgehog");
  
  direction_left = false;
  state = WALKING;
  light.set_blend_func(GL_SRC_ALPHA, GL_ONE);
  highlight.set_blend_func(GL_SRC_ALPHA, GL_ONE);
}
BackgroundGradient::BackgroundGradient(const FileReader& props) :
  drawable()
{
  std::vector<float> colors;

  //props.get("z-pos",  z_pos);
  props.get("colors", colors);

  if (colors.size() % (3 + 4 + 4 + 2) != 0)
  {
    std::cout << "BackgroundGradient: specified color gradient is invalid" << std::endl;
    /** Color gradients are in the format:
          
        (colors start midpoint end R1 G1 B1 A1 R2 G2 B2 A2 I I
        start midpoint end R1 G1 B1 A1 R2 G2 B2 A2 I I
        ...)

        I is ignored

        all specified in float, this is similar to Gimps gradients
        so you can easily copy&paste
    */
    colors.clear();
  }

  drawable.reset(new GradientDrawable(colors));
  Sector::current()->get_scene_graph().add_drawable(drawable);
}
VRDummy::VRDummy(const FileReader& props) :
  sprite(),
  highlight(),
  rotation(),
  jump_time()
{
  props.get("name", name);
  props.get("pos",  pos);
    
  sprite = Sprite3D(Pathname("models/characters/vrdummy/vrdummy.wsprite"));
  rotation = 0;

  highlight = Sprite(Pathname("images/hedgehog_highlight.sprite"));
  highlight.set_blend_func(GL_SRC_ALPHA, GL_ONE);
  
  jump_time = 0;
}
SparkDrawer::SparkDrawer(FileReader& props)
  : color(1.0f, 1.0f, 1.0f),
    width(1.0f),
    buffer()
{
  props.get("color", color);
  props.get("width", width);

  buffer.reset(new VertexArrayDrawable(Vector2f(), 0.0f, Matrix::identity())); 
}
ParticleSystems::ParticleSystems(const FileReader& reader) :
  m_systems(),
  m_drawables()
{
  std::string filename;
  Vector2f    pos;
  reader.get("name", filename);
  reader.get("pos",  pos);

  { // Load the ParticleSystems
    FileReader root_reader = FileReader::parse(Pathname(filename));
    if(root_reader.get_name() != "particle-systems") 
    {
      std::ostringstream msg;
      msg << "'" << filename << "' is not a particle-system file";
      throw std::runtime_error(msg.str());
    }

    std::vector<FileReader> sections = root_reader.get_sections();
    for(std::vector<FileReader>::iterator i = sections.begin(); i != sections.end(); ++i)
    { 
      if (i->get_name() == "particle-system")
      {
        boost::shared_ptr<ParticleSystem> particle_system(new ParticleSystem(*i));
        particle_system->set_pos(pos);
        m_systems.push_back(particle_system);
      }
    }
  }

  for(Systems::iterator i = m_systems.begin(); i != m_systems.end(); ++i)
  {
    boost::shared_ptr<ParticleSystemDrawable> drawable(new ParticleSystemDrawable(**i));

    m_drawables.push_back(drawable);
    Sector::current()->get_scene_graph().add_drawable(drawable);
  }
}
void
SectorBuilder::parse_layer(const FileReader& reader)
{
  FileReader objects_reader;

  if (!reader.get("objects", objects_reader))
  {
    throw std::runtime_error("No objects specified");
  }
  else
  {
    std::vector<FileReader> objects_readers = objects_reader.get_sections();
    for(std::vector<FileReader>::iterator i = objects_readers.begin(); i != objects_readers.end(); ++i)
    {
      parse_object(*i);
    }
  }
}
TileMap::TileMap(FileReader& props)
{
    using namespace lisp;
    int width = -1;
    int height = -1;
    z_pos = 0;
    total_time = 0;

    props.get("name", name);
    props.get("z-pos", z_pos);
    props.get("width", width);
    props.get("height", height);
    if(width <= 0 || height <= 0) {
        throw std::runtime_error(
            "Invalid width or height defined or "
            "data defined before width and height");
    }

    if(width <= 0 || height <= 0) {
        throw std::runtime_error(
            "Invalid width or height defined or "
            "data defined before width and height");
    }

    std::string data_filename;
    if (props.get("data-file", data_filename))
    {
        PHYSFS_file* file = PHYSFS_openRead(data_filename.c_str());
        if (!file)
        {
            throw std::runtime_error("Couldn't open tiledata file: " + data_filename);
        }

        field = Field<Tile*>(width, height);

        for(int y = 0; y < height; ++y)
            for(int x = 0; x < width; ++x)
            {
                uint16_t result;
                if(PHYSFS_readULE16(file, &result) == 0) {
                    std::ostringstream msg;
                    msg << "Problem reading uint16 value: " << PHYSFS_getLastError();
                    throw std::runtime_error(msg.str());
                }

                field(x, y) = TileFactory::current()->create(result);
            }

        PHYSFS_close(file);
    }
    else // read data directly from the levelfile
    {
        Field<int> tmpfield(width, height);

        props.get("data", tmpfield.get_vector());

        field = Field<Tile*>(width, height);
        for (int y = 0; y < field.get_height (); ++y)
        {
            for (int x = 0; x < field.get_width (); ++x)
            {
                field(x, y) = TileFactory::current()->create(tmpfield(x, y));
            }
        }
    }

    props.print_unused_warnings("tilemap");


    if(field.size() == 0)
        throw std::runtime_error("No tiles defined in tilemap");
}
void
SectorBuilder::parse_navgraph(const FileReader& reader)
{
  std::map<std::string, NodeHandle> id_to_node;

  FileReader navgraph_reader;
  if (!reader.get("navigation", navgraph_reader))
  {
    // throw std::runtime_error("SectorBuilder: 'navigation' section missing");
    std::cout << "SectorBuilder: 'navigation' section missing" << std::endl;
  }
  else
  {
    FileReader nodes_reader;
    if (navgraph_reader.get("nodes", nodes_reader))
    {
      const std::vector<FileReader>& nodes_readers = nodes_reader.get_sections();
      for(std::vector<FileReader>::const_iterator i = nodes_readers.begin(); i != nodes_readers.end(); ++i)
      {
        if (i->get_name() != "navgraph-node")
        {
          std::cout << "SectorBuilder::parse_navgraph(): Unknown nodes tag: " << i->get_name() << std::endl;
        }
        else
        {
          Vector2f pos;
          if (i->get("pos", pos))
          {
            NodeHandle node = m_sector.get_navigation_graph().add_node(pos);
            std::string id;
            if (i->get("id", id))
            {
              id_to_node[id] = node;
            }
          }
        }
      }
    }

    FileReader edges_reader;
    if (navgraph_reader.get("edges", edges_reader))
    {
      const std::vector<FileReader>& edges_readers = edges_reader.get_sections();
      for(std::vector<FileReader>::const_iterator i = edges_readers.begin(); i != edges_readers.end(); ++i)
      {
        if (i->get_name() != "navgraph-edge")
        {
          std::cout << "SectorBuilder::parse_navgraph(): Unknown edges tag: " << i->get_name() << std::endl;
        }
        else
        {
          std::string lhs_node;
          std::string rhs_node;
          if (i->get("lhs-node", lhs_node) &&
              i->get("rhs-node", rhs_node))
          {
            std::map<std::string, NodeHandle>::iterator lhs = id_to_node.find(lhs_node);
            std::map<std::string, NodeHandle>::iterator rhs = id_to_node.find(rhs_node);
            if (lhs != id_to_node.end() &&
                rhs != id_to_node.end())
            {
              m_sector.get_navigation_graph().add_edge(lhs->second, rhs->second);
            }
          }
        }
      }
    }
  }
}