-- tkz_elements_path.lua
-- date 2025/05/25
-- version 4.00c
-- Copyright 2025  Alain Matthes
-- This work may be distributed and/or modified under the
-- conditions of the LaTeX Project Public License, either version 1.3
-- of this license or (at your option) any later version.
-- The latest version of this license is in
-- http://www.latex-project.org/lppl.txt
-- and version 1.3 or later is part of all distributions of LaTeX
-- version 2005/12/01 or later.
-- This work has the LPPL maintenance status “maintained”.
-- The Current Maintainer of this work is Alain Matthes.

path = {}
path.__index = path

function path:new(data)
  if data ~= nil and type(data) ~= "table" then
    error("Expected table or nil in path constructor, got " .. type(data), 2)
  end
  return setmetatable(data or {}, self)
end

setmetatable(path, {
  __call = function(cls, ...)
    return cls:new(...)
  end
})


function path.__add(p1, p2)
  local result = {}
  for _, pt in ipairs(p1) do result[#result + 1] = pt end
  for _, pt in ipairs(p2) do result[#result + 1] = pt end
  return path:new(result)
end

function path.__unm(p)
  local result = {}
  for i = #p, 1, -1 do result[#result + 1] = p[i] end
  return path:new(result)
end

function path.__sub(p1, p2)
  return p1 + (-p2)
end

function path:__tostring()
  return "path: { " .. table.concat(self, " , ") .. " }"
end


function path:add_point(z, decimals)
  table.insert(self, utils.format_point(z, decimals))
end

function path:concat(sep)
  sep = sep or " "
  return table.concat(self, sep)
end

function path:add_pair_to_path(z1, z2, decimals)
  decimals = decimals or 5
  local x1 = utils.format_coord(z1.re, decimals)
  local y1 = utils.format_coord(z1.im, decimals)
  local x2 = utils.format_coord(z2.re, decimals)
  local y2 = utils.format_coord(z2.im, decimals)
  local pt = string.format("%s/%s/%s/%s", x1, y1, x2, y2)
  table.insert(self, pt)
end
path.add_pair = path.add_pair_to_path

function path:show()
  for _, pt in ipairs(self) do tex.print(pt) end
end

function path:copy()
  local c = {}
  for i, pt in ipairs(self) do
    c[i] = pt
  end
  return path:new(c)
end


-- Translation
function path:translate(dx, dy)
  local moved = {}
  for _, pt in ipairs(self) do
    local x, y = utils.parse_point(pt)
    local newx = x + dx
    local newy = y + dy
    table.insert(moved, string.format("(%s,%s)", checknumber(newx), checknumber(newy)))
  end
  return path:new(moved)
end

-- Homothétie de centre (cx, cy), rapport k
function path:homothety(center, k)
  local scaled = {}
  for _, pt in ipairs(self) do
    local x, y = utils.parse_point(pt)
    local newx = center.re + k * (x - center.re)
    local newy = center.im + k * (y - center.im)
    table.insert(scaled, string.format("(%s,%s)", utils.checknumber(newx), utils.checknumber(newy)))
  end
  return path:new(scaled)
end

-- Rotation autour de (cx, cy) d'un angle theta en radians
function path:rotate(center, theta)
  local rotated = {}
  local cos_t = math.cos(theta)
  local sin_t = math.sin(theta)
  for _, pt in ipairs(self) do
    local x, y = utils.parse_point(pt)
    local dx, dy = x - center.re, y - center.im
    local newx = center.re + dx * cos_t - dy * sin_t
    local newy = center.im + dx * sin_t + dy * cos_t
    table.insert(rotated, string.format("(%s,%s)", utils.checknumber(newx), utils.checknumber(newy)))
  end
  return path:new(rotated)
end

-- Fermeture
function path:close()
  if #self == 0 then return self end

  local x1, y1 = utils.parse_point(self[1])
  local x2, y2 = utils.parse_point(self[#self])

  if x1 ~= x2 or y1 ~= y2 then
    local closed = {}
    for i, pt in ipairs(self) do
      closed[i] = pt
    end
    closed[#closed + 1] = self[1]
    return path:new(closed)
  else
    return self
  end
end


-- Sous-chemin
function path:sub(i1, i2)
  local subp = {}
  for i = i1 or 1, i2 or #self do
    subp[#subp + 1] = self[i]
  end
  return path:new(subp)
end

return path


