/*
 *  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 "editor.h"
#include <SDL/SDL.h>
#if !defined(__MACOS__) && !defined(__APPLE__)
#  include <GL/glew.h>
#else
#  include <OpenGL/gl.h>
#  include <OpenGL/glu.h>
#endif
#include "game.h"
#include "library.h"
#include "fellow.h"
#include <dirent.h>
#include <stdio.h>
#include <set>
#include <fstream>
#include <sstream>

extern Game *game;

static HasCallback *editor;

HasCallback::~HasCallback()
{
}

Editor::Editor() : top(  50.0f),
                   right(20.0f),
                   right_total(200.0f),
                   brush("air"),
                   question(NOTHING),
                   palette_offset(0.0f),
                   turn_hide_inactive_on(false)
{
  editor = this;
}

Editor::~Editor()
{
}

void Editor::load()
{
  // Emptying grid
  for (vector<vector<Level::Block *> >::iterator i=grid.begin(); i!=grid.end(); ++i)
    for (vector<Level::Block *>::iterator j=i->begin(); j!=i->end(); ++j)
      if (*j)
	delete *j;
  grid.clear();
  affix.clear();
  regions.clear();

  // Reading affixed ones
  ifstream afx("affix.dat", ios_base::binary | ios_base::in);
  while (afx.good() && !afx.eof()) {
    string str[4];
    for (int i=0; i<4; i++) {
      char c = 0;
      for (;;) {
        if (!afx.good())
          goto stop;
	c = afx.get();
	if (c == '\n')
	  break;
	str[i] += c;
        if (str[i] == "end.")
          goto stop;
      }
    }
    const string &name = str[0];
    const Block::Type::Family family = (Block::Type::Family)atoi(str[1].c_str());
    const Vector location = Vector(atoi(str[2].c_str()), atoi(str[3].c_str()));
    affix[pair<string, Block::Type::Family>(name, family)] = location;
  }
stop:
  afx.close();
  cerr << "Luettiin " << affix.size() << " kpl affixeja." << endl;

  for (int pass=0; pass<2; pass++) {
    struct dirent *dir;
    DIR *d = opendir("graphics");
    if (d) {
      while ((dir = readdir(d)) != NULL) {
        string name = dir->d_name;
        if (name.size() < 5)
          continue;
        char begs[][16] = {"block_", "fellow_", "item_"};
        Block::Type::Family fams[] = {Block::Type::STATIC, Block::Type::DYNAMIC, Block::Type::STATIC_COAT};
        for (int b=0; b<3; b++)
          if (name.substr(0, strlen(begs[b])) == begs[b])
	    if (name.substr(name.size() - 4, 4) == ".png") {
	      const string without_png(dir->d_name, 0, name.size() - 4);
	      load(without_png.substr(strlen(begs[b]), without_png.size() - strlen(begs[b])), fams[b], pass);
	    }
      }
      closedir(d);
    }
  }
  /*
  ofstream a("asdf.txt");
  for (int y=0; y<grid[0].size(); y++) {
    for (int x=0; x<grid.size(); x++)
      if (grid[x][y])
        a << (((char)(grid[x][y]->get_parent()) % 20) + 'a');
      else
        a << '-';
    a << endl;
  }
  a.close();
  */
}

