/* Copyright 2022-2026 Kurt Nienhaus
 *
 * This file is part of VgaGames.
 * VgaGames 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 2 of the License, or
 * (at your option) any later version.
 * VgaGames 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 VgaGames.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include "misc.h"

void init_misc_bgposition(void);

static void misc_bgposition_init(int, int, int, int);
static VG_BOOL misc_bgposition_getbg(const struct VG_Rect *, struct VG_Position *, VG_BOOL);
static struct VG_Position misc_bgposition_getobj(struct VG_Position);
static VG_BOOL misc_bgposition_reached(const struct VG_Rect *);


/* set functions */
void
init_misc_bgposition(void)
{
  vg4->misc->bgposition_init = misc_bgposition_init;
  vg4->misc->bgposition_getbg = misc_bgposition_getbg;
  vg4->misc->bgposition_getobj = misc_bgposition_getobj;
  vg4->misc->bgposition_reached = misc_bgposition_reached;
} /* Ende init_misc_bgposition */


/* misc_bgposition_init:
 * positioning background-image: initialize
 * @param bg_w         width of background-image
 * @param bg_h         height of background-image
 * @param move_pixel   number of pixels per game-loop for moving background
 * @param win_percent  centered rectangle on window in percent,
 *                     in which object can move without background-positioning,
 *                     e.g.: 100 = whole window
 *                            50 = half width and height of window
 */
static void
misc_bgposition_init(int bg_w, int bg_h, int move_pixel, int win_percent)
{
  struct vgi_misc *sptr;

  sptr = vg4data.lists.misc.s;

  if (bg_w <= 0 || bg_h <= 0) { return; }
  if (move_pixel < 1) { move_pixel = 1; }
  if (win_percent < 0) { win_percent = 0; }
  if (win_percent > 100) { win_percent = 100; }

  sptr->bgmove.bg_w = bg_w;
  sptr->bgmove.bg_h = bg_h;
  vg4->window->getsize(&sptr->bgmove.win_w, &sptr->bgmove.win_h);
  sptr->bgmove.move_pixel = move_pixel;
  sptr->bgmove.pcnt_w = sptr->bgmove.win_w * win_percent / 100;
  sptr->bgmove.pcnt_h = sptr->bgmove.win_h * win_percent / 100;
  sptr->bgmove.is_moving = VG_FALSE;
  sptr->bgmove.bg_x = sptr->bgmove.bg_y = 0;
  sptr->bgmove.do_atonce = VG_TRUE;
} /* Ende misc_bgposition_init */


/* misc_bgposition_getbg:
 * positioning background-image: calculate position of background-image
 * @param objrect   position of object to be focused
 * @param bgpos     for returning position (centered) of background-image
 * @param atonce    VG_TRUE: focus immediatelly, VG_FALSE: move to object
 * @return  VG_TRUE: moving, VG_FALSE: object reached
 */
