const fs = require('fs');
const util = require('util');
const path = require('path');
const { read } = require('fast-exif');
const iptc = require('node-iptc');
const Vibrant = require('node-vibrant');
const chroma = require('chroma-js');
const chalk = require('chalk');
const R = require('ramda');

const readFile = util.promisify(fs.readFile);

const badContrast = (color1, color2) => chroma.contrast(color1, color2) < 4.5;

const logColorsWithContrast = (color1, color2, text) => {
  const c1hex = color1.hex();
  const c2hex = color2.hex();
  console.log(chalk.hex(c1hex).bgHex(c2hex)(
    `${text} ${c1hex}/${c2hex} ${chroma.contrast(color1, color2)}`
  ));
};

function processColors(vibrantData, imagePath) {
  let Vibrant = chroma(vibrantData.Vibrant.getRgb());
  let DarkVibrant = chroma(vibrantData.DarkVibrant.getRgb());
  let LightVibrant = chroma(vibrantData.LightVibrant.getRgb());
  let Muted = chroma(vibrantData.Muted.getRgb());
  let DarkMuted = chroma(vibrantData.DarkMuted.getRgb());
  let LightMuted = chroma(vibrantData.LightMuted.getRgb());

  // first pass - darken bg and lighten relevant fg colors
  if (badContrast(DarkVibrant, Vibrant) || badContrast(DarkVibrant, LightMuted)) {
    DarkVibrant = DarkVibrant.darken();
    if (badContrast(DarkVibrant, Vibrant)) {
      Vibrant = Vibrant.brighten();
    }
    if (badContrast(DarkVibrant, Vibrant)) {
      Vibrant = Vibrant.brighten();
    }
  }
  // second pass - first doesn't always do enough
  if (badContrast(DarkVibrant, Vibrant) || badContrast(DarkVibrant, LightMuted)) {
    // DarkVibrant = DarkVibrant.darken();
    if (badContrast(DarkVibrant, Vibrant)) {
      Vibrant = Vibrant.brighten(2);
    }
    if (badContrast(DarkVibrant, LightMuted)) {
      LightMuted = LightMuted.brighten(2);
    }
  }

  if (badContrast(DarkVibrant, Vibrant)){
    console.warn('contrast still too low', imagePath);
    logColorsWithContrast(Vibrant, DarkVibrant, 'V-DV');
  }
  if (badContrast(DarkVibrant, LightMuted)){
    console.warn('contrast still too low', imagePath);
    logColorsWithContrast(LightMuted, DarkVibrant, 'LM-DV');
  }


  return {
    Vibrant: Vibrant.rgb(),
    DarkVibrant: DarkVibrant.rgb(),
    LightVibrant: LightVibrant.rgb(),
    Muted: Muted.rgb(),
    DarkMuted: DarkMuted.rgb(),
    LightMuted: LightMuted.rgb(),
  };
}

function convertDMSToDD(dms, positiveDirection) {
  const res = dms
    .map((item, i) => {
      return item / Math.pow(60, i);
    })
    .reduce((a, b) => a + b);
  return positiveDirection ? res : -res;
}

function transformMetaToNodeData(exifData, iptcData, vibrantData, imagePath) {
  const gps = { longitude: null, latitude: null };

  if (exifData) {
    if (
      exifData.gps &&
      exifData.gps.GPSLongitude &&
      exifData.gps.GPSLatitude
    ) {
      gps.longitude = convertDMSToDD(
        exifData.gps.GPSLongitude,
        exifData.gps.GPSLongitudeRef === 'E'
      );
      gps.latitude = convertDMSToDD(
        exifData.gps.GPSLatitude,
        exifData.gps.GPSLatitudeRef === 'N'
      );
    }
  }

  const vibrant = vibrantData ? processColors(vibrantData, imagePath) : null;


  return {
    exif: exifData?.exif,
    gps,
    dateTaken: exifData?.exif?.DateTimeOriginal,
    iptc: iptcData || undefined,
    vibrant,
  };
}


exports.onCreateNode = async function ({ node, actions }) {
  const { createNodeField } = actions;
  if (node.internal.type === 'File' && node.sourceInstanceName === 'gallery') {
    const file = await readFile(node.absolutePath);
    const iptcData = iptc(file);
    const exifData = await read(node.absolutePath);
    const vibrantData = await Vibrant.from(node.absolutePath)
      .quality(3)
      .getPalette();

    createNodeField({
      node,
      name: 'imageMeta',
      value: transformMetaToNodeData(exifData, iptcData, vibrantData, node.absolutePath),
    });
  }
};

// Implement the Gatsby API “createPages”. This is called once the
// data layer is bootstrapped to let plugins create pages from data.
exports.createPages = async ({ graphql, actions, reporter }) => {
  const { createPage } = actions;
  // get all images
  const galleryImages = await graphql(`
      {
        allFile(filter: {
          sourceInstanceName: { eq: "gallery" }}
        ) {
          edges {
            node {
              relativePath,
              base,
              fields {
                imageMeta {
                  dateTaken
                }
              }
            }
          }
        }
      }
    `);
  // Handle errors
  if (galleryImages.errors) {
    reporter.panicOnBuild('Error while running GraphQL query.');
    return;
  }
  // Create pages for each markdown file.
  const galleryImageTemplate = path.resolve('src/components/GalleryImage/GalleryImage.js');
  // const diffDate = (a, b) => 
  //   new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'], a)).getTime() - new Date(R.path(['node', 'childImageSharp', 'fields', 'imageMeta', 'dateTaken'],b)).getTime();
  
  const edges = R.sort(R.descend((edge) => 
    new Date(R.path(['node', 'fields', 'imageMeta', 'dateTaken'], edge))),
  galleryImages.data.allFile.edges);

  edges.forEach(({ node }, index) => {
    const nextImage = index === edges.length - 1
      ? null
      : edges[index + 1].node.base;
    const prevImage = index === 0
      ? null
      : edges[index - 1].node.base;
    const page = {
      path: `photogallery/${node.base}`,
      component: galleryImageTemplate,
      context: {
        imageFilename: node.base,
        nextImage,
        prevImage,
      },
    };
    createPage(page);
  });
};