void Editor::load(const string &name, const Block::Type::Family family, int pass)
{
  Level::Block::Type::insert(name, family);
  Level::Block::Type *t = Level::Block::Type::get(name);
  assert(t);
  if (!t->good())
    return;

  // gridin alustus
  while (grid.size() < (unsigned int)right_total)
    grid.push_back(vector<Level::Block *>());
  while (grid[0].size() < (unsigned int)top)
    for (vector<vector<Level::Block *> >::iterator i=grid.begin(); i!=grid.end(); ++i)
      i->push_back(NULL);

  // If it's affixed, put it in its place
  if (pass == 0 && affix.count(pair<string, Block::Type::Family>(name, family)) == 1) {
    const int x = int(affix[pair<string, Block::Type::Family>(name, family)].x);
    const int y = int(affix[pair<string, Block::Type::Family>(name, family)].y);
    for   (int dx=0; dx<int(t->size.x); dx++)
      for (int dy=0; dy<int(t->size.y); dy++)
	if (dx || dy)
	  grid[x+dx][y+dy] = new Level::Block(Level::Block::Type::get_obscured());
	else
	  grid[x][y] = new Level::Block(t);
    return;
  }
  if (pass == 0)
    return;
  else if (affix.count(pair<string, Block::Type::Family>(name, family)) == 1)
    return;

  // Etsitään sopiva rako gridistä
  for   (int y=0; y<=int(top        ) - int(t->size.y); y++)
    for (int x=0; x<=int(right_total) - int(t->size.x); x++) {
      for   (int dx=0; dx<int(t->size.x); dx++)
        for (int dy=0; dy<int(t->size.y); dy++)
          if (grid[x + dx][y + dy])
            goto try_next_spot;
      // Sijoitetaan blokki gridiin
      for   (int dx=0; dx<int(t->size.x); dx++)
        for (int dy=0; dy<int(t->size.y); dy++)
          if (dx || dy)
            grid[x + dx][y + dy] = new Level::Block(Level::Block::Type::get_obscured());
          else
            grid[x][y] = new Level::Block(t);
      return;
try_next_spot:;
    }
}

