const fs = require('fs')
const gulp = require('gulp')
const gutil = require('gulp-util')
const watch = require('gulp-watch') // better than gulp.watch because detects new files
const generateDts = require('dts-generator').default

gulp.task('ts-types', exec)

/*
Waits for fullcalendar.js to be created/modified before computing,
to avoid competing with and slowing down main build watcher.
*/
gulp.task('ts-types:watch', function() {
  watch('dist/fullcalendar.js', exec)
})

function exec() {
  gutil.log('Computing TypeScript definitions file...')
  return generateDts({
    project: '.', // where the tsconfig is
    name: 'fullcalendar',
    main: 'fullcalendar/src/main',
    out: 'tmp/fullcalendar.d.ts'
  }).then(function() {
    let content = fs.readFileSync('tmp/fullcalendar.d.ts', { encoding: 'utf8' })
    content = filterModuleDeclaration(content)
    if (!fs.existsSync('dist')) {
      fs.mkdirSync('dist')
    }
    fs.writeFileSync('dist/fullcalendar.d.ts', content, { encoding: 'utf8' })
    gutil.log('Wrote TypeScript definitions file.')
  })
}

// Typedef Source Code Transformation Hacks
// ----------------------------------------

function filterModuleDeclaration(s) {
  return s.replace(
    /^declare module '([^']*)' \{([\S\s]*?)[\n\r]+\}/mg,
    function(whole, id, body) {
      return "declare module '" + filterModuleId(id) + "' {" + filterModuleBody(body) + '\n}'
    }
  )
}

function filterModuleId(id) {
  // fullcalendar/src/something/MyClass -> fullcalendar/MyClass
  return id.replace(
    /\/src\/([^/]*\/)*([A-Z][A-Za-z0-9]*)$/,
    '/$2'
  )
}

function filterModuleBody(s) {
  var defaultExportName

  // changes the name of the default export to `Default` and exports it as a *named* export.
  // this allows ambient declaration merging to grab onto it.
  // workaround for https://github.com/Microsoft/TypeScript/issues/14080
  s = s.replace(/export default( abstract)? class ([A-Z][A-Za-z0-9]*)/, function(m0, m1, m2) {
    defaultExportName = m2
    return 'export' + (m1 || '') + ' class Default'
  })

  if (defaultExportName) {
    // replace any references to the original class' name
    s = s.replace(new RegExp('\\b' + defaultExportName + '\\b', 'g'), 'Default')

    // still needs to be exported as default
    s += '\n\texport default Default;'
  }

  s = s.replace(/from '([^']*)'/g, function(whole, id) {
    return "from '" + filterModuleId(id) + "'"
  })

  return s
}

const fs = require('fs')
const gulp = require('gulp')
const gutil = require('gulp-util')
const watch = require('gulp-watch') // better than gulp.watch because detects new files
const generateDts = require('dts-generator').default

gulp.task('ts-types', exec)

/*
Waits for fullcalendar.js to be created/modified before computing,
to avoid competing with and slowing down main build watcher.
*/
gulp.task('ts-types:watch', function() {
  watch('dist/fullcalendar.js', exec)
})

function exec() {
  gutil.log('Computing TypeScript definitions file...')
  return generateDts({
    project: '.', // where the tsconfig is
    name: 'fullcalendar',
    main: 'fullcalendar/src/main',
    out: 'tmp/fullcalendar.d.ts'
  }).then(function() {
    let content = fs.readFileSync('tmp/fullcalendar.d.ts', { encoding: 'utf8' })

    content = filterModuleDeclaration(content)
    content = content.replace(
      /import\((['"])(..\/)+/g,
      'import($1fullcalendar/src/'
    )

    if (!fs.existsSync('dist')) {
      fs.mkdirSync('dist')
    }
    fs.writeFileSync('dist/fullcalendar.d.ts', content, { encoding: 'utf8' })
    gutil.log('Wrote TypeScript definitions file.')
  })
}

// Typedef Source Code Transformation Hacks
// ----------------------------------------

function filterModuleDeclaration(s) {
  return s.replace(
    /^declare module '([^']*)' \{([\S\s]*?)[\n\r]+\}/mg,
    function(whole, id, body) {
      return "declare module '" + filterModuleId(id) + "' {" + filterModuleBody(body) + '\n}'
    }
  )
}

function filterModuleId(id) {
  // fullcalendar/src/something/MyClass -> fullcalendar/MyClass
  return id.replace(
    /\/src\/([^/]*\/)*([A-Z][A-Za-z0-9]*)$/,
    '/$2'
  )
}

function filterModuleBody(s) {
  var defaultExportName

  // changes the name of the default export to `Default` and exports it as a *named* export.
  // this allows ambient declaration merging to grab onto it.
  // workaround for https://github.com/Microsoft/TypeScript/issues/14080
  s = s.replace(/export default( abstract)? class ([A-Z][A-Za-z0-9]*)/, function(m0, m1, m2) {
    defaultExportName = m2
    return 'export' + (m1 || '') + ' class Default'
  })

  if (defaultExportName) {
    // replace any references to the original class' name
    s = s.replace(new RegExp('\\b' + defaultExportName + '\\b', 'g'), 'Default')

    // still needs to be exported as default
    s += '\n\texport default Default;'
  }

  s = s.replace(/from '([^']*)'/g, function(whole, id) {
    return "from '" + filterModuleId(id) + "'"
  })

  return s
}

fix .d.ts choking on wrong import() paths