#include "level.h"
#include "artgl.h"
#include "mzmalloc.h"
#include <time.h>
#include <assert.h>
#include <stdlib.h>
#include "SDL.h"
#include <GL/GL.h>
#include "SDL_ttf.h"
#include <math.h>
#include "glfont.h"

#define chance(x,y) ((rand() % (y)) <= ((x)-1))

#define PLAYER_WALK_SPEED 75.0
#define PLAYER_STAIR_SPEED 100.0
#define PLAYER_STEAL_TIME 0.5
#define PLAYER_FLIP_TIME 0.2

#define RESIDENT_WAKEUP_TIME 2.0
#define RESIDENT_WALK_SPEED 50
#define RESIDENT_SCRATCH_TIME 3.0
#define RESIDENT_STAIR_SPEED 75

#define DOG_WAKEUP_TIME 3.0
#define DOG_WALK_TIME 30.0
#define DOG_WALK_SPEED 20
#define DOG_SCRATCH_TIME 5.0

int level_handle_input(Level* l) {

  SDL_Event event;
  int done = 0;



  Player* p = &(l->player);

  /* Poll events */
  while(SDL_PollEvent(&event)) {
    
    switch(event.type) {
      
    case SDL_QUIT:
      done = 1; 
      break;

    case SDL_KEYDOWN:
      switch (event.key.keysym.sym) {
      case SDLK_ESCAPE:
        done = 1;
        break;
      case SDLK_a:
        p->last_dx = p->dx = -1;
        break;
      case SDLK_d:
        p->last_dx = p->dx = 1;
        break;
      case SDLK_s:
        p->dy = -1;
        break;
      case SDLK_w:
        p->dy = 1;
        break;
      default:
        break;
      }
      break;

    case SDL_KEYUP:
      switch (event.key.keysym.sym) {
      case SDLK_ESCAPE:
        done = 1;
        break;
      case SDLK_a:
        if (p->dx < 0) { p->dx = 0; }
        break;
      case SDLK_d:
        if (p->dx > 0) { p->dx = 0; }
        break;
      case SDLK_s:
        if (p->dy < 0) { p->dy = 0; }
        break;
      case SDLK_w:
        if (p->dy > 0) { p->dy = 0; }
        break;
      default:
        break;
      }
      break;

    case SDL_MOUSEMOTION:
      p->flashlight_angle += (event.motion.yrel * 0.4);
      if (p->flashlight_angle > 85) {
        p->flashlight_angle = 85;
      } else if (p->flashlight_angle < -85) {
        p->flashlight_angle = -85;
      }

    case SDL_MOUSEBUTTONDOWN:
      if (event.button.button == SDL_BUTTON_LEFT) {
        p->action = 1;
      } else if (event.button.button == SDL_BUTTON_RIGHT) {
        p->flashlight_on = !(p->flashlight_on);
        if (p->flashlight_on) {
          p->room->been_lit = 1;
        }
      }
      break;
      
    default:
      break;
    }

  }

  if (done) {
    return LEVEL_RESULT_QUIT;
  } else {
    return LEVEL_RESULT_CONTINUE;
  }


};

static Room* level_moveactor(Level* l, Actor* a, double dx, double dy) {

  House* h = l->house;
  double x = a->x + dx;
  double y = a->y + dy;

  // TODO: special case player leaving house

  if (x < 0) {
    x = 0;
  } else if (x + a->w > h->w) {
    x = h->w - a->w;
  }

  if (y < a->h) {
    y = a->h;
  } else if (y > h->h) {
    y = h->h;
  }
  double realy = h->h - y;
  int newlevel = floor(realy / 100);

  if (newlevel > h->n_floors) { newlevel = h->n_floors; }

  Floor* f = h->floors + newlevel;
  Room* newroom = 0;
  
  int i;
  double cx = x + a->w * 0.5;
  for (i=0; i<f->n_rooms; ++i) {
    Room* r = f->rooms[i];
    if (cx >= r->minx && cx <= r->maxx) {
      newroom = r;
      break;
    }
  }

  assert(newroom);
  
  a->x = x;
  a->y = y;
  return newroom;

}


