/*
 *  Copyright (C) 2008 Kristian Kristola
 *
 *  This program is distributed under the terms of the
 *  GNU General Public License.
 *
 *  This file is part of Dave the Ordinary Spaceman.
 *
 *  Dave the Ordinary Spaceman is free software: you can redistribute
 *  it and/or modify it under the terms of the GNU General Public
 *  License as published by the Free Software Foundation, either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  Dave the Ordinary Spaceman is distributed in the hope that it
 *  will be useful, but WITHOUT ANY WARRANTY; without even the
 *  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 *  PURPOSE.  See the GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Dave the Ordinary Spaceman. If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#include <SDL/SDL.h>
#include <assert.h>
#include <map>
#include <math.h>
#include <iostream>
#include <set>
#include <sstream>
#include "level.h"
#include "library.h"
#include "image.h"
#include "opengl.h"
#include "image.h"
#include "game.h"
#include "fellow.h"
#include "ability.h"
#include "model.h"

using namespace std;

Mix_Music *Level::current_music[LEVEL_COUNT], *Level::current_music_reversed[LEVEL_COUNT], *Level::intro[LEVEL_COUNT];

Mix_Chunk
  *Level::ch_lever,
  *Level::ch_lazor,
  *Level::ch_pink,
  *Level::ch_piuingg,
  *Level::ch_random,
  *Level::ch_tv_noise,
  *Level::ch_tilu,
  *Level::ch_charge,
  *Level::ch_pack;

Level::Level() : render_fellow_start_positions(false)
{
  init();
}

Level::Level(const string &name) : render_fellow_start_positions(false)
{
  init();
  load(name);
}

void Level::init()
{
  background[0] = 0.1f;
  background[1] = 0.1f;
  background[2] = 0.1f;
  background_scene = NULL;
  starfield = NULL;
  rocket = NULL;
  valley = NULL;
}

Level::~Level()
{
  for (list<Fellow *>::iterator i=fellows.begin(); i!=fellows.end(); ++i)
    delete *i;
}

Level::Layer::Layer() : blocks(NULL), used(false), opacity(1.0f)
{
}

Level::Layer::Layer(const Layer &l) : blocks(NULL), size(l.size), used(false), opacity(l.opacity)
{
  if (l.blocks == NULL)
    return;
  else {
    create(size);
    for (int x=0; x<size.w; x++)
      for (int y=0; y<size.h; y++)
	blocks[x][y] = l.blocks[x][y];
  }
}

Level::Layer::~Layer()
{
  clear();
}

void Level::Layer::clear()
{
  if (blocks) {
    for (int x=0; x<size.w; x++)
      delete [] blocks[x];
    delete [] blocks;
  }
  blocks = NULL;
}

void Level::Layer::create(const Level::Size &size)
{
  blocks = new Block *[size.w] ();
  Layer::size = size;
  for (int x=0; x<size.w; x++)
    blocks[x] = new Block [size.h] ();
}

void Level::Layer::format(int layer_num, Level *level)
{
  for   (int x=0; x<size.w; x++)
    for (int y=0; y<size.h; y++) {
      blocks[x][y].set_level(level);
      blocks[x][y].set_position(layer_num, Vector(x, y));
      blocks[x][y].set_type(Level::Block::Type::get_empty());
    }
}

void Level::Layer::augment(int left, int right, int bottom, int top, int layer_num, Level *level)
{
  //if (layer_num == 3)
  //  fprintf(stderr, "augment(%i, %i, %i, %i, %i, %p)\n", left, right, bottom, top, layer_num, level);
  Layer nlay(*this);
  /***************
  map<Level::Block *, Vector> positions;
  for   (int x=0; x<size.w; x++)
    for (int y=0; y<size.h; y++)
      positions[&blocks[x][y]] = Vector(x, y);
  ***************/
#if 0
  for   (int x=0; x<size.w; x++)
    for (int y=0; y<size.h; y++)
      assert(positions.count(blocks[x][y].get_parent()) == 1);
#endif
  assert(left + size.w + right > 0);
  assert(bottom + size.h + top > 0);
  clear();
  create(Size(left + size.w + right, bottom + size.h + top));
  format(layer_num, level);

  // Looping through the common area between both the old and the new layer and copying blocks from old to new
  //  x.  y = old coordinates
  // nx, ny = new coordinates
  /*
  if (layer_num == 3) {
    fprintf(stderr,
	    "*** for   (int x=%i; x<%i; x++)\n",
	    (left  >0 ? 0 : -left  ),
	    (right>0 ? nlay.size.w : nlay.size.w+right));
    fprintf(stderr,
	    "***   for (int y=%i; y<%i; y++) {\n",
	    (bottom>0 ? 0 : -bottom),
	    (top  >0 ? nlay.size.h : nlay.size.h+top  ));
  }
  for   (int x=(left  >0 ? 0 : -left  ); x<(right>0 ? nlay.size.w : nlay.size.w+right); x++)
    for (int y=(bottom>0 ? 0 : -bottom); y<(top  >0 ? nlay.size.h : nlay.size.h+top  ); y++) {
      const int nx =   left<0 ? x+left   : x;
      const int ny = bottom<0 ? y+bottom : y;
      assert(0 <= nx && nx < size.w);
      assert(0 <= ny && ny < size.h);
      assert(0 <= x && x < nlay.size.w);
      assert(0 <= y && y < nlay.size.h);
      blocks[nx][ny] = nlay.blocks[x][y];
      blocks[nx][ny].set_position(layer_num, Vector(nx, ny));
      if (blocks[nx][ny].get_type() == Level::Block::Type::get_obscured()) {
	const Vector &parentpos = positions[blocks[nx][ny].get_parent()];
	if (parentpos.x < 0 || parentpos.x >= size.w ||
	    parentpos.y < 0 || parentpos.y >= size.h)
	  blocks[nx][ny].set_type(Level::Block::Type::get_empty());  // Emptying the block if its parent was wiped out
      } else
	blocks[nx][ny].set_type(nlay.blocks[x][y].get_type());
    }

  for   (int x=0; x<size.w; x++)
    for (int y=0; y<size.h; y++) {
      blocks[x][y].set_level(level);
      blocks[x][y].set_position(layer_num, Vector(x, y));
      if (blocks[x][y].get_type() == NULL)
	blocks[x][y].set_type(Level::Block::Type::get_empty());
    }
  */

  // Updating positions
  /*
  for   (int nx=0; nx<size.w; nx++)
    for (int ny=0; ny<size.h; ny++) {
      blocks[nx][ny].set_position(layer_num, Vector(nx, ny));
    }
  */

  for   (int nx=0; nx<size.w; nx++)
    for (int ny=0; ny<size.h; ny++) {
      const int ox = nx - left;
      const int oy = ny - bottom;
      if (0 <= ox && ox < nlay.size.w &&
	  0 <= oy && oy < nlay.size.h) {
	if (nlay.blocks[ox][oy].get_type() == Level::Block::Type::get_obscured()) {
	  /*********************************
	  if (positions.count(blocks[nx][ny].get_parent()) == 1) {
	    const Vector &parentpos = positions[blocks[nx][ny].get_parent()];
	    if (parentpos.x < 0 || parentpos.x >= size.w ||
		parentpos.y < 0 || parentpos.y >= size.h)
	      blocks[nx][ny].set_type(Level::Block::Type::get_empty());  // Emptying the block if its parent was wiped out
	  }
	  *********************************/
	} else {
	  blocks[nx][ny].set_type(nlay.blocks[ox][oy].get_type());
	}
      } else
	blocks[nx][ny].set_type(Level::Block::Type::get_empty());
    }

  /*
  list<vector<Block> > cool;
  for (int x=0; x<size.w; x++) {
    cool.push_back(vector<Block>());
    for (int y=0; y<size.h; y++)
      cool.back().push_back(blocks[x][y]);
  }
  for (; right>0; right--) {
    cool.push_back(cool.back());
    for (int y=0; y<size.h; y++) {
      cool.back()[y].set_position(cool.back()[y].get_layer(), cool.back()[y].get_position() + Vector(1, 0));
      cool.back()[y].set_type(Block::Type::get_empty());
    }
  for (; left>0; left--) {
    cool.push_front(cool.front());

  }
  for (; top>0; top--) {
  }
  for (; bottom>0; bottom--) {
  }
  for (; left<0; left++)
    cool.pop_front();
  for (; right<0; right++)
    cool.pop_back();
  for (; bottom<0; bottom++)
    for (list<vector<Block> >::iterator i=cool.begin(); i!=cool.end(); ++i)
      i->pop_front();
  for (; top<0; top++)
    for (list<vector<Block> >::iterator i=cool.begin(); i!=cool.end(); ++i)
      i->pop_back();
  */
}

