/*
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 */

/** \file
 * \ingroup eduv
 */

#include <stdio.h>
#include <string.h>

#include "MEM_guardedalloc.h"

#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"

#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"

#include "BLT_translation.h"

#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
#include "BKE_screen.h"

#include "DEG_depsgraph.h"

#include "ED_image.h"
#include "ED_uvedit.h"

#include "UI_interface.h"

#include "WM_api.h"
#include "WM_types.h"

#define B_UVEDIT_VERTEX 3

/* UV Utilities */

static int uvedit_center(Scene *scene, Object **objects, uint objects_len, float center[2])
{
  BMFace *f;
  BMLoop *l;
  BMIter iter, liter;
  MLoopUV *luv;
  int tot = 0;

  zero_v2(center);

  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
    Object *obedit = objects[ob_index];
    BMEditMesh *em = BKE_editmesh_from_object(obedit);
    const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);

    BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
      if (!uvedit_face_visible_test(scene, f)) {
        continue;
      }

      BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
        if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
          luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
          add_v2_v2(center, luv->uv);
          tot++;
        }
      }
    }
  }

  if (tot > 0) {
    center[0] /= tot;
    center[1] /= tot;
  }

  return tot;
}

static void uvedit_translate(Scene *scene,
                             Object **objects,
                             uint objects_len,
                             const float delta[2])
{
  BMFace *f;
  BMLoop *l;
  BMIter iter, liter;
  MLoopUV *luv;

  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
    Object *obedit = objects[ob_index];
    BMEditMesh *em = BKE_editmesh_from_object(obedit);

    const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);

    BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
      if (!uvedit_face_visible_test(scene, f)) {
        continue;
      }

      BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
        if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
          luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
          add_v2_v2(luv->uv, delta);
        }
      }
    }
  }
}

/* Button Functions, using an evil static variable */

static float uvedit_old_center[2];

static void uvedit_vertex_buttons(const bContext *C, uiBlock *block)
{
  SpaceImage *sima = CTX_wm_space_image(C);
  Scene *scene = CTX_data_scene(C);
  float center[2];
  int imx, imy, step, digits;
  uint objects_len = 0;
  Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
      CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);

  ED_space_image_get_size(sima, &imx, &imy);

  if (uvedit_center(scene, objects, objects_len, center)) {
    float range_xy[2][2] = {
        {-10.0f, 10.0f},
        {-10.0f, 10.0f},
    };

    copy_v2_v2(uvedit_old_center, center);

    /* expand UI range by center */
    CLAMP_MAX(range_xy[0][0], uvedit_old_center[0]);
    CLAMP_MIN(range_xy[0][1], uvedit_old_center[0]);
    CLAMP_MAX(range_xy[1][0], uvedit_old_center[1]);
    CLAMP_MIN(range_xy[1][1], uvedit_old_center[1]);

    if (!(sima->flag & SI_COORDFLOATS)) {
      uvedit_old_center[0] *= imx;
      uvedit_old_center[1] *= imy;

      mul_v2_fl(range_xy[0], imx);
      mul_v2_fl(range_xy[1], imy);
    }

    if (sima->flag & SI_COORDFLOATS) {
      step = 1;
      digits = 3;
    }
    else {
      step = 100;
      digits = 2;
    }

    uiBut *but;

    int y = 0;
    UI_block_align_begin(block);
    but = uiDefButF(block,
                    UI_BTYPE_NUM,
                    B_UVEDIT_VERTEX,
                    IFACE_("X:"),
                    0,
                    y -= UI_UNIT_Y,
                    200,
                    UI_UNIT_Y,
                    &uvedit_old_center[0],
                    UNPACK2(range_xy[0]),
                    0,
                    0,
                    "");
    UI_but_number_step_size_set(but, step);
    UI_but_number_precision_set(but, digits);
    but = uiDefButF(block,
                    UI_BTYPE_NUM,
                    B_UVEDIT_VERTEX,
                    IFACE_("Y:"),
                    0,
                    y -= UI_UNIT_Y,
                    200,
                    UI_UNIT_Y,
                    &uvedit_old_center[1],
                    UNPACK2(range_xy[1]),
                    0,
                    0,
                    "");
    UI_but_number_step_size_set(but, step);
    UI_but_number_precision_set(but, digits);
    UI_block_align_end(block);
  }

  MEM_freeN(objects);
}

static void do_uvedit_vertex(bContext *C, void *UNUSED(arg), int event)
{
  SpaceImage *sima = CTX_wm_space_image(C);
  Scene *scene = CTX_data_scene(C);
  float center[2], delta[2];
  int imx, imy;

  if (event != B_UVEDIT_VERTEX) {
    return;
  }

  uint objects_len = 0;
  Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
      CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);

  ED_space_image_get_size(sima, &imx, &imy);
  uvedit_center(scene, objects, objects_len, center);

  if (sima->flag & SI_COORDFLOATS) {
    delta[0] = uvedit_old_center[0] - center[0];
    delta[1] = uvedit_old_center[1] - center[1];
  }
  else {
    delta[0] = uvedit_old_center[0] / imx - center[0];
    delta[1] = uvedit_old_center[1] / imy - center[1];
  }

  uvedit_translate(scene, objects, objects_len, delta);

  WM_event_add_notifier(C, NC_IMAGE, sima->image);
  for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
    Object *obedit = objects[ob_index];
    DEG_id_tag_update((ID *)obedit->data, ID_RECALC_GEOMETRY);
  }

  MEM_freeN(objects);
}

/* Panels */

static bool image_panel_uv_poll(const bContext *C, PanelType *UNUSED(pt))
{
  SpaceImage *sima = CTX_wm_space_image(C);
  if (sima->mode != SI_MODE_UV) {
    return false;
  }
  Object *obedit = CTX_data_edit_object(C);
  return ED_uvedit_test(obedit);
}

static void image_panel_uv(const bContext *C, Panel *panel)
{
  uiBlock *block;

  block = uiLayoutAbsoluteBlock(panel->layout);
  UI_block_func_handle_set(block, do_uvedit_vertex, NULL);

  uvedit_vertex_buttons(C, block);
}

void ED_uvedit_buttons_register(ARegionType *art)
{
  PanelType *pt;

  pt = MEM_callocN(sizeof(PanelType), "spacetype image panel uv");
  strcpy(pt->idname, "IMAGE_PT_uv");
  strcpy(pt->label, N_("UV Vertex")); /* XXX C panels unavailable through RNA bpy.types! */
  /* Could be 'Item' matching 3D view, avoid new tab for two buttons. */
  strcpy(pt->category, "Image");
  pt->draw = image_panel_uv;
  pt->poll = image_panel_uv_poll;
  BLI_addtail(&art->paneltypes, pt);
}