static void level_step_player(Level* l, double dt) {
  
  int old_state = l->player.state;

  int can_descend = 0;
  int can_ascend = 0;
  int can_crouch = 0;
  int can_flatten = PLAYER_FLAG_CAN_FLATTEN;
  int can_steal = 0;
  int can_flip = 0;
  int i;

  Player* p = &(l->player);
  Actor* pa = l->player.actor;
  Room* oldroom = p->room;

  Prop* stealme = 0;
  Prop* upstairs = 0;
  Prop* downstairs = 0;

  // iterate over objects & check flags

  for (i=0; i<oldroom->n_props; ++i) {
    Prop* cur = oldroom->props[i];
    if (!cur) { continue; }
    if (!actor_hoverlap_prop(pa, cur)) { continue; }
    unsigned int f = cur->flags;
    if (f & PROP_FLAG_TREASURE) {
      can_steal = PLAYER_FLAG_CAN_STEAL;
      stealme = cur;
    } else if (f & PROP_FLAG_LIGHTSWITCH) {
      if (oldroom->is_lit) { can_flip = PLAYER_FLAG_CAN_FLIP; }
    } else if (f & PROP_FLAG_STAIRWAYDOWN) {
      can_descend = PLAYER_FLAG_CAN_DESCEND;
      downstairs = cur;
    } else if (f & PROP_FLAG_STAIRWAYUP) {
      can_ascend = PLAYER_FLAG_CAN_ASCEND;
      upstairs = cur;
    } else {
      if (f & PROP_FLAG_CANHIDEUNDER) {
        can_crouch = PLAYER_FLAG_CAN_CROUCH;
      }
      if (!(f & PROP_FLAG_CANHIDEAGAINST)) {
        can_flatten = 0;
      }
    }
  }

  if (can_ascend || can_descend) {
    can_crouch = 0;
    can_flatten = 0;
  }

  if (!oldroom->is_lit && !p->flashlight_on) {
    can_steal = 0;
  }

  p->flags = 
    can_descend | can_ascend | can_crouch | can_flatten | can_steal | can_flip;


  // check state
  if (p->action && p->state <= PLAYER_STATE_WALK) {
    if (can_flip) {
      p->state = PLAYER_STATE_FLIP;
    } else if (can_steal) {
      p->state = PLAYER_STATE_STEAL;
      p->object = stealme;
    } else {
      p->state = PLAYER_STATE_STEAL;
      p->object = 0;
    }
  }
  p->action = 0;

  // check descend/ascend/crouch/flatten
  if (p->dy && p->state <= PLAYER_STATE_WALK) {
    if (p->dy < 0 && can_descend) {
      p->state = PLAYER_STATE_DESCEND;
      p->object = downstairs;
    } else if (p->dy > 0 && can_ascend) {
      p->state = PLAYER_STATE_ASCEND;
      p->object = upstairs;
    } else if (p->dy < 0 && can_crouch) {
      p->state = PLAYER_STATE_CROUCH;
    } else if (p->dy > 0 && can_flatten) {
      p->state = PLAYER_STATE_FLATTEN;
    }
  }

  // handle switch to walking 
  if (p->dx && p->state == PLAYER_STATE_IDLE) {
    p->state = PLAYER_STATE_WALK;
  } 

  if (p->state != old_state) {
    p->state_time = 0;
  }


  // execute new state
  switch (p->state) {
  case PLAYER_STATE_IDLE:
    // do nothing
    break;
  case PLAYER_STATE_WALK:
    if (!p->dx) {
      p->state = PLAYER_STATE_IDLE;
    } else {
      p->room = level_moveactor(l, pa, p->dx * dt * PLAYER_WALK_SPEED, 0.0);
    }
    break;
  case PLAYER_STATE_DESCEND: {
    double dsty;
    if (p->state_time) {
      double realy = l->house->h - pa->y;
      int newlevel = floor(realy / 100);
      dsty = l->house->h - (newlevel*100);
    } else {
      dsty = pa->y + 100;
    }
    p->room = level_moveactor(l, pa, 0.0, dt * PLAYER_STAIR_SPEED);
    if (pa->y >= dsty) {
      p->room = level_moveactor(l, pa, 0.0, -(pa->y-dsty));
      p->object = 0;
      p->state = PLAYER_STATE_IDLE;
    }
    break;
  }
  case PLAYER_STATE_ASCEND: {
    // velocity up. if pos, then idle
    double dsty;
    if (p->state_time) {
      double realy = l->house->h - pa->y;
      int newlevel = floor(realy / 100);
      dsty = l->house->h - ((newlevel+1)*100);
    } else {
      dsty = pa->y - 100;
    }
    p->room = level_moveactor(l, pa, 0.0, -dt * PLAYER_STAIR_SPEED);
    if (pa->y <= dsty) {
      p->room = level_moveactor(l, pa, 0.0, -(pa->y-dsty));
      p->object = 0;
      p->state = PLAYER_STATE_IDLE;
    }
    break;
  }
  case PLAYER_STATE_STEAL:
    // if timeout, disappear object and idle
    if (p->state_time >= PLAYER_STEAL_TIME) {
      if (p->object) {
        l->level_score += p->object->value;
        // TODO: some kind of score event
        for (i=0; i<oldroom->n_props; ++i) {
          if (oldroom->props[i] == p->object) {
            oldroom->props[i] = 0;
          }
        }
        p->object = 0;
        p->has_loot = 1;
      } 
      p->state = PLAYER_STATE_IDLE;
    }
    break;
  case PLAYER_STATE_FLIP:
    // if timeout, idle
    if (p->state_time >= PLAYER_FLIP_TIME) {
      oldroom->is_lit = 0;
      p->state = PLAYER_STATE_IDLE;
    }
    break;
  case PLAYER_STATE_CROUCH:
    // if no dy, idle
    if (p->dy >= 0) { p->state = PLAYER_STATE_IDLE; }
    break;
  case PLAYER_STATE_FLATTEN:
    // if no dy, idle
    if (p->dy <= 0) { p->state = PLAYER_STATE_IDLE; }
    break;
  }

  if (p->state != old_state) {
    p->state_time = 0.0;
  } else {
    p->state_time += dt;
  }

  if (p->room != oldroom && p->flashlight_on) {
    p->room->been_lit = 1;
  }

}