void Level::load(const string &name)
{
  char *cmap = NULL;

  if (name == "liipantönkkä") {
    cmap =
      ".........................................................xx.....................................................................................x..\0"
      "........................................................xx.....................................................................................x...."
      ".......................................................xx.....................................................................................x....."
      "......................................................xx.....................................................................................x......"
      ".....................................................xx.....................................................................................x......."
      "...........................................................................................................................................x........"
      ".......................................................................xxx....xxxx...x...x................................................x........."
      "..........xxxxx...................................xx..................x......x....x..x...x...............................................x.........."
      "..........x.......................xxxxxxxxx......xxxx.................x..x...xxxxxx...xxx...............................................x..........."
      "..........x.......................x.............xx..xx................x...x..x....x....x...............................................x............"
      "..........xxxxx.......xxxxx.......x............xx..xxxx................xxx...x....x....x..............................................x............."
      "........xxx...............xxx.....x...........xx......xx.............................................................................x.............."
      ".........xx...............xx.................xxxxx.....xx...........................................................................x..............."
      "..........xxxxxxxxxxx....xx.................xx..........xx.....................x..x.........x..x...................................x................"
      "...........xxxxxxxxxx....x.................xx............xx.......................................................................x....x............"
      ".............xxxxxxxx.....xxxxxxxxxxxxxxxxxx.......xxxxxxxxx.........xxx....x.xxxxxx.x..xx.xxxxxx................................x.....x............"
      "................xxxxx......................................xx........x.xx...x.x....x.x.xx..x....x...............................x....x.x............"
      "....................x.......................................xx.......x..xx..x.xxxxxx.xxx...xxxxxx..............................x.....x.............."
      "....................x..........................xx............xx......x...xx.x.x....x.x.xx..x....x.............................x......x.............."
      "....................x.........................................xx.....x....xxx.x....x.x..xx.x....x............................x.......x.x............"
      "....................x..........................................xx...........................................................x........x.xxxxxxxxxxxxx"
      "....................x......................xx...................xx.........................................................x.........x.x............"
      "....................x............................................xx.......................................................x..........x.x............"
      "....................x.................xx..........................xx...............xxxxxxxxxxxx..........................x...........x.x............"
      "....................x..............................................xx...................................................x............x.x............"
      "....................x..................................................................................................x............................"
      "....................x.................xx..xx........xxxxxxxx................xxxxxxxxxxxx..............................x............................."
      "....................x..........................xxx......x............................................................x...............x.x............"
      "....................x..........xx...xx............xxxxxx..x.........................................................x................x.x............"
      ".....x..x...........x.........x..x.x..x.......................xxxxxxx.....xxxxxxxxxxxx.............................x.................x.x............"
      "....................x........x....x....x......................................................x...................x..................x.x............"
      ".......x............x.........x.......x..........................................................................x...................x.x............"
      "....................x..........x.....x.............................................................x.x..........xxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxx"
      ".........x..........x............x.x..............................................................x.x..........xxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxx"
      "....................x....................xxxxxxxxxxxxxxxxx.........................................x.x........xxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxx"
      "x..........x........xxxxxxxxxxxxxx.xxxxxxx...............xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.x........xxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxx"
      ".................................x..........................................................................xxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxx"
      ".............x...................x.........xxxxxxxxxxxx....................................................xxxxxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxx"
      "..............x..................xxxxxxxxxxx.....................xxxx.xx.x.x.x.x.x.xxxxxxxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxx..........xxxxx"
      "...............x...............................................xxxxxx.xx.x.x.x.x.x.xxxxxxxxxxxxx...................................................."
      "................x............................................xxxxxxxx.xx.x.x.x.x.x.xxxxxxxxxxxx....................................................."
      ".................x.........................................xxxxxxxxxx.xx.x.x.x.x.x.xxxxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxx"
      "..................x.................xxxx......xxxx......xxx...xxxxxxx.xx.x.x.x.x.x.xxxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxx"
      "..................x.................x........xxxxx.....xx......xxxxxx.xx.x.x.x.x.x.xxxxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxx"
      "..................x.................x........xxxxxx...xx........xxxxx.xx.x.x.x.x.x.xxxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxx"
      "..................x.................x.........xxxxxx.xx..........xxxx.xx.x.x.x.x.x.xxxxxxx.........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxx"
      "..................x.................xxxxxx.....xxxxx.x............xxx.xx.x.x.x.x.x.xxxxxxx........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxx"
      "..................x.................x...........xxxx.x.............xx.xx.x.x.x.x.x.xxxxxx........xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxx"
      "..................x.................x............xxx.x..........................................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxx"
      "..................x.................x.............xx.x.........................................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxx"
      "..................x.................xxxxxxxxxxx....x.x............xxx.xxxx....................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...xxxxxxxxxxxxxxxxxxxx"
      "..................x................................x.x............xxx.xxxx...................xxxxxxx...........................xxxxxxxxxxxxxxxxxxxxx"
      "..................x................................x.x......................................xxxxxxxx..........................xxxxxxxxxxxxxxxxxxxxxx"
      "..................x................................x.x.....................................xxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      "..................x................................x.x....................x...............xxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      "..................xxxxxxxxxxxx........xxxxxxxxxxxxxx.xxxxxxxxxxxx.........xxxxx..........xxxxxxxxx...xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      "..........................................................................xxxxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      "...................................................................................................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      "...................................................................................................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      ".....................................................................................xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      "x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
  }

  assert(cmap);
  char *p = cmap;

  size.w++;
  while (*p++)  // Lasketaan kartan leveys
    size.w++;

  p = cmap;      // Lasketaan kartan korkeus
  while (*p) {
    size.h++;
    p += size.w;
  }

  assert(size.w < 20000);
  assert(size.h < 20000);

  char *cmap2 = new char [size.w * size.h];
  memcpy(cmap2, cmap, size.w * size.h * sizeof(char));
  cmap = cmap2;

  cmap[size.w - 1] = cmap[size.w - 2];  // Paikataan \0-markkerin kolo

  size.w *= 2;
  size.h *= 2;

  // Varataan muistia layereille ja niiden blokeille
  for (int i=0; i<10; i++) {
    layers.push_back(Layer());
    layers[i].create(size);
  }
  blocks = layers[3].blocks;

  fellow_start_positions.create(size);
  item_layer.create(size);

  Level::Block::Type::insert("empty");
  Level::Block::Type::insert("16_coll_ice");
  Level::Block::Type *const empty = Level::Block::Type::get("empty");
  Level::Block::Type *const ice   = Level::Block::Type::get("16_coll_ice");
  assert(empty);
  assert(ice);

  // Alustetaan blokit
  for (int l=-2; l<int(layers.size()); l++)
    for   (int x=0; x<size.w; x++)
      for (int y=0; y<size.h; y++) {
        Block *const b = get_block(l, x, y);
        b->set_position(l, Vector(x, y));
        b->set_level(this);
        b->set_type(Level::Block::Type::get_empty());
      }

  // Laitetaan oikiat blokit
  for   (int x=0; x<size.w/2; x++)
    for (int y=0; y<size.h/2; y++) {
      const int real_x = 2*x;
      const int real_y = 2*y;
      const int lev_y = size.h - real_y - 2;
      Block *const b = &blocks[real_x][lev_y];
      switch (cmap[y * (size.w/2) + x]) {
      case '.': b->set_type(empty);     break;
      case 'x': b->set_type(ice);       break;
      default: assert(!"Muttei ol");
      }
    }

  delete [] cmap;
}

void Level::render() const
{
  //HUOM! Tätä pitäis optimoida siten että bindataan yks blokki, rendataan kaikki
  //      sen tyyppiset, bindataan seuraava, rendataan kaikki sen tyyppiset jne.

  if (game->is_editor_shown())
    glClearColor(0.5, 0.5, 0.5, 0.0);
  else {
    glClearColor(background[0],
		 background[1],
		 background[2],
		 0);
  }
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  render_background_effect();

  if (game->is_editor_shown()) {
    glDisable(GL_TEXTURE_2D);
    glBegin(GL_QUADS);
    glColor4f(background[0], background[1], background[2], 1.0f);
    glVertex2f(0.0f, 0.0f);
    glVertex2f(8.0f * size.w, 0.0f);
    glVertex2f(8.0f * size.w, 8.0f * size.h);
    glVertex2f(0.0f, 8.0f * size.h);
    glEnd();
  }

  glDisable(GL_DEPTH_TEST);

  extern Game *game;
  const Vector &centre = game->get_scroll();
  const int centre_x = int(centre.x);
  const int centre_y = int(centre.y);

  const int r = int(game->get_capacity() / 2);
  for (int l = layers.size() - 1; l >= (render_fellow_start_positions ? FELLOW_STP_LAYER : ITEM_LAYER); l--) {
    int left   = centre_x - int(r*game->get_aspect_ratio()) - 8;
    int right  = centre_x + int(r*game->get_aspect_ratio()) + 2;
    int bottom = centre_y - r - 8;
    int top    = centre_y + r + 2;
    const Layer *lay = l >= 0 ? &layers[l] :
      l == ITEM_LAYER         ? &item_layer :
      l == FELLOW_STP_LAYER   ? &fellow_start_positions : NULL;
    assert(lay);
    if (l == 8) {  // Layer 8 is a special one for very big blocks
      left = 0;
      bottom = 0;
      right = lay->size.w;
      top = lay->size.h;
    }
    const int step = (l==8) ? 128 : 1;
    for   (int x=left;   x<right; x+=step)
      for (int y=bottom; y<top;   y+=step) {
        //if (x + int(layers[l].blocks[x][y].get_size().x) <  left)   continue;
        //if (x                                            >= right)  goto layer_done;
        //if (y + int(layers[l].blocks[x][y].get_size().y) <  bottom) continue;
        //if (y                                            >= top)    break;
        if (x<0 || y<0 || x>=lay->size.w || y>=lay->size.h)
          continue;

	const Level::Block::Type *const ty = lay->blocks[x][y].get_type();
        if (ty->obscured || ty->empty || (ty->invisible && !game->is_editor_shown()))
          continue;

	if (l==-2) {
	  int a;
	  a=5;
	}
	glColor4f(1.0f, 1.0f, 1.0f, lay->opacity);
	lay->blocks[x][y].set_flags(Block::NOT_TRANSFORMED, true);
        lay->blocks[x][y].render();
      }
//layer_done:
      if (l == 3) {
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
        for (list<Fellow *>::const_reverse_iterator i = fellows.rbegin(); i != fellows.rend(); ++i)
	  if (!render_fellow_start_positions || *i == game->get_dave())
	    (*i)->render();
      }
  }

  for (list<Block *>::const_iterator i = items.begin(); i != items.end(); ++i)
    (*i)->render();

  glDisable(GL_BLEND);
}

