/* object OBJID_PLAYER */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "obj-player.h"

unsigned int objnew_PLAYER(void *);

static void f_free(void *, void *);
static VG_BOOL f_run(void *, struct VG_Object *);
static void f_draw(void *, struct VG_Object *);
static int f_data(void *, struct VG_Object *, void *);
static void f_childexit(void *, struct VG_Object *, struct VG_Object *);
static void f_signal(void *, struct VG_Object *, int);

static const int coll_percent = 80;


/* export-function to create a new object-instance of OBJID_PLAYER
 * @param vgame  private structure of the game
 * @return  instance-ID or 0 = error
 */
unsigned int
objnew_PLAYER(void *vgame)
{
  struct s_game *sgame = (struct s_game *)vgame;
  struct VG_Object *objp;
  struct sobj_player *objvars;

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

  /* create private struct */

  objvars = calloc(1, sizeof(*objvars));
  if (objvars == NULL) { return 0; }

  /* load images and sprites */
  objvars->images.stand = vg4->image->load("files/images/player/boy-stand.bmp");
  if (objvars->images.stand == NULL) { f_free(vgame, objvars); return 0; }
  objvars->images.go = vg4->sprite->load("files/images/player/boy-go.sprite");
  if (objvars->images.go == NULL) { f_free(vgame, objvars); return 0; }
  objvars->images.die = vg4->sprite->load("files/images/player/boy-die.sprite");
  if (objvars->images.die == NULL) { f_free(vgame, objvars); return 0; }
  objvars->images.dark_stand = vg4->image->load("files/images/player/boy-dark-stand.bmp");
  if (objvars->images.dark_stand == NULL) { f_free(vgame, objvars); return 0; }
  objvars->images.dark_go = vg4->sprite->load("files/images/player/boy-dark-go.sprite");
  if (objvars->images.dark_go == NULL) { f_free(vgame, objvars); return 0; }
  objvars->images.done = vg4->sprite->load("files/images/player/boy-done.sprite");
  if (objvars->images.done == NULL) { f_free(vgame, objvars); return 0; }
  objvars->images.key_symbol = vg4->image->load("files/images/player/key-symbol.bmp");
  if (objvars->images.key_symbol == NULL) { f_free(vgame, objvars); return 0; }
  objvars->images.heart_symbol = vg4->image->load("files/images/player/heart-symbol.bmp");
  if (objvars->images.heart_symbol == NULL) { f_free(vgame, objvars); return 0; }
  objvars->images.bombs_symbol = vg4->image->load("files/images/player/bombs-symbol.bmp");
  if (objvars->images.bombs_symbol == NULL) { f_free(vgame, objvars); return 0; }

  objvars->gold = sgame->svdt.dnew.gold;
  objvars->anzbombs = sgame->svdt.dnew.anzbombs;
  if (objvars->anzbombs < 1) { objvars->anzbombs = 1; }
  objvars->extralife = sgame->svdt.dnew.extralife;

  /* create object-instance */
  objp = vg4->object->create(OBJID_PLAYER, 0, 0, 5, objvars);
  objp->f_free = f_free;
  objp->f_run = f_run;
  objp->f_draw = f_draw;
  objp->f_data = f_data;
  objp->f_childexit = f_childexit;
  objp->f_signal = f_signal;

  /* object-instance will be later inserted into collision-tag when this is created */

  return objp->instanceid;
}


/* free private struct of object-instance, called from vg4->object->destroy() */
static void
f_free(void *vgame, void *opriv)
{
  struct sobj_player *objvars = (struct sobj_player *)opriv;

  (void)vgame;

  /* free private struct */
  if (objvars->images.stand != NULL) { vg4->image->destroy(objvars->images.stand); }
  if (objvars->images.go != NULL) { vg4->sprite->destroy(objvars->images.go); }
  if (objvars->images.die != NULL) { vg4->sprite->destroy(objvars->images.die); }
  if (objvars->images.dark_stand != NULL) { vg4->image->destroy(objvars->images.dark_stand); }
  if (objvars->images.dark_go != NULL) { vg4->sprite->destroy(objvars->images.dark_go); }
  if (objvars->images.done != NULL) { vg4->sprite->destroy(objvars->images.done); }
  if (objvars->images.key_symbol != NULL) { vg4->image->destroy(objvars->images.key_symbol); }
  if (objvars->images.heart_symbol != NULL) { vg4->image->destroy(objvars->images.heart_symbol); }
  if (objvars->images.bombs_symbol != NULL) { vg4->image->destroy(objvars->images.bombs_symbol); }
  free(objvars);
}