void Editor::handle(SDL_Event *e)
{
  if (turn_hide_inactive_on) {
    turn_hide_inactive_on = false;
    gui.hide_inactive.set(1);
  }

  Uint8 *keystate = SDL_GetKeyState(NULL);

  switch (e->type) {
  case SDL_MOUSEBUTTONDOWN:
  case SDL_MOUSEMOTION:
    if ((e->type == SDL_MOUSEMOTION /*&& e->motion.state*/) || e->type == SDL_MOUSEBUTTONDOWN)
    {
      //previous = *e;
      const int mx = e->type == SDL_MOUSEMOTION ? e->motion.x : e->button.x;
      const int my = e->type == SDL_MOUSEMOTION ? e->motion.y : e->button.y;
      int state = e->motion.state;
      if (e->type == SDL_MOUSEBUTTONDOWN)
        switch (e->button.button) {
        case SDL_BUTTON_LEFT:   state = SDL_BUTTON(1); break;
        case SDL_BUTTON_MIDDLE: state = SDL_BUTTON(2); break;
        case SDL_BUTTON_RIGHT:  state = SDL_BUTTON(3); break;
        }

      if (mx < 800/3*2) {  // Maalataan kenttää

        int la = int(gui.layer.get());
	if      (brush_family == Block::Type::DYNAMIC)     la = FELLOW_STP_LAYER;
	else if (brush_family == Block::Type::STATIC_COAT) la = ITEM_LAYER;
        string final_brush = brush;
        if (state & SDL_BUTTON(3))
          final_brush = "8_empty";

        const int ry = int(float(       my                 ) / -600.0f * game->get_capacity()                            + game->get_dave()->get_position().y + game->get_capacity()                            / 2.0f - (-0.0f) / 2.0f);
        const int rx = int(float(800 - (mx + (800.0f/6.0f))) / -800.0f * game->get_capacity() * game->get_aspect_ratio() + game->get_dave()->get_position().x + game->get_capacity() * game->get_aspect_ratio() / 2.0f - (-0.0f) / 2.0f);

        const int y = int(float(       my                 ) / -600.0f * game->get_capacity()                            + game->get_dave()->get_position().y + game->get_capacity()                            / 2.0f - (Level::Block::Type::get(final_brush)->size.y - 1.0f) / 2.0f);
        const int x = int(float(800 - (mx + (800.0f/6.0f))) / -800.0f * game->get_capacity() * game->get_aspect_ratio() + game->get_dave()->get_position().x + game->get_capacity() * game->get_aspect_ratio() / 2.0f - (Level::Block::Type::get(final_brush)->size.x - 1.0f) / 2.0f);
        Level::Block *b = level->get_block(la, x, y);
        if (b == NULL)
          break;

        bool skip = false;

	// Picking of a brush
	if ((state & SDL_BUTTON(2)) /*&& !(state & SDL_BUTTON(1)) && !(state & SDL_BUTTON(3))*/) {
	  const Level::Block *const bb = level->get_block(la, rx, ry);
	  if (bb) {
	    final_brush = brush = bb->get_parent()->get_type()->name;
            if (gui.block_name) {
              gui.block_name->set_colors((float[4]){1.0f, 1.0f, 1.0f, 1.0f}, (float[4]){1.0f, 1.0f, 1.0f, 0.0f});
	      gui.block_name->set_text(brush);
            }
	    cerr << "picked: " << brush << endl;
	    skip = true;
	  }
	}

        for   (int dx=0; dx<int(Level::Block::Type::get(final_brush)->size.x); dx++)    // Ei tehdä mitään jos yksikin pensselin alueella
          for (int dy=0; dy<int(Level::Block::Type::get(final_brush)->size.y); dy++) {  // oleva blokki on samaa tyyppiä pensselin kanssa
            const Level::Block *const bb = level->get_block(la, x + dx, y + dy);
            if (bb)
            //if (bb->get_parent()->get_type()->name == final_brush)
              if (bb->get_parent()->get_type()->remove_number() == Level::Block::Type::get(final_brush)->remove_number())
                skip = int(gui.touchpad.get()) ? false : true;
              else;
            else
              skip = true;
          }

        if (state && !skip)
          for   (int dx=0; dx<int(Level::Block::Type::get(final_brush)->size.x); dx++)    // Blankataan kaikki blokit kokonaan pensselin alueelta
            for (int dy=0; dy<int(Level::Block::Type::get(final_brush)->size.y); dy++)
              level->get_block(la, x + dx, y + dy)->blank();

        if (state && !skip) {   // Suoritetaan maalaus
	  const vector<Level::Block::Type *> *const variations = Level::Block::Type::get(final_brush)->get_similar_group();
	  assert(variations);
	  assert(variations->size());
	  int selb = int(float(rand()) / RAND_MAX * variations->size());
	  if (selb < 0) selb = 0;
	  if (selb >= (int)variations->size()) selb = variations->size() - 1;
	  Block::Type *selected = variations->at(selb);
	  b->set_type(selected);   //cerr<<"set type "<<la<<" "<<b->get_position()<<" --- "<<selected->name<<endl;
          b->program = (int)gui.program.get();
	  switch (brush_family) {
	  case Block::Type::STATIC:
	    break;
	  case Block::Type::DYNAMIC:
	    break;
	  case Block::Type::STATIC_COAT:
	    break;
	  case Block::Type::DYNAMIC_COAT:
	    break;
	  }
	}

        else if (!skip) {
          indicator.x = float(x);
          indicator.y = float(y);
          indicator.w = Level::Block::Type::get(brush)->size.x;
          indicator.h = Level::Block::Type::get(brush)->size.y;
        }
        else if (skip) {
          indicator.x = -1000.0f;
          indicator.y = -1000.0f;
        }

      } else if (state) {  // Valitaan blokkityyppi paletista

        const float x = /* 800-->10, 800*(2/3)-->0  */       float(mx) * (right / 800.0f) * 3.0f - 2.0f * right - palette_offset;
        const float y = /* 600-->0 , 0        -->25 */ top - float(my) * (top   / 600.0f);
	if (state & SDL_BUTTON(1)) {
	  for (map<Level::Block *, Region>::iterator i = regions.begin(); i != regions.end(); ++i)
	    if (i->second.is_inside(x, y)) {
	      Block::Type *t = i->first->get_type();
	      brush = t->name;
	      brush_family = t->get_family();
              if (gui.block_name) {
                gui.block_name->set_colors((float[4]){1.0f, 1.0f, 1.0f, 1.0f}, (float[4]){1.0f, 1.0f, 1.0f, 0.0f});
                gui.block_name->set_text(brush);
              }
	    }
	} else if (state & SDL_BUTTON(2)) {  // Affixing
	  const int grid_x = int(x); //  / 8.0f / 2.0f);
	  const int grid_y = int(y); //  / 8.0f / 1.5f);
          if (!(0 <= grid_x && grid_x < int(grid.size())) ||
	      !(0 <= grid_y && grid_y < int(grid[0].size())))
            break;
	  cerr << "grid_xy: " << grid_x << ", " << grid_y << endl;
	  //brush = grid[grid_x][grid_y]->get_type()->name;
	  //brush_family = grid[grid_x][grid_y]->get_type()->get_family();
          // 1. laske alue johon siirrettävä blokki laskeutuisi
          const int newsize_x = (int)Block::Type::get(brush)->size.x;
          const int newsize_y = (int)Block::Type::get(brush)->size.y;
          // 2. selvitä onko tällä alueella kiinnitettyjä blokkeja jo entuudestaan
          // 3. jos on niin tee niistä kiinnittämättömiä
          if (grid_x + newsize_x > (int)grid.size() ||
              grid_y + newsize_y > (int)grid[0].size())
            break;
          for   (int x=grid_x; x<grid_x+newsize_x; x++)
            for (int y=grid_y; y<grid_y+newsize_y; y++)
              for (map<Level::Block *, Region>::iterator i = regions.begin(); i != regions.end(); ++i)
                if (i->second.is_inside(x+0.1f, y+0.1f) && grid[int(i->second.x)][int(i->second.y)])
                  if (affix.count(pair<string, Block::Type::Family>(
                      grid[int(i->second.x)][int(i->second.y)]->get_type()->name,
                      grid[int(i->second.x)][int(i->second.y)]->get_type()->family)))
                    affix.erase(pair<string, Block::Type::Family>(
                      i->first->get_type()->name, i->first->get_type()->family))
		      ,
		      cerr<<"otettiin "<<i->first->get_type()->name<<":n affixednessi pois\n";
          // 4. lisää uusi tieto affixiin
          affix[pair<string, Block::Type::Family>(brush, brush_family)] = Vector(grid_x, grid_y);
          cerr << "lisättiin affixediin: " << brush << endl;
          // 5. kirjoita koko affixin sisältö affix.dattiin
	  ofstream afx("affix.dat", ios_base::binary | ios_base::out);
	  for (map<pair<string, Block::Type::Family>, Vector>::iterator i=affix.begin(); i!=affix.end(); ++i) {
	    afx <<     i->first.first   << '\n'
		<< int(i->first.second) << '\n'
		<< int(i->second.x)     << '\n'
		<< int(i->second.y)     << '\n';
	  }
          afx << "end." << endl;
	  afx.close();
          // 6. lataa paletti uudestaan tyhjästä
          load();
          // 7. päivitä regionit
          int bx=0, by=0;
          for (vector<vector<Level::Block *> >::iterator j=grid.begin(); j!=grid.end(); ++j, ++bx, by=0)
            for (vector<Level::Block *>::iterator i=j->begin(); i!=j->end(); ++i, ++by) {
              if (*i == NULL)
                continue;
              if ((*i)->get_type()->obscured)
                continue;
              regions[*i] = Region(bx, by, (*i)->get_size().x, (*i)->get_size().y);
            }
	}
      }
    }
brk:
    break;

  case SDL_KEYDOWN:
    switch (e->key.keysym.sym) {
    case SDLK_s:    // Tallennus
      message = new Teletext();
      message->set_text(
        "Ookko ny AIVAN VARMA että haluat\n"
        "TALLENTAA " + game->get_level_filename() + ":een\n tämän dadan mitä\n"
        "ruurulla nyt näkyy?!! (y/n)");
      message->set_position(Vector(80, 200));
      message->set_scale(0.5f, 0.9f);
      question = SAVE;
      break;

    case SDLK_l:    // Lataus
      message = new Teletext();
      message->set_text(
        "Ookko ny AIVAN VARMA että haluat\n"
        "LADATA " + game->get_level_filename() + ":sta\n Asian tämän nyt\n"
        "ruurulla näkyvän päälle?!! (y/n)");
      message->set_position(Vector(80, 200));
      message->set_scale(0.5f, 0.9f);
      question = LOAD;
      break;

    case SDLK_n:    // Uus kenttä
      message = new Teletext();
      message->set_text(
        "Ookko ny AIVAN VARMA että haluat\n"
        "POISTAA KAIKKI PALIKAT ja aloottaa\n"
        "tyhjästä kentästä? Häh? (y/n)");
      message->set_position(Vector(80, 200));
      message->set_scale(0.5f, 0.9f);
      question = NEW;
      break;

    case SDLK_t:    // Touchpad
      gui.touchpad.set(1.0f - gui.touchpad.get());
      break;

    case SDLK_1: gui.layer.set(1); break;
    case SDLK_2: gui.layer.set(2); break;
    case SDLK_3: gui.layer.set(3); break;
    case SDLK_4: gui.layer.set(4); break;
    case SDLK_5: gui.layer.set(5); break;
    case SDLK_6: gui.layer.set(6); break;
    case SDLK_7: gui.layer.set(7); break;
    case SDLK_8: gui.layer.set(8); break;
    case SDLK_9: gui.layer.set(9); break;
    case SDLK_0: gui.layer.set(0); break;

    case SDLK_h:
      gui.hide_inactive.set(1.0f - gui.hide_inactive.get());
      break;

    case SDLK_y:    // Yes
      switch (question) {
      case NOTHING:
        break;
      case SAVE: {
        ofstream file(game->get_level_filename().c_str(), ios_base::out | ios_base::binary);
        level->save(file);
        file.close();
        question = NOTHING;
        delete message;
        } break;
      case LOAD: {
        ifstream file(game->get_level_filename().c_str(), ios_base::in | ios_base::binary);
        level->load(file);
        file.close();
        question = NOTHING;
        delete message;
        } break;
      case NEW:
        level->blank();
        break;
      case OTHER:
        break;
      }
      break;

    case SDLK_UP:        if (keystate[SDLK_LCTRL]  || keystate[SDLK_RCTRL] ) level->augment( 0,  0,  0,  1);
                    else if (keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT]) level->augment( 0,  0,  0, -1); break;
    case SDLK_DOWN:      if (keystate[SDLK_LCTRL]  || keystate[SDLK_RCTRL] ) level->augment( 0,  0,  1,  0);
                    else if (keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT]) level->augment( 0,  0, -1,  0); break;
    case SDLK_LEFT:      if (keystate[SDLK_LCTRL]  || keystate[SDLK_RCTRL] ) level->augment( 1,  0,  0,  0);
                    else if (keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT]) level->augment(-1,  0,  0,  0); break;
    case SDLK_RIGHT:     if (keystate[SDLK_LCTRL]  || keystate[SDLK_RCTRL] ) level->augment( 0,  1,  0,  0);
                    else if (keystate[SDLK_LSHIFT] || keystate[SDLK_RSHIFT]) level->augment( 0, -1,  0,  0); break;

      /*
    case SDLK_UP:
    case SDLK_DOWN:
    case SDLK_LEFT:
    case SDLK_RIGHT:
      handle(&previous);
      break;
      */
    case SDLK_ESCAPE:
      message = new Teletext();
      message->set_text(
        "En uskalla lopettaa ohjelmaa kun oot\n"
        "editorissa. Mee pois editorista ensin!");
      message->set_position(Vector(80, 200));
      message->set_scale(0.5f, 0.9f);
      question = OTHER;
      break;

    case SDLK_COMMA:
      palette_offset_speed = 0.5f;
      break;

    case SDLK_PERIOD:
      palette_offset_speed = -0.5f;
      break;

    default:
      if (question != NOTHING) {
        question = NOTHING;
        delete message;
      }
      break;
    }
    break;

  case SDL_KEYUP:
    switch (e->key.keysym.sym) {
    case SDLK_COMMA:
    case SDLK_PERIOD:
      palette_offset_speed = 0.0f;
      break;
    default:
      break;
    }
    break;

  case SDL_QUIT:
    message = new Teletext();
    message->set_text(
      "En uskalla lopettaa ohjelmaa kun oot\n"
      "editorissa. Mee pois editorista ensin!\n");
    message->set_position(Vector(80, 200));
    message->set_scale(0.5f, 0.9f);
    question = OTHER;
    break;

  default: break;
  }

  gui.layer.handle(e);
  gui.r.handle(e);
  gui.g.handle(e);
  gui.b.handle(e);
  gui.layer_opq.handle(e);
  gui.touchpad.handle(e);
  gui.hide_inactive.handle(e);
  gui.level.handle(e);
  gui.program.handle(e);
}

