import ISurface from "/app/js/ifaces/ISurface.js";
import NESPalette from "/app/js/models/NESPalette.js";



class NESTileSurface extends ISurface{
  constructor(tile, palette, palindex){
    super();
    this.__tile = tile;
    this.__palette = (palette instanceof NESPalette) ? palette : null;
    this.__pi = (palindex >= 0 && palindex < 8) ? palindex : -1;
  }

  get width(){return 8;}
  get height(){return 8;}
  get length(){return 64;}

  getColor(x, y){
    var ci = this.__tile.getPixelIndex(x, y);
    if (this.__palette !== null){
      return this.__palette.get_palette_color(this.__pi, ci);
    }
    return NESPalette.Default(ci);
  }

  getColorIndex(x, y){
    return {pi:this.__pi, ci:this.__tile.getPixelIndex(x,y)};
  }
}


function clear(ctx, color, cw, ch){
  if (typeof(cw) !== 'number'){
    cw = (Math.floor(ctx.canvas.clientWidth) > 0) ? 
      Math.floor(ctx.canvas.clientWidth) : 
      Math.floor(ctx.canvas.width);
  }

  if (typeof(ch) !== 'number'){
    ch = (Math.floor(ctx.canvas.clientHeight) > 0) ?
      Math.floor(ctx.canvas.clientHeight) :
      Math.floor(ctx.canvas.height);
  }

  ctx.save();
  ctx.fillStyle = color;
  ctx.fillRect(0, 0, cw, ch);
  ctx.restore();
}


function render(surf, sx, sy, sw, sh, scale, ctx, dx, dy, palcolored){
  if (!(surf instanceof ISurface)){
    console.log("WARNING: Cannot render non-ISurface object.");
    return;
  }

  if (sx + sw > surf.width || sy + sh > surf.height){
    console.log("WARNING: Cannot render. Region is out of bounds.");
    return;
  }

  palcolored = (palcolored === true);

  var cw = (Math.floor(ctx.canvas.clientWidth) > 0) ? 
    Math.floor(ctx.canvas.clientWidth) : 
    Math.floor(ctx.canvas.width);

  var ch = (Math.floor(ctx.canvas.clientHeight) > 0) ?
    Math.floor(ctx.canvas.clientHeight) :
    Math.floor(ctx.canvas.height);

  if (cw <= 0 || ch <= 0){return;}

  clear(ctx, NESPalette.Default(4));

  ctx.save();
  var ctximg = ctx.getImageData(0, 0, cw, ch);
  var idat = ctximg.data;

  var PutPixel = (i,j,s,c) => {
    i = Math.round(i);
    j = Math.round(j);
    s = Math.ceil(s);

    var r = parseInt(c.substring(1, 3), 16);
    var g = parseInt(c.substring(3, 5), 16);
    var b = parseInt(c.substring(5, 7), 16);

    for (var y=j; y < j+s; y++){
      for (var x=i; x < i+s; x++){
        if (x >= 0 && x < cw && y >= 0 && y < ch){
          var index = (y*cw*4) + (x*4);
          idat[index] = r;
          idat[index+1] = g;
          idat[index+2] = b;
          idat[index+3] = 255;
        }
      }
    }
  };


  for (let j=sy; j < sy + sh; j++){
    var y = (j*scale) + dy;
    for (let i=sx; i < sx + sw; i++){
      var x = (i*scale) + dx;

      if (x >= 0 && x < cw && y >= 0 && y < ch){
        var color = NESPalette.Default(4);
        if (palcolored){
          color = surf.getColor(i, j);
        } else {
          var pinfo = surf.getColorIndex(i, j);
          color = (pinfo.ci >= 0) ? NESPalette.Default(pinfo.ci) : NESPalette.Default(4);
        }
        PutPixel(x,y,scale,color);
      }
    }
  } 

  ctx.putImageData(ctximg, 0, 0); 
  ctx.restore();
}



function renderToFit(surf, ctx, palcolored){
  if (!(surf instanceof ISurface)){
    console.log("WARNING: Cannot render non-ISurface object.");
    return;
  }

  palcolored = (palcolored === true);

  var cw = (Math.floor(ctx.canvas.clientWidth) > 0) ? 
    Math.floor(ctx.canvas.clientWidth) : 
    Math.floor(ctx.canvas.width);

  var ch = (Math.floor(ctx.canvas.clientHeight) > 0) ?
    Math.floor(ctx.canvas.clientHeight) :
    Math.floor(ctx.canvas.height);

  if (cw <= 0 || ch <= 0){return;}

  var scale = Math.min(
    cw/surf.width,
    ch/surf.height
  );
  var offX = Math.floor((cw - (surf.width*scale)) * 0.5);
  var offY = Math.floor((ch - (surf.height*scale)) * 0.5);

  render(surf, 0, 0, surf.width, surf.height, scale, ctx, offX, offY, palcolored);
}


function renderGridNxN(ctx, gsize, sw, sh, scale, offset, color){
  // Draw grid.
  if (scale > 0.5){
    ctx.save();
    ctx.strokeStyle = color;

    var w = sw * scale;
    var h = sh * scale;

    var length = Math.max(sw, sh);
    for (var i=0; i < length; i += gsize){
      var x = (i*scale) + offset[0];
      var y = (i*scale) + offset[1];

      if (i < sw){
        ctx.beginPath();
        ctx.moveTo(x, offset[1]);
        ctx.lineTo(x, offset[1] + h);
        ctx.stroke();
        ctx.closePath();
      }

      if (i < sh){
        ctx.beginPath();
        ctx.moveTo(offset[0], y);
        ctx.lineTo(offset[0] + w, y);
        ctx.stroke();
        ctx.closePath();
      }
    }
    ctx.restore();
  } 
}


function renderGridMxN(ctx, gw, gh, sw, sh, scale, offset, color){
  if (scale > 0.5){
    ctx.save();
    ctx.strokeStyle = color;

    var w = sw * scale;
    var h = sh * scale;

    var length = Math.max(sw, sh);
    for (let i=0; i < sw; i += gw){
      var x = (i*scale) + offset[0];
      //var y = (i*scale) + offset[1];

      if (i < sw){
        ctx.beginPath();
        ctx.moveTo(x, offset[1]);
        ctx.lineTo(x, offset[1] + h);
        ctx.stroke();
        ctx.closePath();
      }
    }

    for (let i=0; i < sh; i += gh){
      var y = (i * scale) + offset[1];

      if (i < sh){
        ctx.beginPath();
        ctx.moveTo(offset[0], y);
        ctx.lineTo(offset[0] + w, y);
        ctx.stroke();
        ctx.closePath();
      }
    }
    ctx.restore();
  }
}


function renderGridMajorMinor(ctx, mjsize, mnsize, sw, sh, scale, offset, mjcolor, mncolor){
  if (scale > 0.5){
    renderGridNxN(ctx, mnsize, sw, sh, scale, offset, mncolor);
    renderGridNxN(ctx, mjsize, sw, sh, scale, offset, mjcolor);
  }
}


export default {
  clear: clear,
  render: render,
  renderToFit: renderToFit,
  renderGridNxN: renderGridNxN,
  renderGridMxN: renderGridMxN,
  renderGridMajorMinor: renderGridMajorMinor,
  NESTileSurface: NESTileSurface
};