;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC Sucursal en España SL

(ns common-tests.geom-shapes-common-test
  (:require
   [app.common.geom.matrix :as gmt]
   [app.common.geom.point :as gpt]
   [app.common.geom.rect :as grc]
   [app.common.geom.shapes.common :as gco]
   [app.common.math :as mth]
   [clojure.test :as t]))

(t/deftest points->center-test
  (t/testing "Center of a unit square"
    (let [points [(gpt/point 0 0) (gpt/point 10 0)
                  (gpt/point 10 10) (gpt/point 0 10)]
          center (gco/points->center points)]
      (t/is (mth/close? 5.0 (:x center)))
      (t/is (mth/close? 5.0 (:y center)))))

  (t/testing "Center of a rectangle"
    (let [points [(gpt/point 0 0) (gpt/point 20 0)
                  (gpt/point 20 10) (gpt/point 0 10)]
          center (gco/points->center points)]
      (t/is (mth/close? 10.0 (:x center)))
      (t/is (mth/close? 5.0 (:y center)))))

  (t/testing "Center of a translated square"
    (let [points [(gpt/point 100 200) (gpt/point 150 200)
                  (gpt/point 150 250) (gpt/point 100 250)]
          center (gco/points->center points)]
      (t/is (mth/close? 125.0 (:x center)))
      (t/is (mth/close? 225.0 (:y center))))))

(t/deftest shape->center-test
  (t/testing "Center from shape selrect (proper rect record)"
    (let [shape {:selrect (grc/make-rect 10 20 100 50)}
          center (gco/shape->center shape)]
      (t/is (mth/close? 60.0 (:x center)))
      (t/is (mth/close? 45.0 (:y center))))))

(t/deftest transform-points-test
  (t/testing "Transform with identity matrix leaves points unchanged"
    (let [points [(gpt/point 0 0) (gpt/point 10 0)
                  (gpt/point 10 10) (gpt/point 0 10)]
          result (gco/transform-points points (gmt/matrix))]
      (doseq [[p r] (map vector points result)]
        (t/is (mth/close? (:x p) (:x r)))
        (t/is (mth/close? (:y p) (:y r))))))

  (t/testing "Transform with translation matrix"
    (let [points [(gpt/point 0 0) (gpt/point 10 0)
                  (gpt/point 10 10) (gpt/point 0 10)]
          mtx    (gmt/translate-matrix (gpt/point 5 10))
          result (gco/transform-points points mtx)]
      (t/is (mth/close? 5.0 (:x (first result))))
      (t/is (mth/close? 10.0 (:y (first result))))))

  (t/testing "Transform around a center point"
    (let [points [(gpt/point 0 0) (gpt/point 10 0)
                  (gpt/point 10 10) (gpt/point 0 10)]
          center (gco/points->center points)
          mtx    (gmt/scale-matrix (gpt/point 2 2))
          result (gco/transform-points points center mtx)]
      ;; Scaling around center (5,5) by 2x: (0,0)→(-5,-5)
      (t/is (mth/close? -5.0 (:x (first result))))
      (t/is (mth/close? -5.0 (:y (first result))))))

  (t/testing "Transform with nil matrix returns points unchanged"
    (let [points [(gpt/point 1 2) (gpt/point 3 4)]
          result (gco/transform-points points nil)]
      (t/is (= points result))))

  (t/testing "Transform empty points returns empty"
    (let [result (gco/transform-points [] (gmt/matrix))]
      (t/is (= [] result)))))

(t/deftest invalid-geometry?-test
  (t/testing "Valid geometry is not invalid"
    (let [shape {:selrect (grc/make-rect 0 0 100 50)
                 :points [(gpt/point 0 0) (gpt/point 100 0)
                          (gpt/point 100 50) (gpt/point 0 50)]}]
      (t/is (not (gco/invalid-geometry? shape)))))

  (t/testing "NaN x in selrect is invalid"
    (let [selrect (grc/make-rect 0 0 100 50)
          selrect (assoc selrect :x ##NaN)
          shape   {:selrect selrect
                   :points [(gpt/point 0 0) (gpt/point 100 0)
                            (gpt/point 100 50) (gpt/point 0 50)]}]
      (t/is (true? (gco/invalid-geometry? shape)))))

  (t/testing "NaN in points is invalid"
    (let [shape {:selrect (grc/make-rect 0 0 100 50)
                 :points [(gpt/point ##NaN 0) (gpt/point 100 0)
                          (gpt/point 100 50) (gpt/point 0 50)]}]
      (t/is (true? (gco/invalid-geometry? shape))))))

(t/deftest shape->points-test
  (t/testing "Identity transform uses reconstructed points from corners"
    (let [points [(gpt/point 10 20) (gpt/point 40 20)
                  (gpt/point 40 60) (gpt/point 10 60)]
          shape  {:transform (gmt/matrix) :points points}
          result (gco/shape->points shape)]
      (t/is (= 4 (count result)))
      ;; p0 and p2 are used to reconstruct p1 and p3
      (t/is (mth/close? 10.0 (:x (nth result 0))))
      (t/is (mth/close? 20.0 (:y (nth result 0))))
      (t/is (mth/close? 40.0 (:x (nth result 2))))
      (t/is (mth/close? 60.0 (:y (nth result 2))))))

  (t/testing "Non-identity transform returns points as-is"
    (let [points [(gpt/point 10 20) (gpt/point 40 20)
                  (gpt/point 40 60) (gpt/point 10 60)]
          shape  {:transform (gmt/translate-matrix (gpt/point 5 5)) :points points}
          result (gco/shape->points shape)]
      (t/is (= points result)))))

(t/deftest transform-selrect-test
  (t/testing "Transform selrect with identity matrix"
    (let [selrect (grc/make-rect 10 20 100 50)
          result  (gco/transform-selrect selrect (gmt/matrix))]
      (t/is (mth/close? 10.0 (:x result)))
      (t/is (mth/close? 20.0 (:y result)))
      (t/is (mth/close? 100.0 (:width result)))
      (t/is (mth/close? 50.0 (:height result)))))

  (t/testing "Transform selrect with translation"
    (let [selrect (grc/make-rect 0 0 100 50)
          mtx     (gmt/translate-matrix (gpt/point 10 20))
          result  (gco/transform-selrect selrect mtx)]
      (t/is (mth/close? 10.0 (:x result)))
      (t/is (mth/close? 20.0 (:y result))))))
