local DEFAULT_SIZE = 8;
local DEFAULT_CHAR = '#';
local RANDOM_CHARS = { '#', '*', '+', 'x', '@', '=' };

local function firstChar(value)
  value = tostring(value or '');
  if value == '' then return nil; end
  return string.sub(value, 1, 1);
end

local function parsePositiveInteger(value, name)
  local number = tonumber(value);
  if not number or number < 1 or number ~= math.floor(number) then
    return nil, name .. ' doit etre un entier positif';
  end
  return number;
end

local function parseNonNegativeNumber(value, name)
  local number = tonumber(value);
  if not number or number < 0 then
    return nil, name .. ' doit etre un nombre positif ou nul';
  end
  return number;
end

local function clamp(value, minValue, maxValue)
  if value < minValue then return minValue; end
  if value > maxValue then return maxValue; end
  return value;
end

local function createCarre(opts)
  opts = opts or {};
  local random = opts.random or math.random;

  local api = {};

  function api.parseArgs(args)
    args = args or {};
    local config = {
      size = DEFAULT_SIZE,
      char = DEFAULT_CHAR,
      fill = false,
      random = false,
      count = 1,
      delay = 0,
      clear = false,
      explicit = {},
    };

    local i = 1;
    while i <= (args.n or #args) do
      local arg = args[i];

      if arg == '-size' then
        local value, err = parsePositiveInteger(args[i + 1], '-size');
        if not value then return nil, err; end
        config.size = value;
        config.explicit.size = true;
        i = i + 1;
      elseif arg == '-x' then
        local value, err = parsePositiveInteger(args[i + 1], '-x');
        if not value then return nil, err; end
        config.x = value;
        config.explicit.x = true;
        i = i + 1;
      elseif arg == '-y' then
        local value, err = parsePositiveInteger(args[i + 1], '-y');
        if not value then return nil, err; end
        config.y = value;
        config.explicit.y = true;
        i = i + 1;
      elseif arg == '-char' then
        local value = firstChar(args[i + 1]);
        if not value then return nil, '-char doit recevoir un caractere'; end
        config.char = value;
        config.explicit.char = true;
        i = i + 1;
      elseif arg == '-fill' then
        config.fill = true;
      elseif arg == '-random' then
        config.random = true;
      elseif arg == '-count' then
        local value, err = parsePositiveInteger(args[i + 1], '-count');
        if not value then return nil, err; end
        config.count = value;
        i = i + 1;
      elseif arg == '-delay' then
        local value, err = parseNonNegativeNumber(args[i + 1], '-delay');
        if not value then return nil, err; end
        config.delay = value;
        i = i + 1;
      elseif arg == '-clear' then
        config.clear = true;
      else
        return nil, 'option inconnue: ' .. tostring(arg);
      end

      i = i + 1;
    end

    return config;
  end

  function api.computeSquare(config, width, height)
    width = math.max(1, tonumber(width) or 1);
    height = math.max(1, tonumber(height) or 1);
    config = config or {};

    local explicit = config.explicit or {};
    local maxSize = math.max(1, math.min(width, height));
    local size = config.size or DEFAULT_SIZE;
    local char = config.char or DEFAULT_CHAR;

    if config.random then
      if not explicit.size then
        size = random(1, maxSize);
      end
      if not explicit.char then
        char = RANDOM_CHARS[random(1, #RANDOM_CHARS)];
      end
    end

    size = clamp(size, 1, maxSize);

    local maxX = math.max(1, width - size + 1);
    local maxY = math.max(1, height - size + 1);
    local x = config.x;
    local y = config.y;

    if config.random and not explicit.x then
      x = random(1, maxX);
    elseif not x then
      x = math.floor((width - size) / 2) + 1;
    end

    if config.random and not explicit.y then
      y = random(1, maxY);
    elseif not y then
      y = math.floor((height - size) / 2) + 1;
    end

    return {
      x = clamp(x, 1, maxX),
      y = clamp(y, 1, maxY),
      size = size,
      char = firstChar(char) or DEFAULT_CHAR,
      fill = config.fill == true,
    };
  end

  function api.drawSquare(termLib, square)
    for row = 1, square.size do
      termLib.setCursorPos(square.x, square.y + row - 1);

      for column = 1, square.size do
        local isBorder = row == 1 or row == square.size or column == 1 or column == square.size;
        if square.fill or isBorder then
          termLib.write(square.char);
        else
          termLib.write(' ');
        end
      end
    end
  end

  return api;
end

return createCarre;