static int level_step_resident(Level* l, double dt) {

  Enemy* r = &(l->resident);
  Actor* ra = r->actor;
  Player* p = &(l->player);
  Actor* pa = p->actor;

  // TODO: resident should take stairs & flip lights on!

  int old_state = r->state;

  int observed = 0;
  int overlap = actor_overlap_actor(ra, pa);
  if (r->room == p->room) {
    double p_cx = ra->x + ra->w * 0.5;
    double d_cx = pa->x + pa->w * 0.5;
    int facing = (p_cx < d_cx && r->dy < 0) || (p_cx > d_cx && r->dy > 0);
    double dist = fabs(p_cx - d_cx);
    if ((p->flashlight_on || r->room->is_lit) && facing && dist < 180) {
      observed = 1;
    } else if (overlap && 
               p->state != PLAYER_STATE_CROUCH &&
               p->state != PLAYER_STATE_FLATTEN) {
      observed = 1;
    }
  }

  int can_descend = 0;
  int can_ascend = 0;
  int can_flip = 0;
  int i;

  for (i=0; i<r->room->n_props; ++i) {
    Prop* cur = r->room->props[i];
    if (!cur) { continue; }
    if (!actor_hoverlap_prop(ra, cur)) { continue; }
    unsigned int f = cur->flags;
    if (f & PROP_FLAG_LIGHTSWITCH) {
      if (!r->room->is_lit) { can_flip = PLAYER_FLAG_CAN_FLIP; }
    } else if (f & PROP_FLAG_STAIRWAYDOWN) {
      can_descend = PLAYER_FLAG_CAN_DESCEND;
    } else if (f & PROP_FLAG_STAIRWAYUP) {
      can_ascend = PLAYER_FLAG_CAN_ASCEND;
    }
  }

  if (can_flip) {
    r->room->is_lit = 1;
  }

  if (r->state <= RESIDENT_STATE_WALK) {
    if (can_ascend && (pa->y < ra->y || chance(1,50))) {
      r->state = RESIDENT_STATE_ASCEND;
      r->state_time = 0;
    } else if (can_descend && (pa->y > ra->y || chance(1, 50))) {
      r->state = RESIDENT_STATE_DESCEND;
      r->state_time = 0;
    }
  }
  
  

  switch (old_state) {
    // TODO: handle ascend/descend/flip
  case RESIDENT_STATE_DESCEND: {
    double dsty;
    if (r->state_time) {
      double realy = l->house->h - ra->y;
      int newlevel = floor(realy / 100);
      dsty = l->house->h - (newlevel*100);
    } else {
      dsty = ra->y + 100;
    }
    r->room = level_moveactor(l, ra, 0.0, dt * RESIDENT_STAIR_SPEED);
    if (ra->y >= dsty) {
      r->room = level_moveactor(l, ra, 0.0, -(ra->y-dsty));
      r->state = RESIDENT_STATE_SCRATCH;
    }
    break;
  }
  case RESIDENT_STATE_ASCEND: {
    // velocity up. if pos, then idle
    double dsty;
    if (r->state_time) {
      double realy = l->house->h - ra->y;
      int newlevel = floor(realy / 100);
      dsty = l->house->h - ((newlevel+1)*100);
    } else {
      dsty = ra->y - 100;
    }
    r->room = level_moveactor(l, ra, 0.0, -dt * RESIDENT_STAIR_SPEED);
    if (ra->y <= dsty) {
      r->room = level_moveactor(l, ra, 0.0, -(ra->y-dsty));
      r->state = RESIDENT_STATE_SCRATCH;
    }
    break;
  }
  case RESIDENT_STATE_IDLE:
    if (r->room == p->room) {
      if (p->flashlight_on || 
          (p->state == PLAYER_STATE_WALK && p->dx && chance(1,100))) {
        // wake up dog
        r->state = RESIDENT_STATE_WAKEUP; 
        r->dy = 1;
      }
    } 
    break;
  case RESIDENT_STATE_WAKEUP:
    // do wakeup
    if (r->state_time > RESIDENT_WAKEUP_TIME) {
      // just waking up
      r->state = RESIDENT_STATE_WALK;
      r->dx = (rand() % 2) ? 1 : -1;
      r->dy = 0;
    }
    break;
  case RESIDENT_STATE_WALK:
    if (observed) {
      // end it
      r->state = RESIDENT_STATE_GASP;
    } else if (chance(1,1000)) {
      // stop walking and scratch
      r->state = RESIDENT_STATE_SCRATCH;
    } else {
      // move
      r->room = 
        level_moveactor(l, ra, 
                        r->dx * dt * RESIDENT_WALK_SPEED, 0.0);
      // TODO: check for bonus
      if (ra->x == 0 || ra->x + ra->w >= l->house->w-0.1) {
        r->dx *= -1;
      }
    }
    break;
  case RESIDENT_STATE_SCRATCH:
    if (observed) {
      // bark
      r->state = RESIDENT_STATE_GASP;
      
    } else if (r->state_time > RESIDENT_SCRATCH_TIME && chance(1,50)) {
      // walk in a random dir
      r->dx = (rand() % 2) ? 1 : -1;
      r->state = RESIDENT_STATE_WALK;
    }
    break;
  }

  if (r->state == old_state) {
    r->state_time += dt;
  } else {
    r->state_time = 0.0;
  }

  if (r->state == RESIDENT_STATE_GASP) {
    return LEVEL_RESULT_DIE;
  }

  return LEVEL_RESULT_CONTINUE;

}