void Level::render_background_effect() const
{
  const int lev = game->get_current_level_number();
  if (lev == 0) {  // Practice
  }
  else if (lev == 1) {  // Space
    if (starfield == NULL) {
      background_scene = new Scene();
      starfield = new Starfield[9]();
      for (int i=0; i<9; i++) {
        starfield[i].load();
        starfield[i].blend = 1;
        starfield[i].set_texture(lib["star.png"]);
        background_scene->add_model(&starfield[i]);
        if (!i)
          starfield[i].load_shader("graphics/starfield_vert.glsl", "graphics/starfield_frag.glsl");
        else
          starfield[i].use_same_shader_as(&starfield[0]);
      }
    }
    const Vector &d = game->get_scroll();
    for (int i=0; i<9; i++) {
      const float dx = float((i % 3) - 1) * 200.0f;
      const float dy = float((i / 3) - 1) * 200.0f;
      starfield[i].set_position(dx + 350.0f-d.x - 150.0f, dy + 100.0f-d.y, 0.0f + 40.0f * background[2]);
    }
    background_scene->set_camera_pos(0.0f, 0.0f, 0.0f);
    background_scene->render();
  }
  else if (lev == 2) {  // Grass
    if (rocket == NULL) {
      background_scene = new Scene();
      rocket = new Model("graphics/roket.3ds");
      background_scene->add_model(rocket);
      valley = new Model("graphics/laakso.3ds");
      valley->set_texture(lib["laakso.png"]);
      background_scene->add_model(valley);
      sun = new Model("graphics/aurinko.3ds");
      sun->set_texture(lib["aurinko.png"]);
      background_scene->add_model(sun);
      gaussian = new GaussianBlur();
      gaussian->amount = 0.4f;
    }
    rocket->set_position(0.0f, -3.75f, 0.0f);
    rocket->set_scale(4.0f, 4.0f, 4.0f);
    rocket->set_rotation(105.0f, 0.0f, -game->get_dave()->get_position().x * 0.5f);
    valley->set_position(0.0f, -20.0f, 0.0f);
    valley->set_scale(50,50,50);
    valley->set_rotation(10.0f, game->get_dave()->get_position().x * 0.5f, 0.0f);
    sun->set_position(12.0f, 9.0f, -5.0f);  // 10.0f, 7.0f, -5.0f
    sun->set_rotation(90.0f, 0.0035f * SDL_GetTicks(), 0.0f);
    sun->set_scale(10.0f, 10.0f, 10.0f);
    sun->blend = 2;
    sun->lighting = false;
    background_scene->set_background(0xA9, 0xC9, 0xF7);
    background_scene->set_camera_pos(0.0f, 0.0f, 10.0f);
    background_scene->render();
    // Sun
    // Beams moi kristian!
    // Dust
    //
    fbo_chain_end();
    gaussian->amount = 0.69f;
    gaussian->render();
    fbo_chain_begin();
  }
  else if (lev == 3) {  // VCR
  }
}

void Level::begin_physics_sequence()
{
/*for (int x=0; x<size.w; x++)
    for (int y=0; y<size.h; y++)
      if (blocks[x][y].get_type() == Block::COLLISION)
        blocks[x][y].set_type(Block::HOERPELOE);
        */
  for (list<Fellow *>::iterator i = fellows.begin(); i != fellows.end(); ++i)
    (*i)->begin_physics_sequence();
}

void Level::handle(float dt, float time)
{
  for (list<Block *>::iterator i = items.begin(); i != items.end(); ++i)
    dynamic_cast<Ability::Ammo *>(*i)->handle(dt, time);
  for (list<Fellow *>::iterator i = fellows.begin(); i != fellows.end(); ++i)
    (*i)->handle(this, dt, time);

  if (current_music[0] == NULL) {
    current_music         [0] = Mix_LoadMUS("audio/thyks-video_cleaner_fluid.ogg");
    current_music_reversed[0] = Mix_LoadMUS("audio/thyks-video_cleaner_fluid_reverse.ogg");
    intro                 [0] = NULL;
    current_music         [1] = current_music[0];
    current_music_reversed[1] = current_music_reversed[0];
    intro                 [1] = NULL;
    current_music         [2] = Mix_LoadMUS("audio/sid000loop.ogg");
    current_music_reversed[2] = Mix_LoadMUS("audio/sid000loop_reverse.ogg");
    intro                 [2] = Mix_LoadMUS("audio/sid000intro.ogg");
    current_music         [3] = Mix_LoadMUS("audio/sid002loop.ogg");
    current_music_reversed[3] = Mix_LoadMUS("audio/sid002loop_reverse.ogg");
    intro                 [3] = Mix_LoadMUS("audio/sid002intro.ogg");

    ch_lever    = Mix_LoadWAV("audio/avaruusvipu.wav");
    ch_lazor    = Mix_LoadWAV("audio/lazor.wav");
    ch_pink     = Mix_LoadWAV("audio/pink_noise_10s.wav");
    ch_piuingg  = Mix_LoadWAV("audio/piuingg.wav");
    ch_random   = Mix_LoadWAV("audio/random.wav");
    ch_tv_noise = Mix_LoadWAV("audio/tv_noise_10s.wav");
    ch_tilu     = Mix_LoadWAV("audio/tilulilu.wav");
    ch_charge   = Mix_LoadWAV("audio/charge.wav");
    ch_pack     = Mix_LoadWAV("audio/pack.wav");

    play_music(false);
    Mix_VolumeMusic(SDL_MIX_MAXVOLUME);
  }

  while (!to_remove.empty()) {
    const list<Block *>::iterator i = to_remove.begin();
    remove_really(*i);
    to_remove.erase(i);
  }

  if (game->get_dave()->get_active_ability())
    if (game->get_dave()->get_active_ability()->get_type() == Ability::ROCKET_PACK ||
        game->get_dave()->get_active_ability()->get_type() == Ability::SLING)
      game->get_dave()->use(time);
}

// Lisää kenttään uuden heebon
Fellow *Level::add_fellow(Fellow *f)
{
  fellows.push_back(f);
  return f;
}

const unsigned int Level::Block::Type::CODE = 1;

Level::Block::Type::Type(const string &name, const Family f, const unsigned int flags)
 : family(f),
   collidable(false),
   empty(true),
   obscured(false),
   end(false),
   big(false),
   invisible(0),
   image(NULL),
   image_count(0),
   animation_delay(1.0f),
   geometry(RECTANGLE),
   orientation(0),
   animations_owner(NULL)
{
  Type::name = name;
  similar_group.push_back(this);

  // Using a finite state automaton to parse the size of the block type
  string size1, size2;
  int state = 0;
  const string &t = name;
  for (size_t i=0; i<t.size(); i++) {
    switch (state) {
    case 0:
      if (isdigit(t[i]))
        size1 += t[i];
      else if (t[i] == '_')
        state = 1;
      else
        state = 2;
      break;
    case 1:
      if (isdigit(t[i]))
        size2 += t[i];
      else
        state = 2;
      break;
    case 2:
      i = t.size();
      break;
    }
  }
  size.x = float(atoi(size1.c_str())) / 8.0f;
  size.y = float(atoi(size2.c_str())) / 8.0f;
  if (size.x < 0.4f)
    size.x = 2.0f;
  if (size.y < 0.4f)
    size.y = size.x;

  string beginning;
  switch (family) {
  case STATIC:       beginning = "block_";  break;
  case DYNAMIC:      beginning = "fellow_"; break;
  case STATIC_COAT:  beginning = "item_";   break;
  case DYNAMIC_COAT: beginning = "ammo_";   break;
  }

  if (~flags & CODE) {
    image = lib[beginning + name + ".png"];
    if (image == NULL)
      throw 0;
    image->nearest();
  }

  const string real = get_real_name();
  if (real.size() >= 5)
    if (real.substr(0, 5) == "coll_")
      collidable = true;
  if (real.size() >= 14)
    if (real.substr(0, 11) == "coll_slope_") {
      const int angle = atoi(real.substr(11, 3).c_str());
      assert(angle == 45 || angle == 135);
      geometry = R_TRIANGLE;
      switch (angle) {
      case 45:  orientation = 1; break;
      case 135: orientation = 0; break;
      }
    }
  if (real == "empty")
    empty = true;
  else
    empty = false;
  end = real.find("trafficlight") != string::npos;
  big = real.find("BIG_") != string::npos;
  if (real == "obscured")
    obscured = true;
  if (real.find("_invisible") != string::npos)
    invisible = 1;
  if (real.find("_invisible2") != string::npos)
    invisible = 2;

  // Removing the number at the end of the name to see if there are more variations of this block
  const string &without_number = remove_number();
  if (without_number != name) {
    insert(without_number);
    get(without_number)->similar_group.push_back(this);
  }

  // Finding out animation number, frame number, and animation speed
  int anim_number = 0;
  int frame_number = 0;
  animation_speed = 1.0f;
  string::size_type a = name.find("_anim");
  if (a != string::npos) {
    assert(name.size() > a + 6);
    char an = name[a + 5];
    assert('1' <= an && an <= '9');
    anim_number = an - '1';

    a += 7;  // Go to the beginning of frame number
    assert(name[a - 1] == '_');
    string frame;
    while (a < name.size() && isdigit(name[a]))
      frame += name[a++];
    assert(0 < frame.size() && frame.size() < 4);
    frame_number = atoi(frame.c_str()) - 1;

    assert(a < name.size());
    assert(name[a] == '_');  // There has to be '_' after frame number
    a++;  // Go to the beginning of speed
    string speed;
    while (a < name.size() && (isdigit(name[a]) || name[a] == '.'))
      speed += name[a++];
    if (a >= name.size())  // There has to be '_' after the speed
      speed = "";
    else if (name[a] != '_')
      speed = "";
    if (speed.size())
      animation_speed = atof(speed.c_str());
  }

  cerr << "name=" << name << ", anim=" << anim_number << ", frame=" << frame_number << ", speed=" << animation_speed << endl;

  if (anim_number == 0 && frame_number == 0) {
    insert_frame(0, 0, image);
  } else {
    const string &without_info = remove_anim_info();
    insert(without_info, family, flags);
    Type *w = get(without_info);
    w->insert_frame(anim_number, frame_number, image);
    if (animation_speed != 1.0f)
      w->animation_speed = animation_speed;
  }
}