void Editor::cycle()
{
  palette_offset += palette_offset_speed;
}

void Editor::render()
{
  game->rendering_editor = true;
  glPushMatrix();
  glTranslatef(8.0f * indicator.x, 8.0f * indicator.y, 0.0f);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glDisable(GL_TEXTURE_2D);
  glBegin(GL_QUADS);
  glColor4ub(0x72, 0xC2, 0x9C, 128);
  glVertex2f(0.0f,               0.0f);
  glVertex2f(8.0f * indicator.w, 0.0f);
  glVertex2f(8.0f * indicator.w, 8.0f * indicator.h);
  glVertex2f(0.0f,               8.0f * indicator.h);
  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
  glEnd();
  glDisable(GL_BLEND);
  glPopMatrix();

  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  //top   = 50.0f;
  //right = 20.0f;
  //glOrtho(-40.0f, right, 0.0f, top, -1.0f, 1.0f);
  glOrtho(-40.0f - (1.0f-game->get_editor_anim())*right, right - (1.0f-game->get_editor_anim())*right, 0.0f, top, -1.0f, 1.0f);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  glDisable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBegin(GL_QUADS);
  const unsigned char col1[] = {0xF2, 0x73, 0x1B, 0x40};
  const unsigned char col2[] = {0xF2, 0x73, 0x1B, 0xFF};
  const unsigned char col3[] = {0xF2, 0x73, 0x1B, 0xB0};
  glColor4ubv(col2);
  glVertex2f(right, 0.0f);
  glVertex2f(right, top);
  glVertex2f(right/2, top);
  glVertex2f(right/2, 0.0f);
  glVertex2f(right/2, 0.0f);
  glVertex2f(right/2, top);
  glColor4ubv(col1);
  glVertex2f(0.0f, top);
  glVertex2f(0.0f, 0.0f);
  glEnd();
  glLineWidth(1);
  glBegin(GL_LINES);
  glColor4ub(120, 120, 210, 200);
  glVertex2f(0.0f, top);
  glColor4ubv(col3);
  glVertex2f(0.0f, 0.0f);
  glEnd();

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

  glScissor(800*2/3, 0, 800/3, 600);
  glEnable(GL_SCISSOR_TEST);

  glColor4f(1.0f, 1.0f, 1.0f, 1.0f);

  glTranslatef(palette_offset, 0.0f, 0.0f);

  float bx=0, by=0, sel_x, sel_y;
  Level::Block *sel_block = NULL;
  for (vector<vector<Level::Block *> >::iterator j=grid.begin(); j!=grid.end(); ++j, ++bx, by=0)
    for (vector<Level::Block *>::iterator i=j->begin(); i!=j->end(); ++i, ++by) {
      if (*i == NULL)
        continue;
      if ((*i)->get_type()->obscured)
        continue;
      glPushMatrix();
      glTranslatef(bx, by, 0);
      glScalef(1.0f/8.0f, 1.0f/8.0f, 1.0f);
      (*i)->render();
      if (affix.count(pair<string, Block::Type::Family>((*i)->get_type()->name, (*i)->get_type()->family)) == 0) {
        glColor4ub(0x24, 0x5E, 0xEE, 100);
	if ((SDL_GetTicks() / 230) % 2)
	  glColor4ub(0, 0, 0, 0);
        (*i)->render_frame();
        glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
      }
      glPopMatrix();

      regions[*i] = Region(bx, by, (*i)->get_size().x, (*i)->get_size().y);

      if ((*i)->get_type()->name == brush) {
        sel_block = *i;
        sel_x = bx;
        sel_y = by;
      }
    }

  if (sel_block) {
    glPushMatrix();
    glTranslatef(sel_x, sel_y, 0);
    glScalef(1.0f/8.0f, 1.0f/8.0f, 1.0f);
    glLineWidth(2.0f);
    glColor4ub(0xEB, 0x18, 0x64, 128);
    sel_block->render_frame();
    glPopMatrix();
  }
  glDisable(GL_SCISSOR_TEST);

  gui.layer.render();
  gui.r.render();
  gui.g.render();
  gui.b.render();
  gui.layer_opq.render();
  gui.touchpad.render();
  gui.hide_inactive.render();
  gui.level.render();
  gui.program.render();
  if (gui.block_name == NULL) {
    gui.block_name = new Teletext();
    gui.block_name->set_position(Vector(545.0f, 570.0f), Vector(800.0f, 600.0f));
    gui.block_name->set_scale(0.4f, 0.4f);
  }
  gui.block_name->render();

  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);

  if (question)
    message->render();
  game->rendering_editor = false;
}

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

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