static void level_step_dog(Level* l, double dt) {

  Enemy* d = &(l->dog);
  Actor* da = d->actor;
  Player* p = &(l->player);
  Actor* pa = p->actor;

  int old_state = d->state;

  int observed = 0;
  int overlap = actor_overlap_actor(da, pa);

  if (d->room == p->room) {
    double p_cx = da->x + da->w * 0.5;
    double d_cx = pa->x + pa->w * 0.5;
    int facing = (p_cx < d_cx && d->dy < 0) || (p_cx > d_cx && d->dy > 0);
    if ((p->flashlight_on || d->room->is_lit) && facing) {
      observed = 1;
    } else if (overlap && 
               p->state != PLAYER_STATE_CROUCH &&
               p->state != PLAYER_STATE_FLATTEN) {
      observed = 1;
    }
  }

  switch (old_state) {
  case DOG_STATE_SLEEP:
    if (d->room == p->room) {
      if (p->flashlight_on || 
          (p->state == PLAYER_STATE_WALK && p->dx && chance(1,100))) {
        // wake up dog
        d->state = DOG_STATE_WAKEUP; 
        d->dy = 1;
      }
    } 
    break;
  case DOG_STATE_WAKEUP:
    // do wakeup
    if (d->state_time > DOG_WAKEUP_TIME) {
      if (d->dy > 0) {
        // just waking up
        d->state = DOG_STATE_WALK;
        d->dx = (rand() % 2) ? 1 : -1;
        d->dy = 0;
      } else {
        // just lying down
        d->state = DOG_STATE_SLEEP;
        d->dx = d->dy = 0;
      }
    }
    break;
  case DOG_STATE_WALK:
    if (observed) {
      // bark
      d->state = DOG_STATE_BARK;
    } else if (chance(1,1000)) {
      // stop walking and scratch
      d->state = DOG_STATE_SCRATCH;
    } else if (d->state_time > DOG_WALK_TIME && chance(1,1000)) {
      // timed out so sleep
      d->state = DOG_STATE_WAKEUP;
      d->dy = 0;
    } else {
      // move
      d->room = 
        level_moveactor(l, da, 
                        d->dx * dt * DOG_WALK_SPEED, 0.0);
      // TODO: check for bonus
      if (da->x == 0 || da->x + da->w >= l->house->w-0.1) {
        d->dx *= -1;
      }
    }
    break;
  case DOG_STATE_SCRATCH:
    if (observed) {
      // bark
      d->state = DOG_STATE_BARK;
    } else if (d->state_time > DOG_SCRATCH_TIME && chance(1,50)) {
      // walk in a random dir
      d->dx = (rand() % 2) ? 1 : -1;
      d->state = DOG_STATE_WALK;
    }
    break;
  case DOG_STATE_BARK:
    // bark
    if (l->resident.state == RESIDENT_STATE_IDLE) {
      l->resident.state = RESIDENT_STATE_WAKEUP;
      l->resident.state_time = 0;
    } 
    if (p->room != d->room) {
      // walk
      d->dx = (rand() % 2) ? 1 : -1;
      d->state = DOG_STATE_WALK;
    }
    break;
  }
  if (d->state == old_state) {
    d->state_time += dt;
  } else {
    d->state_time = 0.0;
  }
}

int level_step_ai(Level* l) {

  l->last_time = l->cur_time;
  l->cur_time = clock() / (double)(CLOCKS_PER_SEC);
  if (l->last_time == 0) { l->last_time = l->cur_time; }
  
  if (l->paused) { return LEVEL_RESULT_CONTINUE; }

  double dt = l->cur_time - l->last_time;
  l->level_time_left -= dt;

  level_step_player(l, dt);
  //level_step_dog(l, dt);
  return level_step_resident(l, dt);

};

static void room_draw(Room* r, int invert) {
  int i;
  if (invert) {
    glColor3ub(255, 255, 255);
  } else {
    glColor3ub(0, 0, 0);
  }
  double margin = 6;
  glBegin(GL_QUADS);
  glVertex2d(r->minx+margin, r->maxy-margin);
  glVertex2d(r->maxx-margin, r->maxy-margin);
  glVertex2d(r->maxx-margin, r->miny+margin);
  glVertex2d(r->minx+margin, r->miny+margin);
  glEnd();
  for (i=0; i<r->n_props; ++i) {
    Prop* p = r->props[i];
    if (!p) { continue; }
    if (p->flags & PROP_FLAG_TREASURE) {
      if (!invert) { continue; }
      prop_draw(p, 0);
    } else {
      prop_draw(p, invert);
    }
  }
}

static void house_draw(House* h, double zoom, double py) {
  int i,j;

  // draw floors

  double r = 5;

  for (i=0; i<h->n_floors; ++i) {
    double fy = h->h - i*100;
    if (fabs(fy - py) * zoom > 450) { continue; }
    glColor3ub(255, 255, 255);
    glBegin(GL_QUADS);
    glVertex2d(0-r, fy+r);
    glVertex2d(0-r, fy-r);
    glVertex2d(h->w+r, fy-r);
    glVertex2d(h->w+r, fy+r);
    glEnd();
    // draw rooms
    Floor* f = h->floors + i;
    for (j=0; j<f->n_rooms; ++j) {
      Room* room = f->rooms[j];
      glBegin(GL_QUADS);
      if (j == 0) {
        glVertex2d(room->minx-r, room->miny);
        glVertex2d(room->minx+r, room->miny);
        glVertex2d(room->minx+r, room->maxy+r);
        glVertex2d(room->minx-r, room->maxy+r);
      } else {

        double q = 20;

        glVertex2d(room->minx-r, room->miny);
        glVertex2d(room->minx+r, room->miny);
        glVertex2d(room->minx+r, room->miny+q);
        glVertex2d(room->minx-r, room->miny+q);

        glVertex2d(room->minx-r, room->maxy-q);
        glVertex2d(room->minx+r, room->maxy-q);
        glVertex2d(room->minx+r, room->maxy+r);
        glVertex2d(room->minx-r, room->maxy+r);
      }
      if (j == f->n_rooms-1) {
        glVertex2d(room->maxx-r, room->miny);
        glVertex2d(room->maxx+r, room->miny);
        glVertex2d(room->maxx+r, room->maxy+r);
        glVertex2d(room->maxx-r, room->maxy+r);
      }
      glEnd();
      room_draw(room, room->is_lit);
    }
  }

  // roof
  double roofangle = 20;
  double a = h->w * 0.5;
  double o = a * tan(roofangle*M_PI/180.0);

  ArtVpath vp[5];
  vp[0].code = ART_MOVETO;
  vp[0].x = 0;
  vp[0].y = 0;
  vp[1].code = ART_LINETO;
  vp[1].x = a;
  vp[1].y = -o;
  vp[2].code = ART_LINETO;
  vp[2].x = h->w;
  vp[2].y = 0;
  vp[3].code = ART_LINETO;
  vp[3].x = 0;
  vp[3].y = 0;
  vp[4].code = ART_END;

  
  ArtSVP* tmpsvp = art_svp_vpath_stroke(vp, ART_PATH_STROKE_JOIN_MITER,
                                        ART_PATH_STROKE_CAP_SQUARE,
                                        2*r, 100, 1.0);

  artgl_svp_alpha(tmpsvp, 0xffffffff);

  art_svp_free(tmpsvp);

}


