fs = require 'fs'
path = require 'path'
os = require 'os'

# This is the main Gruntfile that manages building N1 distributions.
# The reason it's inside of the build/ folder is so everything can be
# compiled against Node's v8 headers instead of Chrome's v8 headers. All
# packages in the root-level node_modules are compiled against Chrome's v8
# headers.
#
# See src/pro/docs/ContinuousIntegration.md for more detailed
# instructions on how we build N1.
#
# Some useful grunt options are:
#   --install-dir
#   --build-dir
#
# To keep the various directories straight, here are what the various
# directories might look like on MacOS
#
# tmpDir: /var/folders/xl/_qdlmc512sb6cpqryy_2tzzw0000gn/T/ (aka /tmp)
#
# buildDir    = /tmp/nylas-build
# shellAppDir = /tmp/nylas-build/Nylas N1.app
# contentsDir = /tmp/nylas-build/Nylas N1.app/Contents
# appDir      = /tmp/nylas-build/Nylas N1.app/Contents/Resources/app
#
# installDir = /Applications/Nylas N1.app
#
# And on Linux:
#
# tmpDir: /tmp/
#
# buildDir    = /tmp/nylas-build
# shellAppDir = /tmp/nylas-build/Nylas
# contentsDir = /tmp/nylas-build/Nylas
# appDir      = /tmp/nylas-build/Nylas/resources/app
#
# installDir = /usr/local OR $INSTALL_PREFIX
# binDir     = /usr/local/bin
# shareDir   = /usr/local/share/nylas
_ = require 'underscore'

packageJson = require '../package.json'

# Shim harmony collections in case grunt was invoked without harmony
# collections enabled
_.extend(global, require('harmony-collections')) unless global.WeakMap?

