const path = require('path');
const fs = require('fs');
const chalk = require('chalk');
const sharp = require('sharp');
const { execSync } = require('child_process');



const errTxt = txt => chalk.red(txt);
//const sucessTxt = txt => chalk.green(txt);
const warnTxt = txt => chalk.yellow(txt);
const log = txt => console.log(txt);

const recurse = ({
  src,
  destination = false,
  process = () => console.log('add a process property, type: function'),
  mkdir = false, // make a dir if it doesn't exist
  checkExists = true // checks to see if file exists and only processes if it doesn't
}) => {
  const files = fs.readdirSync(path.resolve(src));

  files.map(file => {
    const srcPath = path.resolve(src, file);
    const destPath = path.resolve(destination, file);
    const srcStats = fs.lstatSync(srcPath);
    const srcIsDir = srcStats.isDirectory();
    const destExists = fs.existsSync(destPath);

    if (!srcIsDir && path.basename(srcPath).startsWith('_')) return log(`❕  Ignoring ${errTxt(file)}`);

    if (checkExists && destination && destExists) return log(`❕  ${errTxt(file)} already exists at ${warnTxt(destPath)}`);

    if (destination && !destExists && srcIsDir) {
      log(`✔️  ${warnTxt('Creating')} ${warnTxt(file)} at ${destination}`);
      fs.mkdirSync(destPath);
      recurse({ src: srcPath, destination: destPath, process, mkdir, checkExists });
    } else if (destination && destExists && srcIsDir) {
      recurse({ src: srcPath, destination: destPath, process, mkdir, checkExists });
    }

    if ((!checkExists || !destExists) && !srcIsDir) {
      //log(`✔️  ${warnTxt('Processing')} ${warnTxt(file)} in ${destination}`);
      if (typeof process === 'function') {
        process({ srcPath, destPath, destination, file })
      }
    }

    return true;
  });
};

const execute = (cmd, options = { startMsg: false, endMsg: false, error: () => { } }) => {
  const { startMsg, endMsg, error } = options
  try {
    if (startMsg) console.log(`\n${startMsg}`);
    execSync(cmd, { cwd: process.cwd(), stdio: 'inherit', shell: true }); //stdio: 'inherit', //shell: true 
    if (endMsg) console.log(`\n${endMsg}`);
  } catch (err) {
    if (error) error(err);
  }
}

function generateTiles(source, destination, format) {
  const extensions = ['.jpg', '.tif', '.tiff', '.png', '.jpeg']; // only process these filetypes
  const { name, ext } = path.parse(source);
  if (!extensions.includes(ext)) return undefined;
  const tilePath = path.resolve(destination, name);

  sharp.cache( { items: 200, files: 1000, memory: 200 } );

  if (name.startsWith('_')) {
    console.log('...ignoring', source)
    return undefined;
  }

  if (['zoomify', 'zoom'].includes(format)) return generateTilesZoomify(source, tilePath);
  return generateVipsTilesDeepzoom(source, tilePath); // default
}

function generateTilesDeepzoom(source, tilePath) {
  console.log(source, tilePath);
  sharp(source)
    .toFormat('jpg')
    .tile({
      size: 256, // deault 256
      overlap: 0,
      angle: 0,
      container: 'fs', // fs | zip - fs = filesystem
      //depth: onepixel | onetile | one - default based on layout
      layout: 'dz', // deepzoom
    })
    .toFile(`${tilePath}-deepzoom`, (err, info) => {
      if (err) return;
      const outPath = `${tilePath}-deepzoom_files`;
      const dirs = fs.readdirSync(outPath).filter(f => fs.statSync(path.resolve(outPath, f)));
      const maxZoom = Math.max(...dirs);

      const json = JSON.stringify({
        ...info,
        maxZoom,
        thumbLevel: 7,
        minZoom: 9
      }, null, 2);

      fs.writeFileSync(`${tilePath}-deepzoom.json`, json, 'utf8');
    })
}