static void level_draw_player(Player* p) {

  /*
  glPointSize(5);
  glPushMatrix();
  glTranslated(p->actor->x, p->actor->y, 0);
  glBegin(GL_POINTS);
  glColor3ub(255, 0, 0);
  int i; 
  for (i=0; i<p->state+1; ++i) {
    glVertex2i(10 * i, 10);
  }
  if (p->dx) {
    glColor3ub(0, 255, 0);
    glVertex2i(0, 20);
  }
  if (p->dy) {
    glColor3ub(0, 0, 255);
    glVertex2i(10, 20);
  }
  glEnd();
  glPopMatrix();
  */
  
  ///////////////////////////////////////////////////////////////////////

  Actor* pa = p->actor;

  if (p->flashlight_on && !p->room->is_lit) {

    // set clip planes and then redraw room according to light rotation
    glPushMatrix();

    double tx = pa->x+0.5*pa->w;
    double ty = pa->y-0.6*pa->h;

    double eqn0[4] = { 0.5, 1, 0, 0 };
    double eqn1[4] = { 0.5, -1, 0, 0 };
    double fa = p->flashlight_angle;

    if (p->last_dx < 0) {
      eqn0[0] *= -1;
      eqn1[0] *= -1;
      fa *= -1;
    }

    glTranslated(tx, ty, 0);
    glRotated(fa, 0, 0, 1);

    glClipPlane(GL_CLIP_PLANE0, eqn0);
    glEnable(GL_CLIP_PLANE0);
    glClipPlane(GL_CLIP_PLANE1, eqn1);
    glEnable(GL_CLIP_PLANE1);

    glPopMatrix();

    room_draw(p->room, 1);
    glDisable(GL_CLIP_PLANE0);
    glDisable(GL_CLIP_PLANE1);

  }


  int sprite;

  switch (p->state) {
  case PLAYER_STATE_IDLE:
  case PLAYER_STATE_WALK:
  case PLAYER_STATE_DESCEND:
  case PLAYER_STATE_ASCEND:
    sprite = 0;
    break;
  case PLAYER_STATE_STEAL:
  case PLAYER_STATE_FLIP:
    sprite = 1;
    break;
  case PLAYER_STATE_CROUCH:
    sprite = 2;
    break;
  case PLAYER_STATE_FLATTEN:
    sprite = 3;
    break;
  default:
    sprite = 0;
  };
  
  actor_draw(pa, sprite, 0, 0, 0, p->last_dx < 0, 0);


}



static void level_draw_resident(Enemy* r) {
  actor_draw(r->actor, 0, 0, 0, 0, r->dx < 0, 0);
}

static void level_draw_dog(Enemy* d) {
  actor_draw(d->actor, 0, 0, 0, 0, d->dx < 0, 0);
}