void Level::Block::Type::insert_frame(int animation, int frame, Image *image)
{
  assert(0 <= animation && animation <= 999);
  assert(0 <=     frame && frame     <= 999);
  while (animation >= (int)animations.size())
    animations.push_back(vector<Image *>());
  while (frame >= (int)animations[animation].size())
    animations[animation].push_back(NULL);
  animations[animation][frame] = image;
}

string Level::Block::Type::remove_number() const
{
  string without_number = name;
  while (without_number.size() && isdigit(*without_number.rbegin()))
    without_number.erase(without_number.size() - 1, 1);
  return without_number;
}

string Level::Block::Type::remove_anim_info() const
{
  int state = 0;
  int beg = -1, end = -1;
  for (size_t i=0; i<name.size(); i++) {
    char c = name[i];
    switch (state) {
    case  0: beg = i;
             if (c == '_')   state = 1; else state = 0; break;
    case  1: if (c == 'a')   state = 2; else state = 0; break;
    case  2: if (c == 'n')   state = 3; else state = 0; break;
    case  3: if (c == 'i')   state = 4; else state = 0; break;
    case  4: if (c == 'm')   state = 5; else state = 0; break;
    case  5: if (isdigit(c)) state = 6; else state = 0; break;
    case  6: if (c == '_')   state = 7; else state = 0; break;
    case  7: if (isdigit(c)) state = 8; else state = 0; break;
    case  8: if (isdigit(c)) state = 8; else if (c == '_') {state = 9; end = i;} else state = 0; break;
    case  9: if (isdigit(c) || c == '.') state = 9; else if (c == '_') {state = 10; end = i;} else state = 0; break;
    case 10: i = name.size(); break;
    case 11: break;
    case 12: break;
    case 13: break;
    case 14: break;
    case 15: break;
    default: assert(!"Wamma"); break;
    }
  }
  if (beg > -1 && end > -1) {
    end++;
    string result = name;
    cerr << "remove_anim_info(): \"" << result << "\" --> \"";
    result.replace(beg, end - beg, "");
    cerr << result << "\"\n";
    return result;
  }
  return name;
}

bool Level::Block::Type::good() const
{
  return name.find("_anim") == string::npos;
}

const vector<Level::Block::Type *> *Level::Block::Type::get_similar_group() const
{
  assert(get(remove_number()));
  return &get(remove_number())->similar_group;
}

int Level::Block::Type::get_animation_frame_count(int animation) const
{
  assert(animations_owner == NULL);
  assert(animation < int(animations.size()));
  return (int)animations[animation].size();
}

float Level::Block::Type::get_animation_speed(int animation) const
{
  assert(animations_owner == NULL);
  return animation_speed;
}

const Image *Level::Block::Type::get_animation_frame(int animation, int frame) const
{
  assert(animations_owner == NULL);
  assert(animation < int(animations.size()));
  assert(frame < int(animations[animation].size()));
  return animations[animation][frame];
}

Level::Block::Type::~Type()
{
}

void Level::Block::Type::insert(const string &name, const Level::Block::Type::Family family, const unsigned int flags)
{
  try {
  if (pool.count(name) == 0)
    pool[name] = new Type(name, family, flags);
  } catch (...) {
  }
}

void Level::Block::Type::insert(Type *const t)
{
  assert(t);
  if (pool.count(t->name) == 0)
    pool[t->name] = t;
}

Level::Block::Type *Level::Block::Type::get(const string &name)
{
  if (pool.count(name))
    return pool[name];
  else
    {cerr << "Getattiin EPÃonnistuneesti: " << name << endl; return NULL;}
}

map<string, Level::Block::Type *> Level::Block::Type::pool;
Level::Block::Type *Level::Block::Type::pool_obscured;
Level::Block::Type *Level::Block::Type::pool_empty;

Level::Block::Type *Level::Block::Type::get_obscured()
{
  return pool_obscured;
}

Level::Block::Type *Level::Block::Type::get_empty()
{
  return pool_empty;
}

void Level::Block::Type::initialize()
{
  insert(pool_obscured = new Type("8_obscured"));
  insert(pool_empty    = new Type("8_empty"));
}

const int Level::Block::MIRRORED        = 1;
const int Level::Block::NOT_TRANSFORMED = 2;
const int Level::Block::MONOCHROME      = 4;

Level::Block::Block()
 : program(0),
   type(NULL),
   alive(ALIVE),
   credit(NULL),
   level(NULL),
   parent(NULL),
   orientation(0),
   current_animation(0),
   flags(0),
   credit_rendered_at(0.0f)
{
}

Level::Block::Block(Type *const t)
 : program(0),
   type(NULL),
   alive(ALIVE),
   credit(NULL),
   level(NULL),
   parent(NULL),
   orientation(0),
   current_animation(0),
   flags(0),
   credit_rendered_at(0.0f)
{
  set_type(t);
}

Level::Block::Block(const Level::Block &x)
 : program(x.program),
   position(x.position),
   type(x.type),
   alive(x.alive),
   died_at(x.died_at),
   credit(NULL),
   level(x.level),
   layer(x.layer),
   parent(x.parent),
   orientation(0),
   current_animation(0),
   flags(0),
   credit_rendered_at(0.0f)
{
}

Level::Block &Level::Block::operator=(const Level::Block &x)
{
  program = x.program;
  position = x.position;
  type = x.type;
  level = x.level;
  layer = x.layer;
  parent = x.parent;
  orientation = 0;
  current_animation = 0;
  flags = 0;
  return *this;
}

void Level::Block::set_position(const int l, const Vector &p)
{
  layer = l;
  position = p;
}

void Level::Block::set_type(Level::Block::Type *const t)
{
  assert(t);
  if (level) {
    // Jos naapuriblokkien jyräykseen ei ole tilaa tarpeeksi niin
    // ei vaihdeta koko tyyppiä ollenkaan
    const int px = (int)position.x;
    const int py = (int)position.y;
    assert(t);
    if (px + int(t->size.x) > level->get_size_x(layer) || py + int(t->size.y) > level->get_size_y(layer))
      return;

    // Jyrätään naapuriblokit
    for (  int y=py; y<py+(int)t->size.y; y++)
      for (int x=px; x<px+(int)t->size.x; x++)
        if (x != px || y != py)
          level->get_block(layer, x, y)->set_parent(this);

    parent = this;
    // if (t->size.y * t->size.x > 1) cerr << "jyrätty " << t->size.y * t->size.x - 1 << " kpl" << endl;
  }

  type = t;
  orientation = type->orientation;
}

Vector Level::Block::get_size() const
{
  assert(type);
  return type->size;
}

Level::Block::~Block()
{
}

void Level::Block::bind_image() const
{
  assert(type);
  assert(type->image);
  int ca = current_animation;
  int ac = type->get_animation_count();
  if (ca >= ac)
    ca = ac - 1;
  float game_animation_speed = game->get_animation_speed();
  if (type == game->get_dave()->get_type() && game_animation_speed == 0.0f)
    game_animation_speed = 1.0f;
  int phase = (unsigned int)
              (type->get_animation_speed(ca) * 0.001f * SDL_GetTicks() * game_animation_speed)
              % type->get_animation_frame_count(ca);
  type->get_animation_frame(ca, phase)->bind();
}

void Level::Block::render_credit() const
{
  if (alive == DYING) {
    if (death_animation_state() == 1.0f) {
      alive = DEAD;
    }
  }
  if (credit) {
    const float r = ((~flags) & NOT_TRANSFORMED) ? 0.0f : 1.0f;  // 1 = blokki, 0 = fellowi
    //credit->set_color(1.0f, 0.4f, 0.2f + 0.8f * death_animation_state(), 2.0f * (1.0f - death_animation_state()));
    credit->set_color(2.0f * (1.0f - death_animation_state()),
                      1.0f - death_animation_state(),
                      0.3f * death_animation_state(),
                      2.0f * (1.0f - death_animation_state()));
    credit->set_text(credit->get_text());
    credit->set_position(Vector(0.0f, 30.0f + 50.0f * death_animation_state()) + r * 8.0f * position);
    credit->set_scale(1.5f, 1.5f);
    credit->render(false);
    glColor4f(1,1,1,1);
    credit_rendered_at = game->get_physics_time();
  }
}

void Level::Block::render() const
{
  // Optimoi kentän piirto niin että yks blokkityyppi on yhtä glbegin..endiä
  if (alive == DEAD)
    return;

  if (credit_rendered_at < game->get_physics_time())
    render_credit();

  bind_image();
  if ((flags & MONOCHROME) && game->get_ext()->shaders) {
    game->kuopio_prog->use();
    game->kuopio_prog->set_int("dave", 0);
  }

  //glPushMatrix();
  //static float asdf;
  //asdf += 0.005;
  //glTranslatef(-8.*-position.x-(-8.)*size.x/2., -8.*-position.y-(-8.)*size.y/2., 0);
  //glRotatef(asdf,0,0,1);
  //glTranslatef(-8.*position.x+(-8.)*size.x/2., -8.*position.y+(-8.)*size.y/2., 0);
  glBegin(GL_QUADS);
  //glBegin(GL_TRIANGLES);
  //glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  r_render();
  glEnd();
  //glPopMatrix();

  if ((flags & MONOCHROME) && game->get_ext()->shaders)
    game->kuopio_prog->unuse();
}

