/* do actions according to game-level */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "main.h"

VG_BOOL gamelevel_before(struct s_game *);
VG_BOOL gamelevel_nextlevel(struct s_game *);
VG_BOOL gamelevel_start(struct s_game *);
int gamelevel_done(struct s_game *);
void gamelevel_stop(struct s_game *, int);
void gamelevel_after(struct s_game *, int);


/* do anything before level-loop; return VG_TRUE=OK or VG_FALSE=error/exit-request */
VG_BOOL
gamelevel_before(struct s_game *sgame)
{
  if (sgame == NULL) { return VG_FALSE; }

  /* create player-instance */
  sgame->instanceid.player = objnew_PLAYER(sgame);
  if (sgame->instanceid.player == 0) { return VG_FALSE; }

  /* load audios */
  sgame->instanceid.audio = objnew_AUDIO(sgame);
  if (sgame->instanceid.audio == 0) { return VG_FALSE; }

  return VG_TRUE;
}


/* test for valid level; return VG_TRUE=valid or VG_FALSE=invalid */
VG_BOOL
gamelevel_nextlevel(struct s_game *sgame)
{
  const int ANZLEVEL = 5;

  if (sgame == NULL) { return VG_FALSE; }

  if (sgame->svdt.dnew.gamelevel >= 0 && sgame->svdt.dnew.gamelevel <= ANZLEVEL - 1) { return VG_TRUE; }

  return VG_FALSE;
}


/* do anything before actual level; return VG_TRUE=OK or VG_FALSE=error/exit-request */
VG_BOOL
gamelevel_start(struct s_game *sgame)
{
  const int zufall_osoft_percent = 40;
  struct VG_Rect bgrect;

  if (sgame == NULL) { return VG_FALSE; }

  /* +++ act according to actual level +++ */

  /* initialize nodes and nodelink */
  sgame->nodes.hnode = vg4->hash->create();
  sgame->nodes.ndlnk = vg4->nodelink->create();

  /* create BG background */
  sgame->instanceid.bg = objnew_BG(sgame);
  if (sgame->instanceid.bg == 0) { return VG_FALSE; }
  if (vg4->object->call_data(sgame, sgame->instanceid.bg, &bgrect) <= 0) { return VG_FALSE; }

  /* create collision-tag with size of background */
  sgame->coll_tag = vg4->collision->create(&bgrect, 0, 0);

  /* create BOUNDARY */
  { int i1;
    for (i1 = 0; i1 < bgrect.w; i1 += OBST_SIZE) {
      if (objnew_BOUNDARY(sgame, i1, 0) == 0) { return VG_FALSE; }
      if (objnew_BOUNDARY(sgame, i1, bgrect.h - OBST_SIZE) == 0) { return VG_FALSE; }
    }
    for (i1 = OBST_SIZE; i1 < bgrect.h - OBST_SIZE; i1 += OBST_SIZE) {
      if (objnew_BOUNDARY(sgame, 0, i1) == 0) { return VG_FALSE; }
      if (objnew_BOUNDARY(sgame, bgrect.w - OBST_SIZE, i1) == 0) { return VG_FALSE; }
    }
  }

  /* create OHARD and OSOFT obstacles and nodes */
  { int ix, iy, bgw, bgh, izx, izy;
    struct VG_Point pnt;
    char bkey1[32], bkey2[32];

    bgw = bgrect.w - (OBST_SIZE * 2);
    bgh = bgrect.h - (OBST_SIZE * 2);

    /* add nodes */
    izy = 0;
    for (iy = GANG_SIZE; iy <= bgh; iy += (OBST_SIZE + GANG_SIZE)) {
      izy++;
      izx = 0;
      for (ix = GANG_SIZE; ix <= bgw; ix += (OBST_SIZE + GANG_SIZE)) {
        izx++;
        snprintf(bkey1, sizeof(bkey1), "%d/%d", izx, izy);
        pnt.x = OBST_SIZE + ix - GANG_SIZE / 2;
        pnt.y = OBST_SIZE + iy - GANG_SIZE / 2;
        vg4->hash->set(sgame->nodes.hnode, bkey1, &pnt, sizeof(pnt));
      }
      sgame->nodes.max_x = izx;
    }
    sgame->nodes.max_y = izy;

    /* highest number of nodes */
    izx = sgame->nodes.max_x;
    izy = sgame->nodes.max_y;

    /* add nodelinks */
    for (iy = 1; iy <= izy; iy++) {
      for (ix = 1; ix <= izx; ix++) {
        snprintf(bkey1, sizeof(bkey1), "%d/%d", ix, iy);
        if (ix > 1) {
          snprintf(bkey2, sizeof(bkey2), "%d/%d", ix - 1, iy);
          vg4->nodelink->add(sgame->nodes.ndlnk, bkey1, bkey2, 1, VG_TRUE);
        }
        if (iy > 1) {
          snprintf(bkey2, sizeof(bkey2), "%d/%d", ix, iy - 1);
          vg4->nodelink->add(sgame->nodes.ndlnk, bkey1, bkey2, 1, VG_TRUE);
        }
      }
    }

    /* create OSOFT */
    izy = 0;
    for (iy = GANG_SIZE; iy <= bgh; iy += (OBST_SIZE + GANG_SIZE)) {
      izy++;
      izx = 0;
      for (ix = GANG_SIZE; ix <= bgw; ix += (OBST_SIZE + GANG_SIZE)) {
        izx++;
        pnt.x = OBST_SIZE + ix - GANG_SIZE / 2;
        pnt.y = OBST_SIZE + iy - GANG_SIZE / 2;
        if (ix <= bgw - (OBST_SIZE + GANG_SIZE)) {
          snprintf(bkey1, sizeof(bkey1), "%d/%d", izx, izy);
          snprintf(bkey2, sizeof(bkey2), "%d/%d", izx + 1, izy);
          if (izy != 1 || izx != 1) {
            if (ZUFALL("GAMELEVEL", 1, 100) <= zufall_osoft_percent) {
              if (objnew_OSOFT(sgame, pnt.x + (GANG_SIZE + OBST_SIZE) / 2, pnt.y, bkey1, bkey2) == 0) { return VG_FALSE; }
            }
          }
        }
        if (iy <= bgh - (OBST_SIZE + GANG_SIZE)) {
          snprintf(bkey1, sizeof(bkey1), "%d/%d", izx, izy);
          snprintf(bkey2, sizeof(bkey2), "%d/%d", izx, izy + 1);
          if (izy != 1 || izx != 1) {
            if (ZUFALL("GAMELEVEL", 1, 100) <= zufall_osoft_percent) {
              if (objnew_OSOFT(sgame, pnt.x, pnt.y + (GANG_SIZE + OBST_SIZE) / 2, bkey1, bkey2) == 0) { return VG_FALSE; }
            }
          }
        }
      }
    }

    /* create OHARD */
    for (iy = GANG_SIZE; iy <= bgh; iy += (OBST_SIZE + GANG_SIZE)) {
      for (ix = GANG_SIZE; ix <= bgw; ix += (OBST_SIZE + GANG_SIZE)) {
        pnt.x = OBST_SIZE + ix - GANG_SIZE / 2;
        pnt.y = OBST_SIZE + iy - GANG_SIZE / 2;
        if (ix <= bgw - (OBST_SIZE + GANG_SIZE) && iy <= bgh - (OBST_SIZE + GANG_SIZE)) {
          if (objnew_OHARD(sgame, pnt.x + (GANG_SIZE + OBST_SIZE) / 2, pnt.y + (GANG_SIZE + OBST_SIZE) / 2) == 0) { return VG_FALSE; }
        }
      }
    }
    sgame->svdt.dnew.nr_ohard = vg4->object->list(OBJID_OHARD, 0, NULL);
  }

  /* create management for HUNTER */
  objmgmt_HUNTER(sgame);

  /* create EXIT door */
  sgame->instanceid.exit = objnew_EXIT(sgame);
  if (sgame->instanceid.exit == 0) { return VG_FALSE; }

  /* add goody to each OBJID_OSOFT */
  set_goody_to_osoft(sgame);

  /* reset player */
  vg4->object->call_signal(sgame, sgame->instanceid.player, SIGNO_REINIT);

  /* start music */
  { VG_BOOL do_start = VG_TRUE;
    vg4->object->call_data(sgame, sgame->instanceid.audio, &do_start);
  }

  return VG_TRUE;
}