void level_draw(ArtLibrary* lib, Level* l) {
  
  Player* p = &(l->player);
  Actor* pa = l->player.actor;
  double ctrx = pa->x + pa->w * 0.5;
  double ctry = pa->y - pa->h * 0.5;

  double zoom = 3;

  glClear(GL_COLOR_BUFFER_BIT);
  glPushMatrix();
  glTranslated(400-ctrx, 300-ctry, 0);
  glTranslated(ctrx, ctry, 0);
  glScaled(zoom,zoom,zoom);
  glTranslated(-ctrx, -ctry, 0);

  house_draw(l->house, zoom, l->player.actor->y);
  level_draw_player(&(l->player));
  level_draw_resident(&(l->resident));
  //level_draw_dog(&(l->dog));
  glPopMatrix();

  FontSet* fs = artlibrary_lookup_font(lib, "arial.ttf");
  assert(fs);
  TTF_Font* f = fs->medium;
  assert(f);

  const double slotwidth = 60;
  const int n_slots = 5;
  const double slotminx = 400 - (n_slots * slotwidth * 0.5);

  char buf[1024];
  GlTextBox* tb;

  glBegin(GL_QUADS);
  glColor3ub(0, 0, 0);
  glVertex2d(0,   600);
  glVertex2d(0,   600-(slotwidth+15));
  glVertex2d(800, 600-(slotwidth+15));
  glVertex2d(800, 600);
  glColor3ub(255, 255, 255);
  glVertex2d(0,   600-(slotwidth+5));
  glVertex2d(0,   600-(slotwidth+10));
  glVertex2d(800, 600-(slotwidth+10));
  glVertex2d(800, 600-(slotwidth+5));
  glEnd();

  snprintf(buf, 1024, "LOOT: $%d.00", l->level_score);
  tb = gltext_create(buf, f, 0, 600, 0xffffffff);
  gltext_draw(tb);
  gltext_free(tb);

  if (l->level_time_left >= 0) {
    snprintf(buf, 1024, "TIME: %.2lf", l->level_time_left);
    tb = gltext_create(buf, f, 650, 600, 0xffffffff);
  } else {
    tb = gltext_create("TIME: 0.00", f, 650, 600, 0xff0000ff);
  }
  gltext_draw(tb);
  gltext_free(tb);
  

  glPushMatrix();
  glTranslated(slotminx, 600, 0);

  
  if (p->has_loot) {
    TinySvg* s = artlibrary_lookup_svg(lib, "mask.svg");
    assert(s);
    glPushMatrix();
    glTranslated((slotwidth-s->w)*0.5, -(slotwidth-s->h)*0.5, 0);
    tinysvg_draw(s, 0, 1);
    glPopMatrix();
  } 

  glTranslated(slotwidth, 0, 0);

  if (p->flashlight_on) {
    TinySvg* s = artlibrary_lookup_svg(lib, "flashlight.svg");
    assert(s);
    glPushMatrix();
    glTranslated((slotwidth-s->w)*0.5, -(slotwidth-s->h)*0.5, 0);
    tinysvg_draw(s, 0, 1);
    glPopMatrix();
  }

  glTranslated(slotwidth, 0, 0);

  if (p->flags & PLAYER_FLAG_CAN_STEAL) {    
    TinySvg* s = artlibrary_lookup_svg(lib, "hand.svg");
    assert(s);
    glPushMatrix();
    glTranslated((slotwidth-s->w)*0.5, -(slotwidth-s->h)*0.5, 0);
    tinysvg_draw(s, 0, 1);
    glPopMatrix();
  } else if (p->flags & PLAYER_FLAG_CAN_FLIP) {
    TinySvg* s = artlibrary_lookup_svg(lib, "lightbulb.svg");
    assert(s);
    glPushMatrix();
    glTranslated((slotwidth-s->w)*0.5, -(slotwidth-s->h)*0.5, 0);
    tinysvg_draw(s, 0, 1);
    glPopMatrix();
  }

  glTranslated(slotwidth, 0, 0);

  if (p->flags & PLAYER_FLAG_CAN_ASCEND) {
    TinySvg* s = artlibrary_lookup_svg(lib, "upstairs.svg");
    assert(s);
    glPushMatrix();
    glTranslated((slotwidth-s->w)*0.5, -(slotwidth-s->h)*0.5, 0);
    tinysvg_draw(s, 0, 1);
    glPopMatrix();
  } else if (p->flags & PLAYER_FLAG_CAN_FLATTEN) {
    TinySvg* s = artlibrary_lookup_svg(lib, "uparrow.svg");
    assert(s);
    glPushMatrix();
    glTranslated((slotwidth-s->w)*0.5, -(slotwidth-s->h)*0.5, 0);
    tinysvg_draw(s, 0, 1);
    glPopMatrix();
  } 

  glTranslated(slotwidth, 0, 0);

  if (p->flags & PLAYER_FLAG_CAN_DESCEND) {
    TinySvg* s = artlibrary_lookup_svg(lib, "downstairs.svg");
    assert(s);
    glPushMatrix();
    glTranslated((slotwidth-s->w)*0.5, -(slotwidth-s->h)*0.5, 0);
    tinysvg_draw(s, 0, 1);
    glPopMatrix();
  } else if (p->flags & PLAYER_FLAG_CAN_CROUCH) {
    TinySvg* s = artlibrary_lookup_svg(lib, "downarrow.svg");
    assert(s);
    glPushMatrix();
    glTranslated((slotwidth-s->w)*0.5, -(slotwidth-s->h)*0.5, 0);
    tinysvg_draw(s, 0, 1);
    glPopMatrix();
  }

  glPopMatrix();

  SDL_GL_SwapBuffers();

  // TODO: draw "clock" and score

  
};

Level* level_generate(ArtLibrary* lib, unsigned int level) {

  Level* rval = mznew(Level, 1);
  rval->level = level;
  //rval->paused = 1;

  double roomtime = 30 - 1.5 * level;
  if (roomtime < 5) { roomtime = 5; }

  rval->house = house_generate(lib, level);

  // TODO: better seed positions for dog & resident

  Actor* player_orig = artlibrary_lookup_actor(lib, "player.xml");
  assert(player_orig);
  rval->player.actor = actor_copy(player_orig);
  rval->player.actor->x = 20;
  rval->player.actor->y = rval->house->h;
  rval->player.room = level_moveactor(rval, rval->player.actor, 0, 0);
  rval->player.state = PLAYER_STATE_IDLE;

  Actor* dog_orig = artlibrary_lookup_actor(lib, "dog.xml");
  assert(dog_orig);
  rval->dog.actor = actor_copy(dog_orig);
  rval->dog.actor->x = 20;
  rval->dog.actor->y = rval->house->h - 100;
  rval->dog.room = level_moveactor(rval, rval->dog.actor, 0, 0);
  rval->dog.state = DOG_STATE_SLEEP;

  Actor* resident_orig = artlibrary_lookup_actor(lib, "resident.xml");
  assert(resident_orig);
  rval->resident.actor = actor_copy(resident_orig);
  rval->resident.actor->x = 120;
  rval->resident.actor->y = rval->house->h - 100;
  rval->resident.room = level_moveactor(rval, rval->resident.actor, 0, 0);
  rval->resident.state = RESIDENT_STATE_WALK;
  rval->resident.dx = 1;

  rval->level_total_time = rval->house->n_rooms * roomtime;
  rval->level_time_left = rval->level_total_time;

  return rval;
}