void Level::Block::render_frame() const
{
  glDisable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  //glBegin(GL_LINE_LOOP);
  glBegin(GL_QUADS);
  r_render();
  glEnd();
}

void Level::Block::r_render() const
{
  const float s = 8.0f;
  const int xi = ~flags & MIRRORED;
  const int yi = 0;
  assert(type);
  float r = 1.0f;
  if (/*type->family == Type::DYNAMIC &&*/
      ~flags & NOT_TRANSFORMED)
    r = 0.0f;
  float l = death_animation_state();
  //glColor4f(1.0f, 1.0f, 1.0f, 1.0f - l);
  if (l > 0.4f)
    return;
  l *= 12.0f;
  l *= l;
#if 1
  const bool p1 = game->rendering_editor;
  const float fsx = type->size.x * (type->big && !p1 ? 64.0f : 1.0f);
  const float fsy = type->size.y * (type->big && !p1 ? 64.0f : 1.0f);
  glTexCoord2f(xi?0.0f:1.0f, yi?0.0f:1.0f); glVertex2f(s*(r*position.x) + 0.0f -l, s*(r*position.y) + 0.0f -l);
  glTexCoord2f(xi?1.0f:0.0f, yi?0.0f:1.0f); glVertex2f(s*(r*position.x) + fsx*s+l, s*(r*position.y) + 0.0f -l);
  glTexCoord2f(xi?1.0f:0.0f, yi?1.0f:0.0f); glVertex2f(s*(r*position.x) + fsx*s+l, s*(r*position.y) + fsy*s+l);
  glTexCoord2f(xi?0.0f:1.0f, yi?1.0f:0.0f); glVertex2f(s*(r*position.x) + 0.0f -l, s*(r*position.y) + fsy*s+l);
  //glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
#else
  const float near = -1.3f, far = -1.5f;
  glTexCoord2f(xi?0.0f:1.0f, yi?0.0f:1.0f); glVertex3f(s*(r*position.x) + 0.0f,             s*(r*position.y) + 0.0f,             far);
  glTexCoord2f(xi?1.0f:0.0f, yi?0.0f:1.0f); glVertex3f(s*(r*position.x) + type->size.x*s+l, s*(r*position.y) + 0.0f,             far);
  glTexCoord2f(0.5f, 0.5f); glVertex3f(s*(r*position.x) + (type->size.x*s+l)*0.5f, s*(r*position.y) + (type->size.y*s+l) * 0.5f, near);
  glTexCoord2f(xi?1.0f:0.0f, yi?0.0f:1.0f); glVertex3f(s*(r*position.x) + type->size.x*s+l, s*(r*position.y) + 0.0f,             far);
  glTexCoord2f(xi?1.0f:0.0f, yi?1.0f:0.0f); glVertex3f(s*(r*position.x) + type->size.x*s+l, s*(r*position.y) + type->size.y*s+l, far);
  glTexCoord2f(0.5f, 0.5f); glVertex3f(s*(r*position.x) + (type->size.x*s+l)*0.5f, s*(r*position.y) + (type->size.y*s+l) * 0.5f, near);
  glTexCoord2f(xi?1.0f:0.0f, yi?1.0f:0.0f); glVertex3f(s*(r*position.x) + type->size.x*s+l, s*(r*position.y) + type->size.y*s+l, far);
  glTexCoord2f(xi?0.0f:1.0f, yi?1.0f:0.0f); glVertex3f(s*(r*position.x) + 0.0f,             s*(r*position.y) + type->size.y*s+l, far);
  glTexCoord2f(0.5f, 0.5f); glVertex3f(s*(r*position.x) + (type->size.x*s+l)*0.5f, s*(r*position.y) + (type->size.y*s+l) * 0.5f, near);
  glTexCoord2f(xi?0.0f:1.0f, yi?1.0f:0.0f); glVertex3f(s*(r*position.x) + 0.0f,             s*(r*position.y) + type->size.y*s+l, far);
  glTexCoord2f(xi?0.0f:1.0f, yi?0.0f:1.0f); glVertex3f(s*(r*position.x) + 0.0f,             s*(r*position.y) + 0.0f,             far);
  glTexCoord2f(0.5f, 0.5f); glVertex3f(s*(r*position.x) + (type->size.x*s+l)*0.5f, s*(r*position.y) + (type->size.y*s+l) * 0.5f, near);
#endif
}

Image *Level::Block::get_image() const
{
  assert(!"Poista mut");
  return NULL;
}

// Tyrmää pos:n kohdalla olevan 8x16-blokin kenttään
// ja palauttaa törmäyskohdan ja törmäyspinnan normaalivektorin. Muttei palauta.
/*

        +--+  <-- fellow
        |  |
        |  |              X = pos = törmäävän kappaleen paikka
        |  |    ^         s = second = pinnan normaali
        |  |    |s        p = first = piste jossa kappaleen täytyy olla törmäyksen jälkeen paitsi että tätä ei käytetä enää
        |  |    |
  +-----p  |--------+
  |     X--+        |
  |                 |
  |                 | <-- block
  +-----------------+

 */

// Laskee listan point:ia lähinnä olevista blokeista
const multimap<Level::BlockInfo, Level::Block *, Level::BlockInfo::comp>
  Level::col_get_nearest_blocks(const Vector &point,
				const Vector *fel_pos,
				const Vector *fel_size,
				const Vector *velocity,
				const Block *exception) const
{
  multimap<BlockInfo, Block *, BlockInfo::comp> nearest_blocks;
  const int rx = fel_size ? int(fel_size->x) : 1;
  const int ry = fel_size ? int(fel_size->y) : 2;
  for   (int dx = -10; dx <= rx + 10; dx++)
    for (int dy = -10; dy <= ry + 10; dy++) {
      // Lasketaan yhden fellowin naapuriblokin paikka
      const int x = int(point.x) + dx;
      const int y = int(point.y) + dy;
      if (x < 0 || y < 0 ||
	  x >= size.w || y >= size.h)
	continue;
      Block *const block = &blocks[x][y];
      if (!block->get_type()->collidable)
	continue;
      const Vector block_centre(float(x) + block->get_type()->size.x / 2.0f,
                                float(y) + block->get_type()->size.y / 2.0f);
      const Vector difference = point - block_centre;
      float common_area = 0.0f;
      if (fel_pos && fel_size)
	common_area = block->col_get_intersecting_area(*fel_pos, *fel_size);
      float projected_distance = 0.0f;
      if (velocity)
	projected_distance = *velocity * difference;
      nearest_blocks.insert(pair<BlockInfo, Block *>(BlockInfo(common_area, projected_distance, Vector(x, y)), block));
    }
#define GIRAFFE(type, coll)						  \
  for (list<type>::const_iterator i = coll.begin(); i != coll.end(); ++i) \
    if ((point - (*i)->get_position()).magnitude() <= 10.0f * 10.0f)	  \
      if (*i != exception /*&& *i != game->get_dave()*/)                  \
	nearest_blocks.insert(                                            \
          pair<BlockInfo, Block *>(BlockInfo((fel_pos && fel_size) ? (*i)->col_get_intersecting_area(*fel_pos, *fel_size) : 0.0f, \
					     1.0f,                        \
					     (*i)->get_position()),       \
				   *i));
  GIRAFFE(Block  *, items);
  GIRAFFE(Fellow *, fellows);
#undef GIRAFFE
  return nearest_blocks;
}

void Level::register_potentially_colliding_area(
  float time,
  const Vector &pos,
  const Vector &size,
  Block *target,
  unique_blocks *feedback_uniq,
  areas *feedback_areas)
{
  int x_min = int(pos.x);
  int y_min = int(pos.y);
  int x_max = x_min + int(size.x);
  int y_max = y_min + int(size.y);
  if (x_min < 0) x_min = 0;
  if (y_min < 0) y_min = 0;
  if (x_max >= layers[3].size.w) x_max = layers[3].size.w - 1;
  if (y_max >= layers[3].size.h) y_max = layers[3].size.h - 1;
  for   (int x=x_min; x<=x_max; x++)
    for (int y=y_min; y<=y_max; y++) {
      Block *const b = blocks[x][y].get_parent();
      Block::Collisions *const c = &blocks[x][y].collisions;
      if (c->as_of != time) {
        c->as_of = time;
        c->count = 0;
      }
      // Fetching the block from the level
      if (b->get_type()->collidable) {
	if (b->get_type()->invisible == 1 && target->get_type() == Fellow::DAVE)
	  continue;
        const float area = b->col_get_intersecting_area(pos, size);
	if (feedback_uniq) {
	  feedback_uniq ->insert(pair<Block *, bool >(b, false));
	  feedback_areas->insert(pair<float, Block *>(area, b));
	}
      }
      // Item block on item layer
      Block *const item = item_layer.blocks[x][y].get_parent();
      if (item->get_type() != Block::Type::get_empty() && item->is_alive()) {
        const float area = item->col_get_intersecting_area(pos, size);
	if (feedback_uniq) {
	  feedback_uniq ->insert(pair<Block *, bool >(item, false));
	  feedback_areas->insert(pair<float, Block *>(area, item));
	}
      }
      // Adding the blocks that were already in feedback
      for (int i=0; i<c->count; i++)
	if (c->colliding_with[i] != target) {
	  const float area = c->colliding_with[i]->col_get_intersecting_area(pos, size);
          if (area <= 0.0f)
            continue;
#if 0
          if (target->program == 1 && c->colliding_with[i]->get_type()->family == Block::Type::DYNAMIC) {
            ostringstream o;
            o << area;
            game->text->set_text(o.str());
          }
#endif
	  if (feedback_uniq) {
	    feedback_uniq ->insert(pair<Block *, bool >(c->colliding_with[i], false));
	    feedback_areas->insert(pair<float, Block *>(area, c->colliding_with[i]));
	  }
	}
      c->colliding_with[c->count++] = target;
      c->count %= 16;
    }
#if 0
  cerr << "Areas: ";
  for (areas::iterator i=feedback_areas->begin(); i!=feedback_areas->end(); ++i)
    cerr << '[' << i->first << " : " << i->second << "] ";
  cerr << endl;
#endif
}