function generateVipsTilesDeepzoom(source, tilePath = './dz_tilesets') {
  const getPropsPat = new RegExp(/ .*?="(.*?)"/gi);
  const cliPath = path.parse(process.argv[1])
  const vips = path.resolve(cliPath.dir, 'vips-dev-8.8/bin/vips.exe');
  const { name } = path.parse(source);
  const { dir: out } = path.parse(tilePath);
  const vipOut = path.resolve(out, `${name}-deepzoom`);
  const propPath = path.join(out, `${name}-deepzoom.dzi`);
  const outPath = path.resolve(out, `${name}-deepzoom_files`);

  if(!fs.existsSync(source)) return console.log('source file not found');
  execute(`"${vips}" dzsave "${source}" "${vipOut}" --tile-size 254 --overlap 1 --properties`);
  if(!fs.existsSync(propPath)) return console.log('properties file not found');

  const propertiesTxt = fs.readFileSync(propPath, 'utf8'); 
  const dirs = fs.readdirSync(outPath).filter(f => (fs.statSync(path.resolve(outPath, f)).isDirectory()));
  const maxZoom = Math.max(...dirs);

  const properties = {
    maxZoom,
    thumbLevel: 7,
    minZoom: 9
  };

  propertiesTxt.match(getPropsPat).map(i => {
    const str = i.replace(/"/gi, "").toLowerCase().trim();
    const [ key, val] = str.split("=");
    properties[key] = val;
  });

  const json = JSON.stringify(properties, null, 2);
  fs.writeFileSync(path.join(out, `${name}-deepzoom.json`), json, 'utf8');
  console.log(`FILE: ${name} :`, json);    
}


// function __generateVipsTilesDeepzoom(source, tilePath = './dz_tilesets') {
//   const getPropsPat = new RegExp(/ .*?="(.*?)"/gi);
//   const cliPath = path.parse(process.argv[1])
//   const vips = path.resolve(cliPath.dir, 'vips-dev-8.8/bin/vips.exe');
//   const { name, ext, dir } = path.parse(source);
//   const out = path.resolve(tilePath, `${name}-deepzoom`);
//   const propPath = path.join(dir, `dz_tilesets/${name}-deepzoom.dzi`);
//   const outPath = path.resolve(dir, `dz_tilesets/${name}-deepzoom_files`);

//   //const propertiesPath = path.resolve(path.dirname(inPath), basename+'');

//   if(!fs.existsSync(source)) return console.log('source file not found');

//   execute(`"${vips}" dzsave "${source}" dz_tilesets/${name}-deepzoom --properties`); 

//   if(!fs.existsSync(propPath)) return console.log('properties file not found');

//   const propertiesTxt = fs.readFileSync(propPath, 'utf8'); 
//   const dirs = fs.readdirSync(outPath).filter(f => (fs.statSync(path.resolve(outPath, f)).isDirectory()));
//   const maxZoom = Math.max(...dirs);

//   const properties = {
//     maxZoom,
//     thumbLevel: 7,
//     minZoom: 9
//   };

//   propertiesTxt.match(getPropsPat).map(i => {
//     const str = i.replace(/"/gi, "").toLowerCase().trim();
//     const [ key, val] = str.split("=");
//     properties[key] = val;
//   });

//   const json = JSON.stringify(properties, null, 2);
//   fs.writeFileSync(path.join(out, `${name}-deepzoom.json`), json, 'utf8');
//   console.log(json);  
// }



function generateTilesZoomify(source, tilePath) {
  sharp(source)
    .toFormat('jpg')
    .tile({
      size: 256,
      overlap: 0,
      angle: 0,
      container: 'fs', // fs | zip - fs = filesystem
      layout: 'zoomify', // dz || zoomify || google
    })
    .toFile(`${tilePath}-zoomify`, (err, info) => {
      // no need to generate file
    })
}

function sanitiseName(source) {
  const { name, ext, dir } = path.parse(source);
  const saneName = name.replace(' ', '-').replace(/[,]/, '').toLowerCase().trim();
  const saneExt = ext.toLowerCase();
  const sane = path.resolve(dir, `${saneName}${saneExt}`);
  fs.rename(source, sane, (err) => err && console.log('ERROR: ' + err));
  return sane;
}


function restitchZoomTiles(source, destination) {
  // const getPropsPat = new RegExp(/ .*?="(.*?)"/gi);
  // const vips = path.join('./vips-dev-8.8/bin/vips.exe');
  // const srcStats = fs.lstatSync(source);
  // const outName = path.resolve(destination, 'restitched')

  // if (!srcStats.isDirectory()) return console.log('Source parameter is not a folder.');

  // const propertiesTxt = fs.readFileSync(path.resolve(source, 'ImageProperties.xml'), 'utf8'); 
  // const rows = {};
  // const properties = {};
  // let tiles = [];
  // let z = 0;
  // let rawTiles = [];

  // // loop over tilegorups
  // fs.readdirSync(source).map(group => {
  //   const groupPath = path.join(source, group);
  //   const stats = fs.lstatSync(groupPath);
  //   if (!stats.isDirectory()) return undefined;

  //   // loop
  //   const all = fs.readdirSync(groupPath).map(file => {
  //     const fileZ =  Number(file.charAt(0));
  //     if (isNaN(fileZ)) return null;
  //     if (fileZ > z) z = fileZ; // get highest z
  //     return file;
  //   }).filter(i => i && i.startsWith(z)) // filters to only biggest tiles
  //   .map(i => path.join(groupPath, i));
    
  //   rawTiles = [...rawTiles, ...all];
  // });

  // // extract properties form xml strint into object
  // propertiesTxt.match(getPropsPat).map(i => {
  //   const str = i.replace(/"/gi, "").toLowerCase().trim();
  //   const [ key, val] = str.split("=");
  //   properties[key] = val;
  // });

  // // try and work out which tiles to use
  // rawTiles
  //   .map(filePath => {
  //     const file = path.basename(filePath);
  //     const fileZ =  Number(file.charAt(0));
  //     if (isNaN(fileZ)) return null;
  //     if (fileZ > z) z = fileZ; // get highest z
  //     return filePath;
  //   })
  //   .filter(i => i && path.basename(i).startsWith(z)) // filters to only biggest tiles
  //   .filter(i => i)
  //   .map(filePath => { 
  //     // ordering for zoomify
  //     const file = path.basename(filePath);
  //     //const fileX = Number(file.charAt(2));
  //     const fileY = Number(file.charAt(4));
  //     if (fileY in rows !== true) rows[fileY] = []; 
  //     return rows[fileY].push(filePath);
  //   });

  //   Object.keys(rows).map(i => tiles = [...tiles, ...rows[i]]);  
  //   const across = rows[0].length;
  
  //   execute(`${vips} arrayjoin "${tiles.join(' ')}" --across ${across} ${outName}.jpg`, {
  //     //startMsg: `stitching ${i}`,
  //     error: err => console.log(err.error)
  //   });
  
  //   console.log(tiles);  
}

function convert(workingDir, file, toFormat) {
  const inPath = path.resolve(workingDir, file);
  const name = path.parse(path.basename(inPath)).name;
  const dirPath = path.dirname(inPath);


  sharp(inPath)
  .toFormat(toFormat)
  .toFile(`${name}.${toFormat}`, (err, info) => { 
    console.log(path.resolve(dirPath, name+'.'+toFormat));
  });
}

module.exports = {
  recurse,
  convert,
  execute,
  generateTiles,
  sanitiseName,
  restitchZoomTiles
};