void level_free(Level* l) {
  house_free(l->house);
  actor_free(l->player.actor);
  actor_free(l->dog.actor);
  actor_free(l->resident.actor);
  mzfree(l);
}

void house_debug_print(House* h) {
  int i, j;
  for (i=0; i<h->n_floors; ++i) {

    for (j=0; j<h->cell_w; ++j) { 
      fprintf(stderr, "+------"); 
    } fprintf(stderr, "+\n"); 

    for (j=0; j<h->cell_w; ++j) { 
      fprintf(stderr, "| %04X ", h->floors[i].room_grid[j]);
    } fprintf(stderr, "|\n");

  }

  for (j=0; j<h->cell_w; ++j) { 
    fprintf(stderr, "+------"); 
  } fprintf(stderr, "+\n"); 

}

House* house_generate(ArtLibrary* lib, unsigned int level) {

  House* rval = mznew(House, 1);

  int rlevel = level + (rand() % 5);
      
  // pick number of floors & width
  if (rlevel >= 9) {
    rval->n_floors = (rand() % 3) + 2;
    rval->cell_w = (rand() % 3) + 4;
  } else if (rlevel >= 4) {
    rval->n_floors = (rand() % 2) + 2;
    rval->cell_w = (rand() % 2) + 3;
  } else {
    rval->n_floors = 2;
    rval->cell_w = (rand() % 2) + 3;
  }

  rval->h = 100 * rval->n_floors;
  rval->w = 90.0 * rval->cell_w;
  
  // allocate floors
  rval->floors = mznew(Floor, rval->n_floors);

  // connect some floors
  int i;
  for (i=0; i<rval->n_floors-1; ++i) {
    // connect i and i+1
    Floor* f1 = rval->floors + i;
    Floor* f2 = rval->floors + i + 1;
    int x;
    do {
      x = rand() % rval->cell_w;
    } while (f1->room_grid[x] || f2->room_grid[x]);
    f1->room_grid[x] = (ROOM_TYPE_HALL | PROP_FLAG_STAIRWAYUP);
    f2->room_grid[x] = (ROOM_TYPE_HALL | PROP_FLAG_STAIRWAYDOWN);
    
  }
  
  unsigned int maxlevel = level;
  if (maxlevel > 14) { maxlevel = 14; }
  
  // try connecting more to be nice
  for (i=0; i<rval->n_floors-1; ++i) {
    if (chance(1, 3)) {
      // connect more
      Floor* f1 = rval->floors + i;
      Floor* f2 = rval->floors + i + 1;
      int x = rand() % rval->cell_w;
      if (!f1->room_grid[x] && !f2->room_grid[x]) {
        f1->room_grid[x] = (ROOM_TYPE_HALL | PROP_FLAG_STAIRWAYUP);
        f2->room_grid[x] = (ROOM_TYPE_HALL | PROP_FLAG_STAIRWAYDOWN);
      }
    }
  }

  house_debug_print(rval);

  // pick the rooms contents by size.
  // TODO: enforce that there is a bedroom with a bed in it

  const int types_1[4] = {
    ROOM_TYPE_BATH,
    ROOM_TYPE_LAUNDRY,
    ROOM_TYPE_HALL,
    ROOM_TYPE_BED,
  };

  const int types_2[6] = {
    ROOM_TYPE_BATH,
    ROOM_TYPE_BED,
    ROOM_TYPE_KITCHEN,
    ROOM_TYPE_HALL,
    ROOM_TYPE_STUDY,
    ROOM_TYPE_STUDY,
  };

  const int types_3[4] = {
    ROOM_TYPE_BED,
    ROOM_TYPE_KITCHEN,
    ROOM_TYPE_LIVING,
    ROOM_TYPE_DINING,
  };

  for (i=0; i<rval->n_floors; ++i) {

    Floor* f = rval->floors + i;
    int j;

    for (j=0; j<rval->cell_w; j++) {
      if (f->room_grid[j]) { continue; }

      int k=j+1;
      while (k<rval->cell_w && !f->room_grid[k]) { ++k; }
      int len=k-j;
      if (len > 3) { len = 3; }

      int type = 0;
      int typeok = 0;

      while (!typeok) {
        switch (len) {
        case 1:
          type = types_1[rand()%(sizeof(types_1)/sizeof(int))];
          break;
        case 2:
          type = types_2[rand()%(sizeof(types_2)/sizeof(int))];
          break;
        case 3:
          type = types_3[rand()%(sizeof(types_3)/sizeof(int))];
          break;
        default:
          fprintf(stderr, "len = %d, freaking out!\n", len);
          exit(-1);
        } // switch
        if ((type & ROOM_TYPE_SINGLE) && (type & rval->single_flags)) {
          typeok = 0;
        } else {
          typeok = 1;
        } 
      } // while
      for (k=j; k<j+len; ++k) {
        f->room_grid[k] = type;
      }
      rval->single_flags = (rval->single_flags | type);
      j+=len-1;
    }

    for (j=0; j<rval->cell_w; j++) {
      int type = (f->room_grid[j] & ROOM_TYPE_MASK);
      int flags[3] = { 0, 0, 0 };
      flags[0] = (f->room_grid[j] & PROP_FLAG_MASK);
      int k=j+1;
      while (k<rval->cell_w && (f->room_grid[k] & ROOM_TYPE_MASK) == type) { 
        flags[k-j] = (f->room_grid[k] & PROP_FLAG_MASK);
        ++k; 
      }
      int len = k-j;
      // now make the room
      Room* r = room_create(lib, rval, type, flags, j*90, k*90, rval->h-(i+1)*100, rval->h-i*100);
      ++rval->n_rooms;
      mzrenew(f->rooms, Room*, f->n_rooms, f->n_rooms+1);
      f->rooms[f->n_rooms-1] = r;
      j += len - 1;
    }

  }

  
  // TODO: make frame for house

  house_debug_print(rval);
  
  return rval;
}