pair<Vector, Vector> Level::collide(Vector pos)   // pos = fellowin paikka
{
  assert(!"häh");
  /*
  Vector col_pos, col_norm;
  multimap<BlockInfo, Block *, BlockInfo::comp> nearest_blocks = col_get_nearest_blocks(pos + Vector(0.5f, 1.0f));

  for (multimap<BlockInfo, Block *, BlockInfo::comp>::iterator i = nearest_blocks.begin(); i != nearest_blocks.end(); ++i) {
    Block *block = i->second;
    if (block->get_type() == Block::HOERPELOE) {
      pair<Vector, Vector> p = block->collide(i->first.position, pos);
      if (!p.second.is_zero())
	return p;
    }
  }

  return pair<Vector, Vector>(col_pos, col_norm);
  */
}

/*pair<Vector, Vector>*/Vector Level::Block::collide(const Vector &fel_pos, const Vector &fel_size, bool check)
{
  //Vector col_pos;       // Piste jossa törmätään
  Vector col_norm;      // Suunta jossa törmätään

  if (check == false || col_is_intersecting(fel_pos, fel_size)) {

    assert(type);
    const Vector &block_size = type->size;
    const Vector fel_centre = fel_pos + fel_size/2.0f;
    const Vector block_centre = position + block_size/2.0f;
    const Vector calibration_fel_pos = position + block_size;
    const Vector calibration_fel_centre = calibration_fel_pos + fel_size/2.0f;
    const float corner = block_centre ^ calibration_fel_centre;

    if (type->geometry == Type::RECTANGLE) {
      const float angle = block_centre ^ fel_centre;
      if      (angle <               corner) { /* oikia */ /*col_pos.x = position.x + 1.0f;      */ col_norm = Vector( 1.0f,  0.0f); }
      else if (angle <        M_PI - corner) { /* ylä   */ /*col_pos.y = position.y + 1.0f;      */ col_norm = Vector( 0.0f,  1.0f); }
      else if (angle <        M_PI + corner) { /* vasen */ /*col_pos.x = position.x - fel_size.x;*/ col_norm = Vector(-1.0f,  0.0f); }
      else if (angle < 2.0f * M_PI - corner) { /* ala   */ /*col_pos.y = position.y - fel_size.y;*/ col_norm = Vector( 0.0f, -1.0f); }
      else                                   { /* oikia */ /*col_pos.x = position.x + 1.0f;      */ col_norm = Vector( 1.0f,  0.0f); }
    }
    else if (type->geometry == Type::R_TRIANGLE) {
      if (orientation == 0) {
        float angle = position ^ fel_centre;
        if      (angle <        M_PI / 2)      { /* liuska */ /*col_pos.x = position.x + 1.0f;      */ col_norm = Vector( 0.06f,  1.0f); }  // 0.6f, 1.0f
        else if (angle <        M_PI + corner) { /* vasen  */ /*col_pos.x = position.x - fel_size.x;*/ col_norm = Vector(-1.0f,  0.0f); }
        else                                   { /* ala    */ /*col_pos.y = position.y - fel_size.y;*/ col_norm = Vector( 0.0f, -1.0f); }
      }
      else if (orientation == 1) {
        float angle = (position + Vector(block_size.x, 0)) ^ fel_centre;
        if      (angle <        M_PI / 2)      { /* oikia  */ /*col_pos.x = position.x + 1.0f;      */ col_norm = Vector( 1.0f,  0.0f); }
        else if (angle <        M_PI)          { /* liuska */ /*col_pos.x = position.x + 1.0f;      */ col_norm = Vector(-0.06f,  1.0f); }  // -0.6f, 1.0f
        else if (angle < 2.0f * M_PI - corner) { /* ala    */ /*col_pos.y = position.y - fel_size.y;*/ col_norm = Vector( 0.0f, -1.0f); }
        else                                   { /* oikia  */ /*col_pos.x = position.x + 1.0f;      */ col_norm = Vector( 1.0f,  0.0f); }
      }
    }
  }
  else
    cerr << "Revittiin törmäyspinnan normaalia vaikka ne kaks asiaa ei edes törmää toisiinsa!\n";

  //return pair<Vector, Vector>(col_pos, col_norm);
  return col_norm;
}

// Testaa onko jokin tämän kentän plokki sisäkkäin fellowin kanssa joka sijaittoo fel_pos:ssa
const bool Level::col_is_intersecting(const Vector &fel_pos, const Vector &fel_size) const
{
  const Vector fellow_centre(fel_pos + fel_size / 2.0f);
  const multimap<BlockInfo, Block *, BlockInfo::comp> nearest_blocks = col_get_nearest_blocks(fellow_centre);
  for (multimap<BlockInfo, Block *, BlockInfo::comp>::const_iterator i = nearest_blocks.begin(); i != nearest_blocks.end(); ++i) {
    assert(i->second);
    assert(i->second->get_type());
    if (i->second->get_type()->collidable) {
      if (i->second->col_is_intersecting(fel_pos, fel_size))
	return true;
    }
  }
  return false;
}

const bool Level::Block::col_is_intersecting(const Vector &fel_pos, const Vector &fel_size) const
{
#if 0
  const float space = -0.05f;
  if (fel_pos.x + space > position.x - fel_size.x   &&  // Blokin vasen sivu
      fel_pos.x - space < position.x + block_size.x &&  //        oikea
      fel_pos.y + space > position.y - fel_size.y   &&  //        ala
      fel_pos.y - space < position.y + block_size.y) {  //        ylä
    return true;
  }
  return false;
#else
  return col_get_intersecting_area(fel_pos, fel_size) > 0.0f;
#endif
}

const float Level::Block::col_get_intersecting_area(const Vector &fel_pos, const Vector &fel_size) const
{
  assert(type);
  const float block_left   = position.x;
  const float block_bottom = position.y;
  const float block_right  = position.x + /*block_size.x*/ int(type->size.x);
  const float block_top    = position.y + /*block_size.y*/ int(type->size.y);
  const float   fel_left   = fel_pos.x;
  const float   fel_bottom = fel_pos.y;
  const float   fel_right  = fel_pos.x + fel_size.x;
  const float   fel_top    = fel_pos.y + fel_size.y;
  const float shrink = 0.0f; //0.07f;
  const float left   = (fel_left   > block_left   ? fel_left   : block_left  ) + shrink;
  const float bottom = (fel_bottom > block_bottom ? fel_bottom : block_bottom) + shrink;
  const float right  = (fel_right  < block_right  ? fel_right  : block_right ) - shrink;
  const float top    = (fel_top    < block_top    ? fel_top    : block_top   ) - shrink;
  const float width  = right - left  ;
  const float height = top   - bottom;
  const float rectangular_area = (width < 0.0f && height < 0.0f) ? -width * height : width * height;
  if (type->geometry == Type::RECTANGLE)
    return rectangular_area;
  else if (type->geometry == Type::R_TRIANGLE)
    if (orientation == 0)
      return (fel_left - block_left > type->size.y - (fel_bottom - block_bottom)) ? -0.0f : rectangular_area / 2.0f;  // "rectarea/2" isn't right
    else if (orientation == 1)
      return (block_right - fel_right > type->size.y - (fel_bottom - block_bottom)) ? -0.0f : rectangular_area / 2.0f;

  return -1337;
}

bool Level::Block::handle_collision_events(Level::Block *against, const Vector &col_surf_normal, const Vector &original_velocity)
{
  return false;
}

string Level::Block::Type::get_real_name() const
{
  // muuntaa: "34234__2353____45_nimi245345345"  -->  "nimi"
  string::const_iterator i = name.begin();
  assert(i != name.end());
skip_digits:
  if (isdigit(*i))
    while (i != name.end() && isdigit(*i)) ++i;
  if (i != name.end() && *i == '_') {
    ++i;
    goto skip_digits;
  }
  assert(i != name.end());
  string real(i, name.end());
  string::const_reverse_iterator j = name.rbegin();
  while (j != name.rend() && isdigit(*j)) {
    real.erase(real.begin() + (real.size() - 1));
    ++j;
  }
  return real;
}

void Level::Block::set_parent(Level::Block *const p)
{
  parent = p;
  type = Type::get_obscured();
}

/* Jos kenttä näyttää tältä
 *   ...........
 *   ...ooooo...
 *   ...ooooo...
 *   ...ooooo...
 *   ...xoooo...
 *   ...........
 * niin tämä funktio palauttaa x:n jos this on mikä tahansa
 * o:ista ja o:iden tyyppi on 8_obscured. Jos taas tämä blokki
 * on irrallinen kentästä (eli se istuu kenttäeditorin gridissä)
 * niin sitten palauttaa blokin ittensä komiasti const_castin läpi.
 */
Level::Block *Level::Block::get_parent() const
{
  if (parent)
    return parent;
  else
    return const_cast<Level::Block *>(this);
  /*
  if (level == NULL)
    return const_cast<Level::Block *>(this);

#error tämä ei muuten toimi
  int x = (int)position.x;
  int y = (int)position.y;
  Block *c = level->get_block(x, y);
  while (c && c->get_type() == "8_obscured")
    c = level->get_block(x, --y);
  y++;
  c = level->get_block(x, y);
  while (c && c->get_type() == "8_obscured")
    c = level->get_block(--x, y);
  x++;
  c = level->get_block(x, y);
  assert(c);
  return c;
  */
}

