#!/usr/bin/env node

const _ = require('lodash'); // ships with termcolors; only used because of termcolors customization
const termcolors = require('termcolors');
const { readFileSync, writeFileSync } = require('fs');
const { resolve } = require('path');

const baseColors = Object.freeze({
  red: { gui: '#E06C75', cterm: '204', cterm16: '1' }, // alternate cterm: 168
  dark_red: { gui: '#BE5046', cterm: '196', cterm16: '9' },
  green: { gui: '#98C379', cterm: '114', cterm16: '2' },
  yellow: { gui: '#E5C07B', cterm: '180', cterm16: '3' },
  dark_yellow: { gui: '#D19A66', cterm: '173', cterm16: '11' },
  blue: { gui: '#61AFEF', cterm: '39', cterm16: '4' }, // alternate cterm: 75
  purple: { gui: '#C678DD', cterm: '170', cterm16: '5' }, // alternate cterm: 176
  cyan: { gui: '#56B6C2', cterm: '38', cterm16: '6' }, // alternate cterm: 73
  white: { gui: '#ABB2BF', cterm: '145', cterm16: '15' },
  black: { gui: '#282C34', cterm: '235', cterm16: '0' }
});

const specialColors = Object.freeze({
  comment_grey: { gui: '#5C6370', cterm: '59', cterm16: '7' },
  gutter_fg_grey: { gui: '#4B5263', cterm: '238', cterm16: '8' },
  cursor_grey: { gui: '#2C323C', cterm: '236', cterm16: '0' },
  visual_grey: { gui: '#3E4452', cterm: '237', cterm16: '8' },
  menu_grey: { cterm16: '7' }, // vim theme handles gui/cterm values
  special_grey: { gui: '#3B4048', cterm: '238', cterm16: '7' },
  vertsplit: { gui: '#3E4452', cterm: '59', cterm16: '7' }
});

const colors = Object.assign({}, baseColors, specialColors);

const templateMap = Object.freeze({
  'templates/autoload.template.vim': '../autoload/onedark.vim',
  'templates/One Dark.Xresources': '../term/One Dark.Xresources'
});

const shouldCheck = String(process.argv[2]).toLowerCase() === 'check';

const handleError = (message, cause) => {
  console.error('Error:', message);
  if (cause) console.error('Cause:', cause);
  process.exit(-1);
};

console.log(
  shouldCheck
    ? 'Checking for inconsistencies between templates and existing output files...'
    : 'Generating output files from templates...'
);

Object.keys(templateMap).forEach(templateFilename => {
  // Read the template
  const templatePath = resolve(__dirname, templateFilename);
  let templateText;
  try {
    templateText = readFileSync(templatePath, 'utf8');
  } catch (e) {
    handleError(`Error reading template ${templatePath}`, e);
  }

  // Compile the template
  let template;
  try {
    template = _.template(templateText);
  } catch (e) {
    handleError(`Error compiling template ${templatePath}`, e);
  }

  // Execute the template (generate the output)
  let output;
  try {
    output = template(colors);
  } catch (e) {
    handleError(`Error running template ${templatePath}`, e);
  }

  let outputPath = resolve(__dirname, templateMap[templateFilename]);
  try {
    const existingOutput = readFileSync(outputPath, 'utf8');
    // Only need to do something if the generated output differs from what's already on disk
    if (output !== existingOutput) {
      if (shouldCheck) {
        // Check generated output against existing output file
        handleError(
          `Changes were made to ${templateMap[templateFilename]} that are inconsistent with its template (${templateFilename}).\nDo you need to [re]build?`
        );
      } else {
        // Overwrite existing output file
        try {
          writeFileSync(outputPath, output, 'utf8');
        } catch (e) {
          handleError(`Error writing to output file ${outputPath}`, e);
        }
      }
    }
  } catch (e) {
    handleError(`Error reading existing output file ${outputPath}`, e);
  }
});

try {
  // Use the Xresources theme as a color source since it was generated above via templating
  const xresources = readFileSync(resolve(__dirname, '../term/One Dark.Xresources'), 'utf8');
  const terminalPalette = termcolors.xresources.import(xresources);

  let alacrittyTemplate, itermTemplate, kittyTemplate, konsoleTemplate, terminalAppTemplate;

  // Compile custom terminal color templates based on ones that ship with termcolors
  try {
    alacrittyTemplate = termcolors.export(
      readFileSync(resolve(__dirname, 'templates/One Dark.alacritty')),
      _.partialRight(_.mapValues, function (color) {
        return color.toHex().slice(1);
      })
    );

    itermTemplate = termcolors.export(
      // From termcolors/lib/formats/iterm.js
      readFileSync(resolve(__dirname, 'templates/One Dark.itermcolors')),
      _.partialRight(_.mapValues, function (color) {
        return color.toAvgRgbArray();
      })
    );

    kittyTemplate = termcolors.export(
      readFileSync(resolve(__dirname, 'templates/One Dark.kitty')),
      _.partialRight(_.mapValues, function (color) {
        return color.toHex().slice(1);
      })
    );

    konsoleTemplate = termcolors.export(
      // From termcolors/lib/formats/konsole.js
      readFileSync(resolve(__dirname, 'templates/One Dark.colorscheme')),
      _.partialRight(_.mapValues, function (color) {
        return color.toRgbArray().join();
      })
    );

    // From termcolors/lib/formats/terminal-app.js
    const code = [
      Buffer.from(
        '62706c6973743030d40102030405061516582476657273696f6e58246f626a65637473592461726368697665725424746f7012000186a0a307080f55246e756c6cd3090a0b0c0d0e554e535247425c4e53436f6c6f7253706163655624636c6173734f1027',
        'hex'
      ),
      Buffer.from(
        '0010018002d2101112135a24636c6173736e616d655824636c6173736573574e53436f6c6f72a21214584e534f626a6563745f100f4e534b657965644172636869766572d1171854726f6f74800108111a232d32373b41484e5b628c8e9095a0a9b1b4bdcfd2d700000000000001010000000000000019000000000000000000000000000000d9',
        'hex'
      )
    ];

    terminalAppTemplate = termcolors.export(
      readFileSync(resolve(__dirname, 'templates/One Dark.terminal')),
      // From termcolors/lib/formats/terminal-app.js
      _.partialRight(_.mapValues, function (color) {
        var srgb = color.toAvgRgbArray();
        srgb = srgb
          .map(function (n) {
            return n.toFixed(10).toString();
          })
          .join(' ');
        var output = code[0].toString('binary') + srgb + code[1].toString('binary');
        output = Buffer.from(output, 'binary').toString('base64');
        return output.match(/.{1,68}/g).join('\n\t');
      })
    );
  } catch (e) {
    handleError('Error compiling terminal color template', e);
  }

  try {
    writeFileSync(resolve(__dirname, '../term/One Dark.alacritty'), alacrittyTemplate(terminalPalette));
    writeFileSync(resolve(__dirname, '../term/One Dark.itermcolors'), itermTemplate(terminalPalette));
    writeFileSync(resolve(__dirname, '../term/One Dark.kitty'), kittyTemplate(terminalPalette));
    writeFileSync(resolve(__dirname, '../term/One Dark.colorscheme'), konsoleTemplate(terminalPalette));
    writeFileSync(resolve(__dirname, '../term/One Dark.terminal'), terminalAppTemplate(terminalPalette));
  } catch (e) {
    handleError('Error writing terminal color file', e);
  }
} catch (e) {
  handleError('Error reading Xresources terminal color file', e);
}

console.log('Success!');