void house_free(House* h) {

  int i, j;

  for (i=0; i<h->n_floors; ++i) {
    Floor* f = h->floors + i;
    for (j=0; j<f->n_rooms; ++j) {
      room_free(f->rooms[j]);
    }
    mzfree(f->rooms);
  }
  mzfree(h->floors);
  mzfree(h);
  
}


Room* room_create(ArtLibrary* lib, House* h, int type, int flags[3], double minx, double maxx, double miny, double maxy) {

  printf("room has type %04x, minx=%lf, maxx=%lf, y=%lf\n", type, minx, maxx, miny);

  Room* r = mznew(Room, 1);
  r->minx = minx;
  r->maxx = maxx;
  r->miny = miny;
  r->maxy = maxy;
  r->type = type;

  // TOOD: if flags has stairway up and or down, then deal
  Prop* stairup = artlibrary_lookup_prop(lib, "stairup.xml");
  Prop* stairdown = artlibrary_lookup_prop(lib, "stairdown.xml");
  assert(stairup && stairdown);

  int i;
  int n_stairs = 0;
  Prop* stairs[3];
  
  for (i=0; i<3; ++i) {
    Prop* p = 0;
    if (flags[i] & PROP_FLAG_STAIRWAYUP) {
      p = prop_copy(stairup);
    } else if (flags[i] & PROP_FLAG_STAIRWAYDOWN) {
      p = prop_copy(stairdown);
    }
    if (p) {
      mzrenew(r->props, Prop*, r->n_props, r->n_props+1);
      r->props[r->n_props-1] = p;
      double w = p->svg->w;
      double g = (90 - w) * 0.5;
      double x = minx + (90 * i) + g;
      stairs[n_stairs] = p;
      p->x = x;
      p->y = maxy;
      ++n_stairs;
    }
  }
  
  // TODO: populate room in room_create
  int lmargin = 10 + rand() % 10;
  int rmargin = 10 + rand() % 10;
  double x = minx + lmargin;
  double pright = maxx - rmargin;

  assert(lib->n_props && lib->props);

  Prop** treasures = (Prop**)alloca(sizeof(Prop*)*lib->n_props);
  int ntreasures = 0;

  // find minimum width for props in this room
  double pminw=600;
  for (i=0; i<lib->n_props; ++i) {
    int f = lib->props[i]->flags;
    if (f & (PROP_FLAG_TREASURE)) {
      treasures[ntreasures++] = lib->props[i];
    }
    if (!(f & type) || (f & PROP_FLAG_SPECIALMASK)) {
      continue; 
    }
    double pw = lib->props[i]->svg->w;
    if (pw < pminw) { pminw = pw; }
  }

  double lastx=0;
  double lastw=0;
  while (x < pright - pminw) {


    // find a prop suitable for this room
    Prop* p = 0;
    while (!p || !(p->flags & type) || p->flags & PROP_FLAG_SPECIALMASK) { 
      p = lib->props[rand() % lib->n_props];
    }

    int j;
    int bad = 0;
    for (j=0; j<n_stairs; ++j) {
      p->x = x;
      if (prop_hoverlap_prop(p, stairs[j])) {
        x = stairs[j]->x + stairs[j]->svg->w + rand() % 100 + 4;
        bad = 1;
        break;
      }
    }
    if (bad) { continue; }
    
    if (x + p->svg->w > pright) {
      continue;
    }
    printf("found a prop called %s\n", p->filename);
    // place it
    p = prop_copy(p);
    mzrenew(r->props, Prop*, r->n_props, r->n_props+1);
    r->props[r->n_props-1] = p;
    lastx = p->x = x; 
    lastw = p->svg->w;
    p->y = maxy;
    
    if (p->flags & (PROP_FLAG_T_TOP | PROP_FLAG_T_CENTER)) {
      Prop* treasure = prop_copy(treasures[rand() % ntreasures]);
      treasure->x = x + (p->svg->w - treasure->svg->w)*0.5;
      if (p->flags & PROP_FLAG_T_TOP) {
        treasure->y = maxy - p->svg->h;
      } else {
        treasure->y = maxy - (p->svg->h - 5 - treasure->svg->h);
      }
      mzrenew(r->props, Prop*, r->n_props, r->n_props+1);
      r->props[r->n_props-1] = treasure;
    }

    x += p->svg->w + rand() % 100 + 4;
  };
  

  printf("now the room has %d props at y=%lf.\n", r->n_props, maxy);


  return r;
}

void room_free(Room* r) {
  int i;
  for (i=0; i<r->n_props; ++i) {
    if (r->props[i]) {
      prop_free(r->props[i]);
    }
  }
  if (r->props) { mzfree(r->props); }
  mzfree(r);
}