void Level::Block::set_level(Level *const l)
{
  level = l;
}

Level::Block *Level::get_block(const int x, const int y) const
{
  if (x < 0 || y < 0 || x >= size.w || y >= size.h)
    return NULL;
  else
    return &blocks[x][y];
}

float *Level::get_background()
{
  return background;
}

const int Level::ITEM_LAYER = -1, Level::FELLOW_STP_LAYER = -2;

Level::Block *Level::get_block(const int l, const int x, const int y) const
{
  if (0 <= l && l < (int)layers.size())
    return layers[l].get(x, y);
  else if (l == FELLOW_STP_LAYER)
    return fellow_start_positions.get(x, y);
  else if (l == ITEM_LAYER)
    return item_layer.get(x, y);
  else
    return NULL;
}

int Level::get_size_x(int l) const
{
  if (l >= 0)
    return layers[l].size.w;
  else if (l == ITEM_LAYER)
    return item_layer.size.w;
  else if (l == FELLOW_STP_LAYER)
    return fellow_start_positions.size.w;
  else
    throw;
}

int Level::get_size_y(int l) const
{
  if (l >= 0)
    return layers[l].size.h;
  else if (l == ITEM_LAYER)
    return item_layer.size.h;
  else if (l == FELLOW_STP_LAYER)
    return fellow_start_positions.size.h;
  else
    throw;
}

void Level::Block::blank() /*const*/
{
  if (level == NULL)
    return;
  const Block *const p = get_parent();
  assert(p->get_type());
  const int size_x = int(p->get_type()->size.x);
  const int size_y = int(p->get_type()->size.y);
  for   (int x=0; x<size_x; x++)
    for (int y=0; y<size_y; y++)
      level->get_block(layer,
                       int(p->position.x) + x,
                       int(p->position.y) + y)->set_type(Block::Type::get_empty());
}

const Vector &Level::Block::get_position() const
{
  return position;
}

void Level::Block::die(const float t, int score)
{
  if (alive == ALIVE) {
    alive = DYING;
    died_at = t;
  }
  if (score) {
    if (0 <= score && score < 10)
      game->get_dave()->add_voltage(score);
    else
      game->get_dave()->add_score(score);
    static Block *previous;
    static int previous_score;

    credit = new Teletext();
    ostringstream s;
    const int oscore = score;
    if (previous && previous->credit && !previous->is_dead())
      if ((previous->position - position).magnitude() < 4.1f)
      {
        delete previous->credit;
        previous->credit = NULL;
        score += previous_score;
      }
    if (oscore >= 10 || oscore < 0)
      s << score;
    else
      s << '+' << score << 'V';
    if (credit)
      credit->set_text(s.str());

    previous = this;
    previous_score = score;
  }
}

float Level::Block::death_animation_state() const
{
  if (alive == ALIVE)
    return 0.0f;
  float state = game->get_physics_time() - died_at;
  state *= 1.0f;
  if (state < 0.0f) state = 0.0f;
  if (state > 1.0f) state = 1.0f;
  return state;
}

void Level::Block::set_alive(bool x)
{
  alive = x ? ALIVE : DEAD;
  if (credit) {
    delete credit;
    credit = NULL;
  }
}

void Level::resize(int x, int y)
{
}

void Level::augment(int left, int right, int bottom, int top)
{
  for (int l=-2; l<int(layers.size()); l++)
    get_layer(l)->augment(left, right, bottom, top, l, this);
  size.w += left + right;
  size.h += bottom + top;
  blocks = layers[3].blocks;
}

void Level::blank()
{
  for (int l=-2; l<(int)layers.size(); l++)
    for   (int x=0; x<int(get_size_x()); x++)
      for (int y=0; y<int(get_size_y()); y++)
        get_block(l, x, y)->blank();
}

int Level::get_layer_count() const
{
  return int(layers.size());
}

void Level::set_layer_opacity(const int l, const float o)
{
  if (0 <= l && l < (int)layers.size())
    layers[l].opacity = o;
}

float Level::get_layer_opacity(const int l)
{
  if (0 <= l && l < (int)layers.size())
    return layers[l].opacity;
  else
    return -1.0f;
}

Level::Block *Level::add_item(Level::Block *i)
{
  items.push_back(i);
  return i;
}

void Level::remove(Block *b)
{
  to_remove.push_back(b);
}

void Level::remove_really(Block *b)
{
  try {
    for (list<Fellow *>::iterator i=fellows.begin(); i!=fellows.end(); ++i)
      if (static_cast<Block *>(*i) == b) {
        fellows.erase(i);
        delete dynamic_cast<Fellow *>(b);
        break;
      }
    for (list<Block *>::iterator i=items.begin(); i!=items.end(); ++i)
      if (*i == b) {
        items.erase(i);
        delete b;
        break;
      }
  } catch (exception &e) {
      cerr << "EXXEPTUUNI\n" << flush;
  }
}

const Level::Layer *Level::generate_temporary_fellow_layer() const
{
  assert(!"muttei ol");
  Layer *l = new Layer();
  l->create(size);
  return l;
}

const Level::Layer *Level::generate_temporary_item_layer() const
{
  assert(!"muttei ol");
  Layer *l = new Layer();
  l->create(size);
  return l;
}

void Level::reset()
{
  Fellow *dave = game->get_dave();
  if (dave == NULL)
    return;
  Fellow *dave_kuopio = game->get_dave_kuopio();
  for (list<Fellow *>::iterator i=fellows.begin(); i!=fellows.end(); ++i)
    if (*i != dave && *i != dave_kuopio)
      delete *i;
  fellows.clear();
  fellows.push_back(dave);
  fellows.push_back(dave_kuopio);
  dave->rise_from_dead();
  dave_kuopio->set_alive(false);
  for   (int x=0; x<fellow_start_positions.size.w; x++)
    for (int y=0; y<fellow_start_positions.size.h; y++) {
      const Block::Type *const t = fellow_start_positions.blocks[x][y].get_type();
      if (!t->empty && !t->obscured && t!=dave->get_type()) {
	Fellow *f = new Fellow(t);
        f->program = fellow_start_positions.blocks[x][y].program;
	add_fellow(f);
	f->set_position(Vector(x, y));
      }
    }
  for   (int x=0; x<item_layer.size.w; x++)
    for (int y=0; y<item_layer.size.h; y++)
      item_layer.blocks[x][y].set_alive(true);
}

Vector Level::find_start_position(Level::Block::Type *type) const
{
  for   (int x=0; x<fellow_start_positions.size.w; x++)
    for (int y=0; y<fellow_start_positions.size.h; y++) {
      const Block::Type *const t = fellow_start_positions.blocks[x][y].get_type();
      if (t == type)
	return Vector(x, y);
    }
  return Vector(0.0f, 0.0f);
}

Mix_Music *Level::get_music(bool reversed) const
{
  const int n = game->get_current_level_number();
  return reversed ? current_music_reversed[n] : current_music[n];
}

void Level::play_intro()
{
  const int n = game->get_current_level_number();
  if (intro[n])
    Mix_PlayMusic(intro[n], 1);
}

void Level::play_music(bool rewinding, bool same_as_before)
{
  if (same_as_before)
    rewinding = playing_reverse_music;
  music_start_time = 0.001f * SDL_GetTicks();
  assert(get_music(rewinding));
  Mix_PlayMusic(get_music(rewinding), 1);
  playing_reverse_music = rewinding;
}

void Level::set_music_position(float x)
{
  Mix_SetMusicPosition(x),cerr<<"-------------------------------setmusicposition\n";
  //music_start_time -= x - get_music_position();
//music_start_time = - (x + 0.001f * SDL_GetTicks())
  music_start_time = 0.001f * SDL_GetTicks() - x;
}

float Level::get_music_duration()
{
  return 262.427188f;
}

float Level::get_music_position()
{
  const float r = 0.001f * SDL_GetTicks() - music_start_time;
  //return playing_reverse_music ? 3.0f * r : r;
  return r;
}

/*
static ostream &operator +=(ostream &s, const unsigned short x)
{
  //s.put(x & 0xff);
  //s.put(x >> 8);
  return s;
}
*/

static void write_ubyte(ostream &s, const unsigned char x)
{
  s.write((char *)&x, 1);
}

static void write_string(ostream &s, const string &str)  // HUOM! Jättää lopetusmerkin pois toisin kuin read_string
{
  s.write(str.c_str(), str.size());
}

static void write_ushort(ostream &s, const unsigned short x)
{
  s.write((char*)&x, 2);
}

static void write_8_or_24_bits(ostream &s, const unsigned short x, const bool RLE)
{
  if (x < 127)
    write_ubyte(s, (RLE ? 128 : 0) | (unsigned char)x);
  else {
    write_ubyte(s, RLE ? 255 : 127);
    write_ushort(s, x);
  }
}

#warning NOTE! saving and loading could be optimized by using something more clever than map!