static VG_BOOL
misc_bgposition_getbg(const struct VG_Rect *objrect, struct VG_Position *bgpos, VG_BOOL atonce)
{
  int pcntdiff_w, pcntdiff_h, bgdiff_w, bgdiff_h;
  struct vgi_misc *sptr;
  int obj_x, obj_y;
  VG_BOOL is_moving;

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

  sptr = vg4data.lists.misc.s;
  is_moving = sptr->bgmove.is_moving;

  if (bgpos == NULL && (atonce || sptr->bgmove.do_atonce)) { return VG_TRUE; }
  if (sptr->bgmove.do_atonce) { atonce = VG_TRUE; sptr->bgmove.do_atonce = VG_FALSE; }

  pcntdiff_w = (sptr->bgmove.win_w - sptr->bgmove.pcnt_w) / 2;
  pcntdiff_h = (sptr->bgmove.win_h - sptr->bgmove.pcnt_h) / 2;
  bgdiff_w = sptr->bgmove.win_w - sptr->bgmove.bg_w;
  bgdiff_h = sptr->bgmove.win_h - sptr->bgmove.bg_h;

  obj_x = objrect->x + objrect->w / 2;
  obj_y = objrect->y + objrect->h / 2;

  if (atonce) {
    /* width */
    if (bgdiff_w >= 0) {  /* width: background <= window */
      bgpos->x = bgdiff_w / 2;
    } else {  /* width: background > window */
      bgpos->x = -obj_x + sptr->bgmove.win_w / 2;
      if (bgpos->x > 0) { bgpos->x = 0; }
      if (bgpos->x < bgdiff_w) { bgpos->x = bgdiff_w; }
    }
    sptr->bgmove.bg_x = bgpos->x;

    /* height */
    if (bgdiff_h >= 0) {  /* height: background <= window */
      bgpos->y = bgdiff_h / 2;
    } else {  /* height: background > window */
      bgpos->y = -obj_y + sptr->bgmove.win_h / 2;
      if (bgpos->y > 0) { bgpos->y = 0; }
      if (bgpos->y < bgdiff_h) { bgpos->y = bgdiff_h; }
    }
    sptr->bgmove.bg_y = bgpos->y;

    is_moving = VG_FALSE;
  }

  if (bgpos != NULL) {
    bgpos->x = sptr->bgmove.bg_x;
    bgpos->y = sptr->bgmove.bg_y;
  }

  if (!is_moving) {
    int ivon, ibis;

    /* width */
    if (bgdiff_w < 0) {  /* width: background > window */
      ivon = -obj_x + pcntdiff_w;
      if (ivon > 0) { ivon = 0; }
      if (ivon < bgdiff_w) { ivon = bgdiff_w; }
      ibis = -obj_x + pcntdiff_w + sptr->bgmove.pcnt_w;
      if (ibis > 0) { ibis = 0; }
      if (ibis < bgdiff_w) { ibis = bgdiff_w; }
      if (sptr->bgmove.bg_x < ivon || sptr->bgmove.bg_x > ibis) { is_moving = VG_TRUE; }
    }

    /* height */
    if (bgdiff_h < 0) {  /* height: background > window */
      ivon = -obj_y + pcntdiff_h;
      if (ivon > 0) { ivon = 0; }
      if (ivon < bgdiff_h) { ivon = bgdiff_h; }
      ibis = -obj_y + pcntdiff_h + sptr->bgmove.pcnt_h;
      if (ibis > 0) { ibis = 0; }
      if (ibis < bgdiff_h) { ibis = bgdiff_h; }
      if (sptr->bgmove.bg_y < ivon || sptr->bgmove.bg_y > ibis) { is_moving = VG_TRUE; }
    }
  }

  if (bgpos == NULL) { return is_moving; }

  sptr->bgmove.is_moving = is_moving;

  if (sptr->bgmove.is_moving) {
    int imipu, nomove = 0;

    /* width */
    if (bgdiff_w >= 0) {  /* width: background <= window */
      bgpos->x = bgdiff_w / 2;
      nomove++;
    } else {  /* width: background > window */
      imipu = -obj_x + sptr->bgmove.win_w / 2;
      if (imipu > 0) { imipu = 0; }
      if (imipu < bgdiff_w) { imipu = bgdiff_w; }
      if (bgpos->x < imipu) {
        bgpos->x += sptr->bgmove.move_pixel;
        if (bgpos->x > imipu) { bgpos->x = imipu; }
      } else if (bgpos->x > imipu) {
        bgpos->x -= sptr->bgmove.move_pixel;
        if (bgpos->x < imipu) { bgpos->x = imipu; }
      }
      if (bgpos->x == imipu) { nomove++; }
    }
    sptr->bgmove.bg_x = bgpos->x;

    /* height */
    if (bgdiff_h >= 0) {  /* height: background <= window */
      bgpos->y = bgdiff_h / 2;
      nomove++;
    } else {  /* height: background > window */
      imipu = -obj_y + sptr->bgmove.win_h / 2;
      if (imipu > 0) { imipu = 0; }
      if (imipu < bgdiff_h) { imipu = bgdiff_h; }
      if (bgpos->y < imipu) {
        bgpos->y += sptr->bgmove.move_pixel;
        if (bgpos->y > imipu) { bgpos->y = imipu; }
      } else if (bgpos->y > imipu) {
        bgpos->y -= sptr->bgmove.move_pixel;
        if (bgpos->y < imipu) { bgpos->y = imipu; }
      }
      if (bgpos->y == imipu) { nomove++; }
    }
    sptr->bgmove.bg_y = bgpos->y;

    /* still moving? */
    if (nomove == 2) { sptr->bgmove.is_moving = VG_FALSE; }
  }

  bgpos->pos = VG_POS_CENTERED;
  bgpos->x += (sptr->bgmove.bg_w / 2);
  bgpos->y += (sptr->bgmove.bg_h / 2);

  return sptr->bgmove.is_moving;
} /* Ende misc_bgposition_getbg */


/* misc_bgposition_getobj:
 * positioning background-image: return window-position of object's background-position
 * @param opos   object's background-position
 * @return  object's window-position
 */
static struct VG_Position
misc_bgposition_getobj(struct VG_Position opos)
{
  struct VG_Position npos = opos;
  struct vgi_misc *sptr;

  sptr = vg4data.lists.misc.s;

  npos.x += sptr->bgmove.bg_x;
  npos.y += sptr->bgmove.bg_y;

  return npos;
} /* Ende misc_bgposition_getobj */


/* misc_bgposition_reached:
 * positioning background-image: return whether moving background-image has reached object to be focused
 * @param objrect   position of object to be focused
 * @return  VG_TRUE: reached, VG_FALSE: moving
 */
static VG_BOOL
misc_bgposition_reached(const struct VG_Rect *objrect)
{
  return !vg4->misc->bgposition_getbg(objrect, NULL, VG_FALSE);
} /* Ende misc_bgposition_reached */