/* move object-instance, called from vg4->object->call_run() */
static VG_BOOL
f_run(void *vgame, struct VG_Object *objp)
{
  const int moving_factor = 30;
  struct s_game *sgame = (struct s_game *)vgame;
  struct sobj_player *objvars = (struct sobj_player *)objp->opriv;
  int angle, movfac;

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

  /* check if player is dying or dead */
  if (objvars->status == SOBJ_PLAYER_DIE) {
    if (vg4->sprite->has_next(objvars->images.die)) { return VG_TRUE; }
    return VG_FALSE;  /* now dead */
  }

  /* level clear */
  if (objvars->status == SOBJ_PLAYER_DONE) {
    if (!vg4->sprite->has_next(objvars->images.done)) {
      sgame->svdt.dnew.gold = objvars->gold;
      sgame->svdt.dnew.anzbombs = objvars->anzbombs;
      sgame->svdt.dnew.extralife = objvars->extralife;
      sgame->level_clear = VG_TRUE;
    }
    return VG_TRUE;
  }

  /* immun-time */
  if (objvars->extralife < 0) { objvars->extralife++; }

  /* check for key-strokes */
  movfac = moving_factor;
  angle = -1;
  objvars->status = SOBJ_PLAYER_STAND;
  if (angle < 0) {
    if (vg4->input->key_newpressed(sgame->kref.k_up)) { movfac = 10; }
    if (vg4->input->key_pressed(sgame->kref.k_up)) {
      angle = 0;
    }
  }
  if (angle < 0) {
    if (vg4->input->key_newpressed(sgame->kref.k_right)) { movfac = 10; }
    if (vg4->input->key_pressed(sgame->kref.k_right)) {
      objvars->moving = SOBJ_PLAYER_MOV_RIGHT;
      angle = 90;
    }
  }
  if (angle < 0) {
    if (vg4->input->key_newpressed(sgame->kref.k_down)) { movfac = 10; }
    if (vg4->input->key_pressed(sgame->kref.k_down)) {
      angle = 180;
    }
  }
  if (angle < 0) {
    if (vg4->input->key_newpressed(sgame->kref.k_left)) { movfac = 10; }
    if (vg4->input->key_pressed(sgame->kref.k_left)) {
      objvars->moving = SOBJ_PLAYER_MOV_LEFT;
      angle = 270;
    }
  }
  if (vg4->input->key_newpressed(sgame->kref.k_drop_bomb)) {
    if (objvars->bombwait == 0 && objvars->putbombs < objvars->anzbombs) {
      objnew_BOMB(sgame, objp->instanceid, objvars->rectc.rect.x + objvars->rectc.rect.w / 2, objvars->rectc.rect.y + objvars->rectc.rect.h / 2);
      objvars->bombwait = 4;
      objvars->putbombs++;
    }
  }

  if (objvars->bombwait > 0) { objvars->bombwait--; }
  if (objvars->dark_counter > 0) { objvars->dark_counter--; }

  /* move and check for collisions */
  if (angle >= 0) {
    struct VG_RectCent *rectcp;
    int rectc_nr = vg4->misc->moving_one_step(&objvars->rectc, angle, movfac, &rectcp);
    if (!vg4->misc->move_and_check_collisions(vgame, objp->instanceid, &objvars->rectc, sgame->coll_tag, rectcp, rectc_nr)) {
      ; /* object-instance is dead: ignore it here */
    }
    if (rectcp != NULL) { free(rectcp); }
    if (objvars->status == SOBJ_PLAYER_STAND) { objvars->status = SOBJ_PLAYER_GO; }
  }

  return VG_TRUE;
}