void Level::save(ostream &stream) const
{
  stream << "Dave";
  stream << "LV2";

  stream.put(128 | (unsigned char)layers.size());

  list<const Layer *> final_layers;

  unsigned char layer;
  for (layer = 0; layer < (unsigned char)layers.size(); layer++)
    final_layers.push_back(&layers[layer]);
  final_layers.push_back(&fellow_start_positions);
  final_layers.push_back(&item_layer);

  layer = 0;
  for (list<const Layer *>::iterator lai = final_layers.begin(); lai != final_layers.end(); ++lai, layer++) {

    const Layer *const la = *lai;

    unsigned char lnum = layer;
    if (la == &fellow_start_positions)
      lnum = 128 | 0;
    else if (la == &item_layer)
      lnum = 128 | 1;
    write_ubyte(stream, lnum);

    write_ushort(stream, (unsigned short)la->size.w);
    write_ushort(stream, (unsigned short)la->size.h);

    map<Block::Type *, unsigned short> numbers;
    for   (int x=0; x<la->size.w; x++)
      for (int y=0; y<la->size.h; y++)
        numbers[la->blocks[x][y].get_type()] = 0;

    unsigned short c = 0;
    for (map<Block::Type *, unsigned short>::iterator i=numbers.begin(); i!=numbers.end(); ++i) {
      i->second = c++;
      write_string(stream, i->first->name);
      write_ubyte(stream, 0);
    }

    write_ubyte(stream, 0);

    list<unsigned short> indices;
    for   (int y=0; y<la->size.h; y++)
      for (int x=0; x<la->size.w; x++)
        indices.push_back(numbers[la->blocks[x][y].get_type()]);

    for (list<unsigned short>::iterator i=indices.begin(); i!=indices.end(); ) {
      unsigned char count = 0;
      unsigned short testing = *i;
      for (; i != indices.end() && count < 255; ++i)
        if (*i == testing)
          count++;
        else
          break;
      if (count < 3)  // Ei maksa vaivaa RLE-pakata, kirjoitetaan indeksi(t)
        for (int j=0; j<count; j++)
          write_8_or_24_bits(stream, testing, false);
      else {          // RLE-pakataan
        write_8_or_24_bits(stream, testing, true);
        write_ubyte(stream, count);
      }
    }
  }

  // Saving some special stuff
  for   (int x=0; x<fellow_start_positions.size.w; x++)
    for (int y=0; y<fellow_start_positions.size.h; y++) {
      const Block *const b = &fellow_start_positions.blocks[x][y];
      if (b->get_type() != Level::Block::Type::get_empty() &&
          b->get_type() != Level::Block::Type::get_obscured() &&
          b->program) {
        write_ubyte(stream, 1);
        write_ushort(stream, (unsigned short)x);
        write_ushort(stream, (unsigned short)y);
        write_ubyte(stream, b->program);
      }
    }

  write_ubyte(stream, 0);

  // HUOM!
  // Hauska pakkausidea jonka impementoimalla ei ehkä tarvitte zlibbiä
  // ollenkaan: kun streami on kirjootettu täyteen niin sitten se
  // kelataan alkuun ja aletaan ettiä sieltä tavuja niin kauan kun
  // tuloo kahta samaa tavua ja sitte ne muunnetaan biteiksi. Lopuksi
  // vielä sama uudestaan mutta neljän saman tavun kanssa
  // ABBBABBBAAAABABABBABAC --> 01110111 00001010 11010... 3 C
  // ABCDBCDBBCDABDACBCDABE --> 00011011 01101101 01101100 01110010 01101100 01...... 6 E
  // Lisäksi tämän voi vielä tehdä monta kertaa peräkkäin, esim. kolme!
}

static string get_string(istream &s, const int size)
{
  string str;
  for (int i=0; i<size; i++)
    str += (char)s.get();
  return str;
}

static unsigned short get_ushort(istream &s)
{
  unsigned short x = 0;
  x |= (unsigned char)s.get();
  x |= s.get() << 8;
  return x;
}

static string get_string(istream &s)
{
  string x;
  char c;
  while ((c = (char)s.get()))
    x += c;
  return x;
}

static unsigned char get_ubyte(istream &s)
{
  unsigned char x;
  s.read((char *)&x, 1);
  return x;
}

void Level::load(istream &stream)
{
  string magic = get_string(stream, 4);
  assert(magic == "Dave");
  string format = get_string(stream, 3);

  if (format == "RAW") {
    blank();

    get_ushort(stream); // w
    get_ushort(stream); // h

    map<unsigned short, Block::Type *> types;
    unsigned short c = 0;

    for (bool done = false; !done; done = !stream.peek()) {
      const string &name = get_string(stream);
      Block::Type::insert(name);
      types[c++] = Block::Type::get(name);
    }

    stream.get();

    for   (int x=0; x<size.w; x++)
      for (int y=0; y<size.h; y++) {
        Block::Type *const type = types[get_ushort(stream)];
        if (!type->obscured)
          blocks[x][y].set_type(type);
      }
  }

  else if (format[0] == 'L' && format[1] == 'V') {
    blank();

    const char ver = format[2];
    unsigned char layers = get_ubyte(stream);
    const bool have_items_and_fellows = (layers & 128) == 128;
    layers &= 127;
    const int block_layers = layers;

    if (have_items_and_fellows)
      layers += 2;

    for (int file_layer=0; file_layer<layers; file_layer++) {

      int layer;
      if (ver == '2')
        layer = get_ubyte(stream);
      else
        layer = 3;

      if (layer & 128)
	if (layer == (128 | 0))
	  layer = FELLOW_STP_LAYER;
	else if (layer == (128 | 1))
	  layer = ITEM_LAYER;
	else
	  throw;
      else if (layer >= block_layers)
	throw;

      const unsigned short width  = get_ushort(stream);
      const unsigned short height = get_ushort(stream);

      map<unsigned short, Block::Type *> types;
      unsigned short c = 0;

      for (bool done = false; !done; done = !stream.peek()) {
        string name = get_string(stream);
        // A workaround for formats that don't support _coll_ and _slope_ identifiers
	if      (name == "16_32_ice")          name = "16_32_coll_ice";
	else if (name == "16_32_kivipilari")   name = "16_32_coll_kivipilari";
	else if (name == "16_32_kivi")         name = "16_32_coll_kivi";
	else if (name == "16_8_kivi")          name = "16_8_coll_kivi";
	else if (name == "16_ice")             name = "16_coll_ice";
	else if (name == "16_kivi2")           name = "16_coll_kivi2";
	else if (name == "16_kivi_collision")  name = "16_coll_kivi_collision";
	else if (name == "16_kivi_halkeama")   name = "16_coll_kivi_halkeama";
	else if (name == "16_kivi")            name = "16_coll_kivi";
	else if (name == "16_kivi_saeaetoe")   name = "16_coll_kivi_saeaetoe";
	else if (name == "16_kivi_viisto_inv") name = "16_coll_slope_045_kivi_viisto_inv";
	else if (name == "16_kivi_viisto")     name = "16_coll_slope_135_kivi_viisto";
	else if (name == "32_16_ice")          name = "32_16_coll_ice";
	else if (name == "32_16_kivi")         name = "32_16_coll_kivi";
	else if (name == "32_kivi2")           name = "32_coll_kivi2";
	else if (name == "32_kivi_halkeama")   name = "32_coll_kivi_halkeama";
	else if (name == "32_kivi_liuska_inv") name = "32_coll_slope_045_kivi_liuska_inv";
	else if (name == "32_kivi_liuska")     name = "32_coll_slope_135_kivi_liuska";
	else if (name == "32_kivi")            name = "32_coll_kivi";
	else if (name == "64_dirt_bottom")     name = "64_coll_dirt_bottom";
	else if (name == "64_dirt_top")        name = "64_coll_dirt_top";
	else if (name == "8_16_kivi")          name = "8_16_coll_kivi";
	else if (name == "8_kivi2")            name = "8_coll_kivi2";
	else if (name == "8_kivi")             name = "8_coll_kivi";
        Block::Type::insert(name);
        types[c++] = Block::Type::get(name);
	if (types[c-1] == NULL) {
#if 0
	  cerr << "Could not load type '" << name << "'" << endl;
	  return;
#else
          types[c-1] = Block::Type::get_empty();
#endif
	}
	else cerr << types[c-1] << " ";
      }

      stream.get();

      get_layer(layer)->clear();
      get_layer(layer)->create(Size(width, height));
      get_layer(layer)->format(layer, this);

      for   (int y=0; y<height; )
        for (int x=0; y<height; ) {
          const streampos asdfasdfasdf = stream.tellg();
          assert(stream.good());
          unsigned char byte = get_ubyte(stream);
          unsigned char word = 0;
          unsigned char count = 1;
          if ((byte & 127) == 127)
            word = get_ushort(stream);
          if (byte & 128)
            count = get_ubyte(stream);
          byte &= 127;
          if (!count) throw;
          Block::Type *const type = types[word ? word : byte];
          if (type == NULL) {
            cerr << "Tähän kosahti: " << hex << /*stream.tellg()*/asdfasdfasdf << endl << dec;
            cerr << "Ei voinu ladata tyyppiä " << int(word ? word : byte) << endl;
            cerr << "x=" << x << " y=" << y << endl;
            return;
          }
          for (int i=0; i<count; i++) {
            assert(y < height || (y == height && x == 0));
            if (!type->obscured)
              get_block(layer, x, y)->set_type(type);
            x++;
            if (x >= width) {
              x = 0;
              y++;
            }
          }
        }
      for   (int x=0; x<width;  x++)
	for (int y=0; y<height; y++)
	  assert(get_block(layer, x, y)->get_type());
    }
    size = Level::layers[3].size;
    blocks = Level::layers[3].blocks;

    // All the layers have now been loaded. Next load some special stuff like block programs.
    while (stream.good()) {
      const char c = stream.get();
      if (!c) break;
      switch (c) {
      case 1: {  // Fellow program
          const unsigned short x = get_ushort(stream);
          const unsigned short y = get_ushort(stream);
          const unsigned char p = get_ubyte(stream);
          assert(x < fellow_start_positions.size.w);
          assert(y < fellow_start_positions.size.h);
          fellow_start_positions.blocks[x][y].program = p;
        } break;
      }
    }
  }
  else assert(!"KOLMISIVUINEN ERNULOOTA");
}
