const { PNG } = require("pngjs");
const path = require('path');
const fs = require('fs');

/**
 * Loads an image into memory.
 * @param image Image to load
 * @returns A promise that resolves to the loaded image.
 */
const imageLoad = (image) => new Promise(resolve => {
  fs.createReadStream(image)
    .pipe(new PNG({ filterType: 4 }))
    .on("parsed", function () {
      // Normalize
      const pixels = [];
      for(let y = 0; y < this.height; y++) {
        for(let x = 0; x < this.width; x++) {
          const idx = (this.width * y + x) << 2;
          const r = this.data[idx];
          const g = this.data[idx + 1];
          const b = this.data[idx + 2];
          const a = this.data[idx + 3];

          pixels.push({ r, g, b, a });
        }
      }
      resolve({ pixels, width: this.width, height: this.height });
    })
  ;
});

/**
 * Writes an image to an output file.
 * @param image Image to write.
 * @param file File to write to.
 * @returns A promise that, when resolved, has saved the image.
 */
const imageWrite = (image, file) => new Promise(resolve => {
  const png = new PNG({ width: image.width, height: image.height });
  png.width = image.width;
  png.height = image.height;
  
  for(let y = 0; y < image.height; y++) {
    for(let x = 0; x < image.width; x++) {
      const i = (image.width * y + x);
      const idx = i << 2;

      const pixel = image.pixels[i];
      png.data[idx] = pixel.r;
      png.data[idx + 1] = pixel.g;
      png.data[idx + 2] = pixel.b;
      png.data[idx + 3] = pixel.a;
    }
  }

  png.pack().pipe(fs.createWriteStream(file))
    .on('close', () => resolve(true))
  ;
});

/**
 * Creates a blank image
 * @param width Width of the image.
 * @param height Height of the image.
 * @param fill Optional pixel to fill with, defaults to 0,0,0,0
 * @returns The newly created image.
 */
const imageCreate = (width, height, pixel) => {
  if(!pixel || !pixel.r) pixel = { r:0, g:0, b:0, a:0 };
  const pixels = [];
  for(let i = 0; i < width * height; i++) pixels.push({ ...pixel });
  return { pixels, width, height };
}

/**
 * Copies an area of a source image into a target image.
 * @param target Target image to copy into.
 * @param source Source image to copy from.
 * @param tx Target X position to copy into
 * @param ty Target Y position to copy into
 * @param sub Optional source area to use, defined as { x, y, width, height }.
 */
const imageCopy = (target, source, tx, ty, sub) => {
  if(!sub) sub = { x: 0, y: 0, width: source.width, height: source.height };

  for(let x = sub.x; x < sub.x+sub.width; x++) {
    for(let y = sub.y; y < sub.y+sub.height; y++) {
      let absX = x - sub.x + tx;
      let absY = y - sub.y + ty;
      if(absX > target.width || absY > target.height) continue;
      let ti = absY * target.width + absX;
      let si = y * source.width + x;
      target.pixels[ti] = { ...source.pixels[si] };
    }
  }
}

module.exports = {
  imageWrite,
  imageCreate,
  imageLoad,
  imageCopy
}