/* draw object-instance, called from vg4->object->call_draw() */
static void
f_draw(void *vgame, struct VG_Object *objp)
{
  struct s_game *sgame = (struct s_game *)vgame;
  struct sobj_player *objvars = (struct sobj_player *)objp->opriv;
  struct VG_Image *imgp;
  struct VG_ImagecopyAttr iattr;
  struct VG_Position posi;

  if (sgame == NULL) { return; }

  if (sgame->suspend && sgame->instanceid.focus == sgame->instanceid.exit) { return; }

  imgp = NULL;
  VG_IMAGECOPY_ATTR_DEFAULT(&iattr);

  if (objvars->status == SOBJ_PLAYER_DIE) {  /* player dying */
    if (!vg4->sprite->next(objvars->images.die, &imgp, &iattr)) {
      /* player dead */
      return;
    }

  } else if (objvars->status == SOBJ_PLAYER_DONE) {
    vg4->sprite->next(objvars->images.done, &imgp, &iattr);

  } else {
    if (objvars->status == SOBJ_PLAYER_STAND) {
      if (objvars->dark_counter == 0) {
        imgp = objvars->images.stand;
      } else {
        if (objvars->dark_counter > 1000 / LOOP_TIME * 5 || objvars->dark_counter % 10 >= 5) {
          imgp = objvars->images.dark_stand;
        } else {
          imgp = objvars->images.stand;
        }
      }
    } else if (objvars->status == SOBJ_PLAYER_GO) {
      if (objvars->dark_counter == 0) {
        vg4->sprite->next(objvars->images.go, &imgp, &iattr);
      } else {
        if (objvars->dark_counter > 1000 / LOOP_TIME * 5 || objvars->dark_counter % 10 >= 5) {
          vg4->sprite->next(objvars->images.dark_go, &imgp, &iattr);
        } else {
          vg4->sprite->next(objvars->images.go, &imgp, &iattr);
        }
      }
    }
  }

  if (imgp != NULL) {
    if (objvars->moving == SOBJ_PLAYER_MOV_LEFT) {
      iattr.image.flip = VG_AXE_VERTICAL;
    }
    vg4->misc->rect2position(&posi, &objvars->rectc.rect);
    posi = vg4->misc->bgposition_getobj(posi);
    vg4->window->copy(imgp, &posi, &iattr);
  }

  /* draw icons */
  { int iw0, iw1, i1;
    char btxt[128];

    iw0 = 7;
    if (objvars->extralife > 0) {
      vg4->image->getsize(objvars->images.heart_symbol, NULL, &iw1, NULL);
      posi.pos = VG_POS_CENTERED;
      posi.x = iw0;
      posi.y = sgame->winh - 10;
      vg4->window->copy(objvars->images.heart_symbol, &posi, NULL);
      iw0 = iw0 + iw1 + 3;
    }

    if (objvars->has_key) {
      vg4->image->getsize(objvars->images.key_symbol, NULL, &iw1, NULL);
      posi.pos = VG_POS_CENTERED;
      posi.x = iw0;
      posi.y = sgame->winh - 10;
      vg4->window->copy(objvars->images.key_symbol, &posi, NULL);
      iw0 = iw0 + iw1 + 3;
    }

    vg4->image->getsize(objvars->images.bombs_symbol, NULL, &iw1, NULL);
    for (i1 = 0; i1 < (objvars->anzbombs - objvars->putbombs); i1++) {
      posi.pos = VG_POS_CENTERED;
      posi.x = iw0;
      posi.y = sgame->winh - 10;
      vg4->window->copy(objvars->images.bombs_symbol, &posi, NULL);
      iw0 = iw0 + iw1 + 0;
    }

    snprintf(btxt, sizeof(btxt), "%%{imgfile: files/images/player/gold-symbol.bmp%%}: %d", objvars->gold);
    imgp = vg4->font->totext(btxt, NULL, NULL, NULL, NULL);
    if (imgp != NULL) {
      posi.pos = VG_POS_LOWER_RIGHT;
      posi.x = sgame->winw - 10;
      posi.y = sgame->winh - 2;
      vg4->window->copy(imgp, &posi, NULL);
      vg4->image->destroy(imgp);
    }
  }
}


/* get/set data from/for object-instance, called from vg4->object->call_data() */
static int
f_data(void *vgame, struct VG_Object *objp, void *vptr)
{
  struct s_game *sgame = (struct s_game *)vgame;
  struct sobj_player *objvars = (struct sobj_player *)objp->opriv;
  struct VG_Rect *rect = (struct VG_Rect *)vptr;

  if (sgame == NULL || rect == NULL) { return 0; }

  /* return position */
  *rect = objvars->rectc.rect;

  return 1;
}


/* a child of this object-instance is being destroyed */
static void
f_childexit(void *vgame, struct VG_Object *objp, struct VG_Object *child_objp)
{
  struct sobj_player *objvars = (struct sobj_player *)objp->opriv;

  (void)vgame;

  if (strcmp(child_objp->objid, OBJID_BOMB) == 0) {
    objvars->putbombs--;
  }
}

/* receive signal, called from vg4->object->call_signal*() */
static void
f_signal(void *vgame, struct VG_Object *objp, int signo)
{
  struct s_game *sgame = (struct s_game *)vgame;
  struct sobj_player *objvars = (struct sobj_player *)objp->opriv;

  if (signo == SIGNO_REINIT) {  /* reinitialize */
    struct VG_Point *pntp;

    /* set position */
    objvars->rectc.centx = objvars->rectc.centy = 0;
    objvars->rectc.rect.x = objvars->rectc.rect.y = 0;
    objvars->status = SOBJ_PLAYER_STAND;
    objvars->moving = SOBJ_PLAYER_MOV_RIGHT;
    vg4->image->getsize(objvars->images.stand, NULL, &objvars->rectc.rect.w, &objvars->rectc.rect.h);
    pntp = (struct VG_Point *)vg4->hash->get(sgame->nodes.hnode, "1/1", NULL);
    if (pntp != NULL) {
      objvars->rectc.rect.x = pntp->x - objvars->rectc.rect.w / 2;
      objvars->rectc.rect.y = pntp->y - objvars->rectc.rect.h / 2;
    }

    objvars->dark_counter = 0;
    objvars->has_key = VG_FALSE;
    objvars->bombwait = 0;
    objvars->putbombs = 0;

    /* rewind sprites */
    vg4->sprite->rewind(objvars->images.go);
    vg4->sprite->rewind(objvars->images.die);
    vg4->sprite->rewind(objvars->images.dark_go);
    vg4->sprite->rewind(objvars->images.done);

    /* insert object-instance into collision-tag */
    vg4->collision->insert(sgame->coll_tag, objp->instanceid, &objvars->rectc.rect, coll_percent);
  }
}