/* check if actual level is through; return one of GAMEPLAY_* */
int
gamelevel_done(struct s_game *sgame)
{
  if (sgame == NULL) { return GAMEPLAY_MENU; }

  /* act according to actual level */
  if (sgame->level_clear) { sgame->level_clear = VG_FALSE; return GAMEPLAY_DONE; }

  return GAMEPLAY_NOOP;
}


/* do anything after actual level */
void
gamelevel_stop(struct s_game *sgame, int gplay)
{
  if (sgame == NULL) { return; }

  /* +++ act according to actual level +++ */

  /* stop music */
  { VG_BOOL do_start = VG_FALSE;
    if (gplay == GAMEPLAY_DONE || gplay == GAMEPLAY_DEAD) {  /* level completed or dead */
      vg4->object->call_data(sgame, sgame->instanceid.audio, &do_start);
    } else {
      vg4->object->call_data(sgame, sgame->instanceid.audio, NULL);  /* stop at once */
    }
  }

  /* destroy object-instances except PLAYER and AUDIO */
  vg4->object->destroy_except(sgame, OBJID_PLAYER, OBJID_AUDIO, NULL);

  /* destroy object managements */
  vg4->object->mgmt_destroy(OBJID_HUNTER);

  /* destroy collision-tag */
  vg4->collision->destroy(sgame->coll_tag);
  sgame->coll_tag = 0;

  /* destroy nodes and nodelink */
  vg4->nodelink->destroy(sgame->nodes.ndlnk);
  vg4->hash->destroy(sgame->nodes.hnode);
  memset(&sgame->nodes, 0, sizeof(sgame->nodes));

  /* clear rest of game struct */
  sgame->suspend = VG_FALSE;
  sgame->instanceid.exit = 0;
  sgame->instanceid.bg = 0;
  sgame->instanceid.focus = 0;
}


/* do anything after level-loop */
void
gamelevel_after(struct s_game *sgame, int gplay)
{
  if (sgame == NULL) { return; }

  (void)gplay;

  /* destroy audio-instance */
  vg4->object->destroy(sgame, sgame->instanceid.audio);

  /* destroy player-instance */
  vg4->object->destroy(sgame, sgame->instanceid.player);
}
