(function (global) {
  global.ARQ = global.ARQ || {};
  var canvas = null;
  var ctx = null;
  var imgCache = {};
  var raf = null;

  function ensureLayerContent(layer) {
    layer.contenido = layer.contenido || {};
    layer.contenido.strokes = layer.contenido.strokes || [];
    layer.contenido.texts = layer.contenido.texts || [];
    layer.contenido.shapes = layer.contenido.shapes || [];
    return layer.contenido;
  }

  function drawStroke(c, stroke) {
    var pts = stroke.puntos || [];
    if (pts.length < 2) return;
    c.save();
    c.globalAlpha = stroke.opacidad != null ? stroke.opacidad : 1;
    c.lineCap = 'round';
    c.lineJoin = 'round';
    c.lineWidth = stroke.grosor || 2;
    if (stroke.herramienta === 'borrador') {
      c.globalCompositeOperation = 'destination-out';
      c.strokeStyle = 'rgba(0,0,0,1)';
    } else {
      c.globalCompositeOperation = 'source-over';
      c.strokeStyle = stroke.color || '#000';
    }
    if (stroke.herramienta === 'marcador') {
      c.globalAlpha *= 0.35;
    }
    if (stroke.herramienta === 'pincel_suave') {
      c.shadowBlur = (stroke.grosor || 2) * 0.8;
      c.shadowColor = stroke.color || '#000';
    }
    c.beginPath();
    c.moveTo(pts[0].x, pts[0].y);
    var smooth = stroke.suavizado != null ? stroke.suavizado : 4;
    for (var i = 1; i < pts.length - 1; i++) {
      var p0 = pts[i], p1 = pts[i + 1];
      if (smooth > 0 && i < pts.length - 1) {
        var mx = (p0.x + p1.x) / 2, my = (p0.y + p1.y) / 2;
        c.quadraticCurveTo(p0.x, p0.y, mx, my);
      } else {
        c.lineTo(p0.x, p0.y);
      }
    }
    if (pts.length > 1) c.lineTo(pts[pts.length - 1].x, pts[pts.length - 1].y);
    c.stroke();
    c.restore();
  }

  function drawShape(c, s) {
    c.save();
    c.globalAlpha = s.opacidad != null ? s.opacidad : 1;
    c.strokeStyle = s.color || '#000';
    c.fillStyle = s.fill || 'transparent';
    c.lineWidth = s.grosor || 2;
    if (s.tipo === 'rect') {
      if (s.fill && s.fill !== 'transparent') c.fillRect(s.x, s.y, s.w, s.h);
      c.strokeRect(s.x, s.y, s.w, s.h);
    } else if (s.tipo === 'circulo') {
      c.beginPath();
      c.arc(s.cx, s.cy, s.r, 0, Math.PI * 2);
      if (s.fill && s.fill !== 'transparent') c.fill();
      c.stroke();
    } else if (s.tipo === 'linea' || s.tipo === 'flecha') {
      c.beginPath();
      c.moveTo(s.x1, s.y1);
      c.lineTo(s.x2, s.y2);
      c.stroke();
    }
    c.restore();
  }

  function drawText(c, t) {
    c.save();
    c.globalCompositeOperation = 'source-over';
    c.font = (t.fontSize || 24) + 'px ' + (t.fontFamily || 'Segoe UI, sans-serif');
    c.fillStyle = t.color || '#000';
    c.globalAlpha = t.opacidad != null ? t.opacidad : 1;
    c.textBaseline = 'alphabetic';
    if (t.rotation) {
      c.translate(t.x, t.y);
      c.rotate((t.rotation * Math.PI) / 180);
      c.fillText(t.text || '', 0, 0);
    } else {
      c.fillText(t.text || '', t.x, t.y);
    }
    c.restore();
  }

  function loadImage(src, cb) {
    if (!src) return cb(null);
    if (imgCache[src] && imgCache[src].complete) return cb(imgCache[src]);
    var im = new Image();
    im.crossOrigin = 'anonymous';
    im.onload = function () {
      imgCache[src] = im;
      cb(im);
    };
    im.onerror = function () { cb(null); };
    im.src = src;
  }

  function drawTriangle(ctx, im, s0x, s0y, s1x, s1y, s2x, s2y, d0, d1, d2) {
    var denom = s0x * (s1y - s2y) + s1x * (s2y - s0y) + s2x * (s0y - s1y);
    if (Math.abs(denom) < 1e-10) return;
    var m11 = (d0.x * (s1y - s2y) + d1.x * (s2y - s0y) + d2.x * (s0y - s1y)) / denom;
    var m12 = (d0.x * (s2x - s1x) + d1.x * (s0x - s2x) + d2.x * (s1x - s0x)) / denom;
    var m21 = (d0.y * (s1y - s2y) + d1.y * (s2y - s0y) + d2.y * (s0y - s1y)) / denom;
    var m22 = (d0.y * (s2x - s1x) + d1.y * (s0x - s2x) + d2.y * (s1x - s0x)) / denom;
    var dx = d0.x - m11 * s0x - m12 * s0y;
    var dy = d0.y - m21 * s0x - m22 * s0y;
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(d0.x, d0.y);
    ctx.lineTo(d1.x, d1.y);
    ctx.lineTo(d2.x, d2.y);
    ctx.closePath();
    ctx.clip();
    ctx.transform(m11, m21, m12, m22, dx, dy);
    ctx.drawImage(im, 0, 0);
    ctx.restore();
  }

  function drawImageQuad(ctx, im, corners) {
    var w = im.width;
    var h = im.height;
    drawTriangle(ctx, im, 0, 0, w, 0, 0, h, corners[0], corners[1], corners[3]);
    drawTriangle(ctx, im, w, 0, w, h, 0, h, corners[1], corners[2], corners[3]);
  }

  function drawLayerContent(ctx, layer, sheet) {
    var w = sheet.ancho || 1920;
    var h = sheet.alto || 1080;
    var cont = ensureLayerContent(layer);
    if (cont.imageSrc && imgCache[cont.imageSrc] && imgCache[cont.imageSrc].complete) {
      var im = imgCache[cont.imageSrc];
      var tr = cont.imageTransform || { x: 0, y: 0, w: w, h: h, rotation: 0 };
      ctx.save();
      if (tr.corners && tr.corners.length === 4) {
        drawImageQuad(ctx, im, tr.corners);
      } else {
        ctx.translate(tr.x + tr.w / 2, tr.y + tr.h / 2);
        ctx.rotate(((tr.rotation || 0) * Math.PI) / 180);
        ctx.drawImage(im, -tr.w / 2, -tr.h / 2, tr.w, tr.h);
      }
      ctx.restore();
    }
    (cont.strokes || []).forEach(function (s) { drawStroke(ctx, s); });
    (cont.shapes || []).forEach(function (s) { drawShape(ctx, s); });
    (cont.texts || []).forEach(function (t) { drawText(ctx, t); });
  }

  ARQ.canvasEngine = {
    init: function (el) {
      canvas = el;
      ctx = canvas.getContext('2d');
    },
    getCanvas: function () { return canvas; },
    getContext: function () { return ctx; },
    getImgCache: function () { return imgCache; },
    loadImage: loadImage,

    /**
     * Precarga imágenes de las capas indicadas (o todas las visibles del sheet).
     * @returns {Promise<void>}
     */
    preloadLayerImages: function (sheet, layers) {
      var pending = [];
      (layers || []).forEach(function (layer) {
        if (!layer || !layer.visible) return;
        var cont = layer.contenido || {};
        if (!cont.imageSrc) return;
        var src = cont.imageSrc;
        if (imgCache[src] && imgCache[src].complete) return;
        pending.push(new Promise(function (resolve) {
          loadImage(src, function () { resolve(); });
        }));
      });
      return Promise.all(pending).then(function () {});
    },

    /**
     * Dibuja capas en orden (array ya ordenado por z_index).
     * @param {CanvasRenderingContext2D} c
     * @param {{ancho:number,alto:number}} sheet
     * @param {Array} layersOrdered capas ordenadas por z
     * @param {{includeBackground?:boolean}} opts
     */
    renderLayersToContext: function (c, sheet, layersOrdered, opts) {
      opts = opts || {};
      var w = sheet.ancho || 1920;
      var h = sheet.alto || 1080;
      if (opts.includeBackground) {
        c.fillStyle = sheet.fondo || '#ffffff';
        c.fillRect(0, 0, w, h);
      }
      (layersOrdered || []).forEach(function (layer) {
        if (!layer.visible) return;
        c.save();
        c.globalAlpha = layer.opacidad != null ? layer.opacidad : 1;
        drawLayerContent(c, layer, sheet);
        c.restore();
      });
    },

    requestRedraw: function () {
      if (raf) return;
      raf = requestAnimationFrame(function () {
        raf = null;
        ARQ.canvasEngine.redraw();
      });
    },
    redraw: function () {
      if (!ctx || !canvas) return;
      var project = ARQ.store.getProject();
      var sheet = ARQ.utils.activeSheet(project);
      if (!sheet) return;
      var w = sheet.ancho || 1920;
      var h = sheet.alto || 1080;
      if (canvas.width !== w || canvas.height !== h) {
        canvas.width = w;
        canvas.height = h;
      }
      ctx.fillStyle = sheet.fondo || '#ffffff';
      ctx.fillRect(0, 0, w, h);
      var layers = ARQ.utils.sortLayers(sheet.capas || []);
      layers.forEach(function (layer) {
        if (!layer.visible) return;
        ctx.save();
        ctx.globalAlpha = layer.opacidad != null ? layer.opacidad : 1;
        var cont = ensureLayerContent(layer);
        if (cont.imageSrc && imgCache[cont.imageSrc] && imgCache[cont.imageSrc].complete) {
          var im = imgCache[cont.imageSrc];
          var tr = cont.imageTransform || { x: 0, y: 0, w: w, h: h, rotation: 0 };
          ctx.save();
          if (tr.corners && tr.corners.length === 4) {
            drawImageQuad(ctx, im, tr.corners);
          } else {
            ctx.translate(tr.x + tr.w / 2, tr.y + tr.h / 2);
            ctx.rotate(((tr.rotation || 0) * Math.PI) / 180);
            ctx.drawImage(im, -tr.w / 2, -tr.h / 2, tr.w, tr.h);
          }
          ctx.restore();
        } else if (cont.imageSrc) {
          loadImage(cont.imageSrc, function () {
            ARQ.canvasEngine.requestRedraw();
          });
        }
        (cont.strokes || []).forEach(function (s) { drawStroke(ctx, s); });
        (cont.shapes || []).forEach(function (s) { drawShape(ctx, s); });
        (cont.texts || []).forEach(function (t) { drawText(ctx, t); });
        ctx.restore();
      });
      var live = ARQ.pointer && ARQ.pointer.getLiveStroke ? ARQ.pointer.getLiveStroke() : null;
      if (live && live.puntos && live.puntos.length >= 2) {
        drawStroke(ctx, live);
      }
      if (ARQ.transform && ARQ.transform.isActive && ARQ.transform.isActive()) {
        var tl = ARQ.store.activeLayer();
        if (tl && tl.id === ARQ.transform.layerId && tl.contenido && tl.contenido.imageSrc &&
            ARQ.guards.canDrawOnLayer(tl)) {
          var oc = ARQ.transform.getCorners(tl);
          ARQ.transform.drawOverlay(ctx, oc);
        }
      }
      if (ARQ.selection && ARQ.selection.drawOverlay) {
        ARQ.selection.drawOverlay(ctx, sheet);
      }
      if (ARQ.selection && ARQ.selection.shouldAnimate && ARQ.selection.shouldAnimate(sheet)) {
        requestAnimationFrame(function () {
          ARQ.canvasEngine.redraw();
        });
      }
    },
  };
})(typeof window !== 'undefined' ? window : this);