module.exports = (grunt) ->
  grunt.loadNpmTasks('grunt-coffeelint-cjsx')
  grunt.loadNpmTasks('grunt-lesslint')
  grunt.loadNpmTasks('grunt-cson')
  grunt.loadNpmTasks('grunt-contrib-csslint')
  grunt.loadNpmTasks('grunt-coffee-react')
  grunt.loadNpmTasks('grunt-contrib-coffee')
  grunt.loadNpmTasks('grunt-contrib-less')
  grunt.loadNpmTasks('grunt-shell')
  grunt.loadNpmTasks('grunt-markdown')
  grunt.loadNpmTasks('grunt-download-electron')
  grunt.loadNpmTasks('grunt-electron-installer')
  grunt.loadTasks('tasks')

  # This allows all subsequent paths to the relative to the root of the repo
  grunt.file.setBase(path.resolve('..'))

  [major, minor, patch] = packageJson.version.split('.')
  tmpDir = os.tmpdir()
  appName = if process.platform is 'darwin' then 'Nylas N1.app' else 'Nylas'
  appFileName = packageJson.name
  buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'nylas-build')
  buildDir = path.resolve(buildDir)
  installDir = grunt.option('install-dir')

  home = if process.platform is 'win32' then process.env.USERPROFILE else process.env.HOME
  electronDownloadDir = path.join(home, '.nylas', 'electron')

  symbolsDir = path.join(buildDir, 'Nylas.breakpad.syms')
  shellAppDir = path.join(buildDir, appName)
  if process.platform is 'win32'
    contentsDir = shellAppDir
    appDir = path.join(shellAppDir, 'resources', 'app')
    installDir ?= path.join(process.env.ProgramFiles, appName)
    killCommand = 'taskkill /F /IM nylas.exe'
  else if process.platform is 'darwin'
    contentsDir = path.join(shellAppDir, 'Contents')
    appDir = path.join(contentsDir, 'Resources', 'app')
    installDir ?= path.join('/Applications', appName)
    killCommand = 'pkill -9 Nylas'
  else
    contentsDir = shellAppDir
    appDir = path.join(shellAppDir, 'resources', 'app')
    installDir ?= process.env.INSTALL_PREFIX ? '/usr/local'
    killCommand = 'pkill -9 nylas'

  grunt.option('appDir', appDir)
  installDir = path.resolve(installDir)

  if process.platform is "linux"
    linuxBinDir = path.join(installDir, "bin")
    linuxShareDir = path.join(installDir, "share", appFileName)
  else
    linuxBinDir = null
    linuxShareDir = null

  cjsxConfig =
    glob_to_multiple:
      expand: true
      src: [
        'src/**/*.cjsx'
        'internal_packages/**/*.cjsx'
        '!src/**/node_modules/**/*.cjsx'
        '!internal_packages/**/node_modules/**/*.cjsx'
      ]
      dest: appDir
      ext: '.js'

  coffeeConfig =
    glob_to_multiple:
      expand: true
      src: [
        'src/**/*.coffee'
        'internal_packages/**/*.coffee'
        'static/**/*.coffee'
        '!src/**/node_modules/**/*.coffee'
        '!internal_packages/**/node_modules/**/*.coffee'
      ]
      dest: appDir
      ext: '.js'

  babelConfig =
    options: require("../static/babelrc")
    dist:
      files: [{
        expand: true
        src: [
          'src/**/*.es6'
          'src/**/*.es'
          'src/**/*.jsx'
          'internal_packages/**/*.es6'
          'internal_packages/**/*.es'
          'internal_packages/**/*.jsx'
          'static/**/*.es6'
          'static/**/*.es'
        ]
        dest: appDir
        ext: '.js'
      }]

  lessConfig =
    options:
      paths: [
        'static/variables'
        'static'
      ]
    glob_to_multiple:
      expand: true
      src: [
        'static/**/*.less'
      ]
      dest: appDir
      ext: '.css'

  prebuildLessConfig =
    src: [
      'static/**/*.less'
    ]

  csonConfig =
    options:
      rootObject: false
      cachePath: path.join(home, '.nylas', 'compile-cache', 'grunt-cson')

    glob_to_multiple:
      expand: true
      src: [
        'static/**/*.cson'
      ]
      dest: appDir
      ext: '.json'

  for folder in ['node_modules', 'internal_packages']
    if not fs.existsSync(folder)
      console.log("Ignoring #{folder}, which is missing.")
      continue
    for child in fs.readdirSync(folder) when child isnt '.bin'
      directory = path.join(folder, child)
      metadataPath = path.join(directory, 'package.json')
      continue unless grunt.file.isFile(metadataPath)

      {engines, theme} = grunt.file.readJSON(metadataPath)
      if engines?.nylas?
        lessConfig.glob_to_multiple.src.push("#{directory}/**/*.less")
        prebuildLessConfig.src.push("#{directory}/**/*.less") unless theme
        csonConfig.glob_to_multiple.src.push("#{directory}/**/*.cson")

  COFFEE_SRC = [
    'internal_packages/**/*.cjsx'
    'internal_packages/**/*.coffee'
    'dot-nylas/**/*.coffee'
    'src/**/*.coffee'
    'src/**/*.cjsx'
    'spec/**/*.cjsx'
    'spec/**/*.coffee'
    '!src/**/node_modules/**/*.coffee'
    '!internal_packages/**/node_modules/**/*.coffee'
  ]
  ES_SRC = [
    'internal_packages/**/*.jsx'
    'internal_packages/**/*.es6'
    'internal_packages/**/*.es'
    'dot-nylas/**/*.es6'
    'dot-nylas/**/*.es'
    'src/**/*.es6'
    'src/**/*.es'
    'src/**/*.jsx'
    'spec/**/*.es6'
    'spec/**/*.es'
    'spec/**/*.jsx'
    '!src/**/node_modules/**/*.es6'
    '!src/**/node_modules/**/*.es'
    '!src/**/node_modules/**/*.jsx'
    '!internal_packages/**/node_modules/**/*.es6'
    '!internal_packages/**/node_modules/**/*.es'
    '!internal_packages/**/node_modules/**/*.jsx'
  ]

  grunt.initConfig
    pkg: grunt.file.readJSON('package.json')

    nylasGruntConfig: {appDir, appName, appFileName, symbolsDir, buildDir, contentsDir, installDir, shellAppDir, linuxBinDir, linuxShareDir}

    docsOutputDir: 'docs/output'

    coffee: coffeeConfig

    babel: babelConfig

    cjsx: cjsxConfig

    less: lessConfig

    'prebuild-less': prebuildLessConfig

    cson: csonConfig

    nylaslint:
      src: COFFEE_SRC.concat(ES_SRC)

    coffeelint:
      options:
        configFile: 'build/config/coffeelint.json'
      src: COFFEE_SRC
      build: [
        'build/tasks/**/*.coffee'
        'build/Gruntfile.coffee'
      ]
      test: [
        'spec/**/*.cjsx'
        'spec/**/*.coffee'
      ]
      static: [
        'static/**/*.coffee'
        'static/**/*.cjsx'
      ]
      target:
        grunt.option("target")?.split(" ") or []

    eslint:
      options:
        ignore: false
        configFile: 'build/config/eslint.json'
      target: ES_SRC

    eslintFixer:
      src: ES_SRC

    csslint:
      options:
        'adjoining-classes': false
        'duplicate-background-images': false
        'box-model': false
        'box-sizing': false
        'bulletproof-font-face': false
        'compatible-vendor-prefixes': false
        'display-property-grouping': false
        'fallback-colors': false
        'font-sizes': false
        'gradients': false
        'ids': false
        'important': false
        'known-properties': false
        'outline-none': false
        'overqualified-elements': false
        'qualified-headings': false
        'unique-headings': false
        'universal-selector': false
        'vendor-prefix': false
        'duplicate-properties': false # doesn't place nice with mixins
      src: [
        'static/**/*.css'
      ]

    lesslint:
      src: [
        'internal_packages/**/*.less'
        'dot-nylas/**/*.less'
        'static/**/*.less'
      ]
      options:
        imports: ['variables/*.less']

    markdown:
      guides:
        files: [
          expand: true
          cwd: 'docs'
          src: '**/*.md'
          dest: 'docs/output/'
          ext: '.html'
        ]
        options:
          template: 'docs/template.jst'
          templateContext:
            title: "Documentation"
            tag: "v#{major}.#{minor}"
          markdownOptions:
            gfm: true
          preCompile: (src, context) ->
            fm = require 'json-front-matter'
            parsed = fm.parse(src)
            _.extend(context, parsed.attributes)
            parsed.body

    'download-electron':
      version: packageJson.electronVersion
      outputDir: 'electron'
      downloadDir: electronDownloadDir
      rebuild: true  # rebuild native modules after electron is updated
      token: process.env.NYLAS_GITHUB_OAUTH_TOKEN

    'create-windows-installer':
      installer:
        appDirectory: shellAppDir
        outputDirectory: path.join(buildDir, 'installer')
        authors: 'Nylas Inc.'
        loadingGif: path.resolve(__dirname, 'resources', 'win', 'loading.gif')
        iconUrl: 'http://edgehill.s3.amazonaws.com/static/nylas.ico'
        setupIcon: path.resolve(__dirname, 'resources', 'win', 'nylas.ico')
        certificateFile: process.env.CERTIFICATE_FILE
        certificatePassword: process.env.WINDOWS_CODESIGN_KEY_PASSWORD
        exe: 'nylas.exe'

    shell:
      'kill-n1':
        command: killCommand
        options:
          stdout: false
          stderr: false
          failOnError: false

  grunt.registerTask('compile',
    ['coffee', 'cjsx', 'babel', 'prebuild-less', 'cson'])

  grunt.registerTask('lint',
    ['eslint', 'lesslint', 'nylaslint', 'coffeelint', 'csslint'])

  grunt.registerTask('test', ['shell:kill-n1', 'run-unit-tests'])

  grunt.registerTask('docs', ['build-docs', 'render-docs'])

  buildTasks = [
    'copy-files-for-build',
    'compile',
    'generate-license:save',
    'generate-module-cache',
    'compile-packages-slug']
  buildTasks.push('copy-info-plist') if process.platform is 'darwin'
  buildTasks.push('set-exe-icon') if process.platform is 'win32'
  grunt.registerTask('build', buildTasks)

  ciTasks = ['output-disk-space',
             'download-electron',
             'build']
  ciTasks.push('dump-symbols') if process.platform isnt 'win32'
  ciTasks.push('set-version', 'lint', 'generate-asar')

  if process.platform is "darwin"
    ciTasks.push('test', 'codesign', 'mkdmg')

  else if process.platform is "linux"
    ciTasks.push('mkdeb')
    ciTasks.push('mkrpm')

  else if process.platform is "win32"
    ciTasks.push('create-windows-installer:installer')

  {shouldPublishBuild} = require('./tasks/task-helpers')(grunt)
  if shouldPublishBuild()
    ciTasks.push('publish-nylas-build')

  grunt.registerTask('ci', ciTasks)

  defaultTasks = ['download-electron', 'build', 'set-version', 'generate-asar']
  defaultTasks.push 'mkdmg' if process.platform is 'darwin'
  defaultTasks.push 'install' unless process.platform is 'linux'
  grunt.registerTask('default', defaultTasks)