Editor::GUI::GUI()
:     layer    (745, 550, 15, 15, 3  , 0, 9  , "leiska"),
          r    (685, 550, 45, 15, 255, 0, 255, "R"),
          g    (685, 535, 45, 15, 255, 0, 255, "G"),
          b    (685, 520, 45, 15, 255, 0, 255, "B"),
  layer_opq    (745, 520, 45, 15, 255, 0, 255, "opacity"),
  touchpad     (600, 550, 15, 15, 0  , 0, 1  , "touchpad"),
  hide_inactive(600, 520, 15, 15, 0  , 0, 1  , "only actv"),
  level        (565, 550, 15, 15, 0  , 0, 2  , "level"),
  program      (565, 520, 30, 15, 0  , 0, 255, "program"),
  block_name(NULL)
{
  r.set_label_pos(-10, 0); r.set_sensitivity(1.0f);
  g.set_label_pos(-10, 0); g.set_sensitivity(1.0f);
  b.set_label_pos(-10, 0); b.set_sensitivity(1.0f);
  layer_opq.set_sensitivity(1.0f);
}

Editor::GUI::~GUI()
{
}

Widget::Widget(int x, int y, int w, int h, const string &label)
: x(x), y(y),
  w(w), h(h),
  label_str(label),
  label(NULL),
  label_x(0),
  label_y(-14)
{
}

