(function (global) {
  global.ARQ = global.ARQ || {};
  /**
   * Flood fill RGBA con tolerancia (suma de diferencias por canal).
   */
  ARQ.floodFill = {
    apply: function (imageData, width, height, startX, startY, fr, fg, fb, fa, tolerance, contiguous) {
      var data = imageData.data;
      var w = width | 0;
      var h = height | 0;
      var ix = Math.floor(startX);
      var iy = Math.floor(startY);
      if (ix < 0 || iy < 0 || ix >= w || iy >= h) return 0;
      var startIdx = (iy * w + ix) * 4;
      var tr = data[startIdx];
      var tg = data[startIdx + 1];
      var tb = data[startIdx + 2];
      var ta = data[startIdx + 3];
      var tol = tolerance != null ? tolerance : 32;

      function matchAt(px, py) {
        var idx = (py * w + px) * 4;
        var r = data[idx];
        var g = data[idx + 1];
        var b = data[idx + 2];
        var a = data[idx + 3];
        return Math.abs(r - tr) + Math.abs(g - tg) + Math.abs(b - tb) + Math.abs(a - ta) <= tol;
      }

      if (contiguous === false) {
        var filledNc = 0;
        var px;
        var py;
        for (py = 0; py < h; py++) {
          for (px = 0; px < w; px++) {
            if (!matchAt(px, py)) continue;
            var idx2 = (py * w + px) * 4;
            data[idx2] = fr;
            data[idx2 + 1] = fg;
            data[idx2 + 2] = fb;
            data[idx2 + 3] = fa;
            filledNc++;
          }
        }
        return filledNc;
      }

      var visited = new Uint8Array(w * h);
      var qx = [];
      var qy = [];
      var qh = 0;
      qx.push(ix);
      qy.push(iy);
      visited[iy * w + ix] = 1;
      var filled = 0;

      while (qh < qx.length) {
        var x = qx[qh];
        var y = qy[qh];
        qh++;
        var idx = (y * w + x) * 4;
        data[idx] = fr;
        data[idx + 1] = fg;
        data[idx + 2] = fb;
        data[idx + 3] = fa;
        filled++;

        var dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
        var d;
        for (d = 0; d < 4; d++) {
          var nx = x + dirs[d][0];
          var ny = y + dirs[d][1];
          if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
          var vi = ny * w + nx;
          if (visited[vi]) continue;
          if (!matchAt(nx, ny)) continue;
          visited[vi] = 1;
          qx.push(nx);
          qy.push(ny);
        }
      }
      return filled;
    },

    /**
     * Máscara de selección por similitud de color (no altera imageData).
     * @returns {Uint8Array} longitud w*h, 1 = seleccionado
     */
    buildSelectionMask: function (imageData, width, height, startX, startY, tolerance, contiguous) {
      var data = imageData.data;
      var w = width | 0;
      var h = height | 0;
      var ix = Math.floor(startX);
      var iy = Math.floor(startY);
      var out = new Uint8Array(w * h);
      var tol = tolerance != null ? tolerance : 32;
      if (ix < 0 || iy < 0 || ix >= w || iy >= h) return out;
      var startIdx = (iy * w + ix) * 4;
      var tr = data[startIdx];
      var tg = data[startIdx + 1];
      var tb = data[startIdx + 2];
      var ta = data[startIdx + 3];

      function matchAt(px, py) {
        var idx = (py * w + px) * 4;
        var r = data[idx];
        var g = data[idx + 1];
        var b = data[idx + 2];
        var a = data[idx + 3];
        return Math.abs(r - tr) + Math.abs(g - tg) + Math.abs(b - tb) + Math.abs(a - ta) <= tol;
      }

      if (contiguous !== false) {
        var visited = new Uint8Array(w * h);
        var qx = [];
        var qy = [];
        var qh = 0;
        qx.push(ix);
        qy.push(iy);
        visited[iy * w + ix] = 1;
        while (qh < qx.length) {
          var x = qx[qh];
          var y = qy[qh];
          qh++;
          var vi = y * w + x;
          out[vi] = 1;
          var dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
          var d;
          for (d = 0; d < 4; d++) {
            var nx = x + dirs[d][0];
            var ny = y + dirs[d][1];
            if (nx < 0 || ny < 0 || nx >= w || ny >= h) continue;
            var v2 = ny * w + nx;
            if (visited[v2]) continue;
            if (!matchAt(nx, ny)) continue;
            visited[v2] = 1;
            qx.push(nx);
            qy.push(ny);
          }
        }
      } else {
        var px;
        var py;
        var i;
        for (py = 0; py < h; py++) {
          for (px = 0; px < w; px++) {
            if (matchAt(px, py)) out[py * w + px] = 1;
          }
        }
      }
      return out;
    },
  };
})(typeof window !== 'undefined' ? window : this);