Widget::~Widget()
{
  if (label)
    delete label;
}

void Widget::render()
{
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  glOrtho(0, 800, 0, 600, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();

  glDisable(GL_TEXTURE_2D);
  glDisable(GL_BLEND);
  glBegin(GL_QUADS);
  glColor4ub(0x94, 0x24, 0x24, 0xFF);
  glVertex2f(x    , y    );
  glVertex2f(x + w, y    );
  glVertex2f(x + w, y + h);
  glVertex2f(x    , y + h);
  glEnd();
  glLineWidth(1);
  glBegin(GL_LINES);
  glColor4ub(0xE0, 0x1B, 0x1B, 0xFF);
  glVertex2f(x    , y    );
  glVertex2f(x + w, y    );
  glVertex2f(x + w, y    );
  glVertex2f(x + w, y + h);
  glColor4ub(0x4D, 0x14, 0x14, 0xFF);
  glVertex2f(x + w, y + h);
  glVertex2f(x    , y + h);
  glVertex2f(x    , y + h);
  glVertex2f(x    , y    );
  glEnd();
  glColor4f(1,1,1,1);

  if (label == NULL)
    label = new Teletext();

  label->set_text(label_str);
  label->set_position(Vector(x + label_x, y + label_y), Vector(800.0f, 600.0f));
  label->set_scale(0.3f, 0.6f);
  label->set_color(0.0f, 0.0f, 0.0f, 1.0f);
  label->render();
}

void Widget::handle(SDL_Event *e)
{
}

void Widget::set_bounds(int a, int b, int c, int d)
{
  x = a, y = b;
  w = c, h = d;
}

void Widget::set_label_pos(int x, int y)
{
  label_x = x;
  label_y = y;
}

void Widget::pop()
{
  glMatrixMode(GL_MODELVIEW);
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
}

bool Widget::is_inside(const int tx, const int ty) const
{
  return x <=     tx &&     tx <= x + w &&
         y <= 600-ty && 600-ty <= y + h;
}

Number::Number(int x, int y, int w, int h, float v, float min, float max, const string &label)
: Widget(x, y, w, h, label),
  type(FLOAT),
  factor(10),
  tmp_value(factor * v),
  value(v),
  min(min),
  max(max),
  text(NULL),
  adjusting(false)
{
}

Number::Number(int x, int y, int w, int h, int v, int min, int max, const string &label)
: Widget(x, y, w, h, label),
  type(INT),
  factor(10),
  tmp_value(factor * v),
  value(v),
  min(min),
  max(max),
  text(NULL),
  adjusting(false)
{
}

Number::~Number()
{
  if (text)
    delete text;
}

void Number::set_sensitivity(float s)
{
  factor = s;
}

float Number::get() const
{
  return value;
}

void Number::set(float v)
{
  value = v;
  tmp_value = factor * v;
  editor->handle_callback(label_str, value);
}

void Number::render()
{
  Widget::render();

  if (!text)
    text = new Teletext();

  ostringstream s;
  s << value;
  text->set_text(s.str());
  text->set_position(Vector(x, y), Vector(800.0f, 600.0f));
  text->set_scale(0.6f, 0.9f);
  text->set_color(1.0f, 1.0f, 1.0f, 1.0f);
  text->render();

  pop();
}

void Number::handle(SDL_Event *e)
{
  if (e->type == SDL_MOUSEBUTTONDOWN) {
    if (label_str == "level" && is_inside(e->button.x, e->button.y)) {  // Kludge
      if (value == 0.0f) value = 1.0f;
      else if (value == 1.0f) value = 2.0f;
      else value = 0.0f;
      editor->handle_callback(label_str, value);
    }
    if (is_inside(e->button.x, e->button.y))
      adjusting = true;
    else;
  } else if (e->type == SDL_MOUSEBUTTONUP)
    adjusting = false;
  else if (e->type == SDL_MOUSEMOTION)
    if (adjusting) {
      tmp_value += -e->motion.yrel + e->motion.xrel;
      if (tmp_value / factor < min)
        tmp_value = min * factor;
      if (tmp_value / factor > max)
        tmp_value = max * factor;
      value = tmp_value / factor;
      if (type == INT)
	value = int(value);
      editor->handle_callback(label_str, value);
    }
}

// Päivittää widgettien arvot vastaamaan tämänhetkistä tilannetta
void Editor::update_options()
{
  gui.layer_opq.set(255.0f * game->get_current_level()->get_layer_opacity(int(gui.layer.get())));
}

void Editor::handle_callback(const string &label, const float value)
{
  if (label == "leiska") {
    if (gui.hide_inactive.get())
      for (int i=0; i<game->get_current_level()->get_layer_count(); i++)
        game->get_current_level()->set_layer_opacity(i, i == int(gui.layer.get()) ? 1.0f : 0.05f);
    update_options();
  }
  else if (label == "R") {
    game->get_current_level()->get_background()[0] = value / 255.0f;
  }
  else if (label == "G") {
    game->get_current_level()->get_background()[1] = value / 255.0f;
  }
  else if (label == "B") {
    game->get_current_level()->get_background()[2] = value / 255.0f;
  }
  else if (label == "opacity") {
    game->get_current_level()->set_layer_opacity(int(gui.layer.get()), value / 255.0f);
  }
  else if (label == "only actv") {
    if (value == 1.0f) {
      if (opacities.empty()) {
        save_opacities();
        for (int i=0; i<game->get_current_level()->get_layer_count(); i++)
          game->get_current_level()->set_layer_opacity(i, i == int(gui.layer.get()) ? 1.0f : 0.05f);
      }
    } else
      restore_opacities();
    update_options();
  }
  else if (label == "level") {
    game->switch_level(int(value));
    game->get_current_level()->reset();
    game->get_dave()->set_position(game->get_current_level()->find_start_position(game->get_dave()->get_type()));
  }
}

void Editor::save_opacities()
{
  assert(opacities.empty());
  for (int i=0; i<game->get_current_level()->get_layer_count(); i++)
    opacities.push_back(int(game->get_current_level()->get_layer_opacity(i) * 255.0f));
}

void Editor::restore_opacities()
{
  for (unsigned int i=0; i<opacities.size(); i++)
    game->get_current_level()->set_layer_opacity(i, opacities[i] / 255.0f);
  opacities.clear();
}

void Editor::cleanup()
{
  turn_hide_inactive_on = gui.hide_inactive.get() == 1.0f;
  gui.hide_inactive.set(0);
}
