###
# Modules
###
_        = require('lodash')
gulp     = require('gulp')
clean    = require('gulp-rimraf')
coffee   = require('gulp-coffee')
concat   = require('gulp-concat')
filter   = require('gulp-filter')
iif      = require('gulp-if')
imagemin = require('gulp-imagemin')
header   = require('gulp-header')
jade     = require('gulp-jade')
minify   = require('gulp-minify-css')
nodemon  = require('gulp-nodemon')
plumber  = require('gulp-plumber')
react    = require('gulp-react')
sass     = require('gulp-ruby-sass')
uglify   = require('gulp-uglify')
sync     = require('browser-sync')

{ exec, spawn } = require('child_process')
env = require('./env.json')


###
# Directory Paths
###
_src = './_src'
_build = './build'
_static = '../_static'
_modules = './node_modules'
p =
  src:
    backend: "#{_src}/backend"
    frontend: "#{_src}/frontend"
    vendor: "#{_src}/vendor"
  build:
    public: "#{_build}/public"
    js: "#{_build}/public/js"
    css: "#{_build}/public/css"
    img: "#{_build}/public/img"
    templates: "#{_build}/public/js/templates"
    vendor:
      js: "#{_build}/public/js/vendor"
      css: "#{_build}/public/css/vendor"
  static:
    js: "#{_static}/js"
    css: "#{_static}/css"
    img: "#{_static}/img"
    templates: "#{_static}/js/templates"
    vendor:
      js: "#{_static}/js/vendor"
      css: "#{_static}/css/vendor"


###
# Tasker
###
_do = (src='', dest='', task='') ->
  _copy = task is ''
  _sass = /sass/.test task
  _jade = /jade/.test task
  _coffee = /coffee/.test task
  _uglify = /uglify/.test task
  _minify = /minify/.test task
  _imagemin = /imagemin/.test task
  gulp.src(src)
    .pipe(do plumber)
    # Sass
    .pipe(iif(_sass, sass({compass:true, style: 'compressed'})))
    # Jade
    .pipe(iif(_jade, jade({data: env, pretty: true})))
    # Coffee/React
    .pipe(iif(_coffee, coffee(bare:true)))
    .pipe(iif(_coffee, header("/** @jsx React.DOM */")))
    .pipe(iif(_coffee, react()))
    # Uglify
    .pipe(iif(_uglify, uglify()))
    # Minify
    .pipe(iif(_minify, minify()))
    # Imagemin
    .pipe(iif(_imagemin, imagemin()))
    .pipe(gulp.dest(dest))
    .pipe(iif(_sass, filter('**/*.css')))
    .pipe(iif(_copy or _sass, sync.reload(stream: true)))

###
# Configs
###
gulp.task 'config', ->
  _do('./Procfile', _build)
  _do('./env.json', _build)


###
# Clean
###
gulp.task 'clean', ->
  gulp.src(_build, {read: false}).pipe(clean())
  exec "rm -rf #{_static}", (err, stdout, stderr) ->
    process.stdout.write stdout
    process.stdout.write stderr


###
# Clean libs
###
gulp.task 'wipe', ['clean'], ->
  gulp.src(_modules, {read: false}).pipe(clean())
  gulp.src(p.src.vendor, {read: false}).pipe(clean())


###
# Vendor Files
###
gulp.task 'vendor', ->
  _do("#{p.src.vendor}/requirejs/require.js", p.build.vendor.js)
  _do("#{p.src.vendor}/zepto/zepto.js", p.build.vendor.js)
  _do("#{p.src.vendor}/lodash/dist/lodash.underscore.js", p.build.vendor.js)
  _do("#{p.src.vendor}/backbone/backbone.js", p.build.vendor.js)
  _do("#{p.src.vendor}/require-jade/jade.js", p.build.vendor.js)
  _do("#{p.src.vendor}/backbone.qp/backbone.queryparams.js", p.build.vendor.js)
  _do("#{p.src.vendor}/mixen/mixen.js", p.build.vendor.js)
  _do("#{p.src.vendor}/q/q.js", p.build.vendor.js)
  _do("#{p.src.vendor}/grid/*.css", p.build.vendor.css)
  _do("#{p.src.vendor}/prismjs/prism.js", p.build.vendor.js)
  _do("#{p.src.vendor}/prismjs/prism.css", p.build.vendor.css)
  _do("#{p.src.vendor}/react/react.js", p.build.vendor.js)


###
# Tasks
###
gulp.task 'backend', ->
  _do("#{p.src.backend}/**/*.coffee", _build, 'coffee')
gulp.task 'views', ->
  _do("#{p.src.frontend}/views/**/*.jade", "#{_build}/views")
gulp.task 'coffee', ->
  _do("#{p.src.frontend}/coffee/**/*.coffee", p.build.js, 'coffee')
gulp.task 'sass', ->
  _do("#{p.src.frontend}/sass/**/*.sass", p.build.css, 'sass')
gulp.task 'templates', ->
  _do("#{p.src.frontend}/templates/**/*", p.build.templates)
gulp.task 'images', ->
  _do("#{p.src.frontend}/images/**/*", p.build.img)


###
# Build
###
gulp.task 'build', [
  'config', 'vendor', 'coffee', 'sass'
  'templates', 'images', 'backend', 'views'
]


###
# Test
###
gulp.task 'test', ->
  app = spawn 'node', ['server.js'],
    cwd: "#{__dirname}/#{_build.slice(2)}"
    env: _.extend process.env,
      NODE_ENV: 'test'
      PORT: 7001

  exec "cortado #{_src.slice(2)}/test", (err, stdout, stderr) ->
    process.stdout.write stdout
    process.stdout.write stderr
    app.kill('SIGINT')


###
# Watch/BrowserSync
###
gulp.task 'watch', ['browser-sync'], ->
  gulp.watch "#{p.src.backend}/**/*.coffee", ['backend']
  gulp.watch "#{p.src.frontend}/coffee/**/*.coffee", ['coffee']
  gulp.watch "#{p.src.frontend}/sass/**/*.sass", ['sass']
  gulp.watch "#{p.src.frontend}/views/**/*.jade", ['views']
  gulp.watch "#{p.src.frontend}/templates/**/*.jade", ['templates']

gulp.task 'browser-sync', ['nodemon'], ->
  sync
    proxy: "localhost:#{env.EXPRESS_PORT}"
    port: +env.EXPRESS_PORT+1
    open: false

gulp.task 'nodemon', ['build'], (cb) ->
  called = false
  onStart = ->
    return if called
    do cb
    called = true
  onRestart = -> setTimeout (-> sync.reload(stream:false)), 200
  opts =
    watch: _build
    script: "#{_build}/server.js"
    env:
      NODE_ENV: 'development'
  nodemon(opts).on('start', onStart).on('restart', onRestart)


###
# Static Build
###
gulp.task 'static', ['clean'], ->

  # Vendors
  _do("#{p.src.vendor}/requirejs/require.js", p.static.vendor.js, 'uglify')
  _do("#{p.src.vendor}/lodash/dist/lodash.underscore.js", p.static.vendor.js, 'uglify')
  _do("#{p.src.vendor}/backbone/backbone.js", p.static.vendor.js, 'uglify')
  _do("#{p.src.vendor}/backbone.qp/backbone.queryparams.js", p.static.vendor.js, 'uglify')
  _do("#{p.src.vendor}/q/q.js", p.static.vendor.js, 'uglify')
  _do("#{p.src.vendor}/grid/*.css", p.static.vendor.css, 'minify')
  _do("#{p.src.vendor}/prismjs/prism.js", p.static.vendor.js, 'uglify')
  _do("#{p.src.vendor}/prismjs/prism.css", p.static.vendor.css, 'minify')
  _do("#{p.src.vendor}/zepto/zepto.js", p.static.vendor.js, 'uglify')
  _do("#{p.src.vendor}/mixen/mixen.js", p.static.vendor.js, 'uglify')
  _do("#{p.src.vendor}/require-jade/jade.js", p.static.vendor.js)

  # Top-Level Text
  _do("#{_src}/misc/**/*", _static)

  # Index
  _do(["#{p.src.frontend}/views/index.jade","#{p.src.frontend}/views/404.jade"], _static, 'jade')

  # Jade Templates
  _do("#{p.src.frontend}/templates/**/*", p.static.templates)

  # Coffee
  _do("#{p.src.frontend}/coffee/**/*.coffee", p.static.js, 'coffee uglify')

  # Sass
  _do("#{p.src.frontend}/sass/**/*.sass", p.static.css, 'sass')

  # Images
  _do("#{p.src.frontend}/images/**/*", p.static.img, 'imagemin')


###
# Deploy to Github Pages
###
gulp.task 'deploy', ['static'], ->

  CMDS = [
    "cd #{_static}"
    "git init"
    "git checkout -b gh-pages"
    "git add -A"
    "git commit -m '.'"
    "git remote add github git@github.com:#{env.GITHUB_NAME.toLowerCase()}/#{env.NAME}.git"
    "git push -fu github gh-pages"
  ].join(' && ')

  exec CMDS, (err, stdout, stderr) ->
    process.stdout.write stdout
    process.stdout.write stderr


###
# Default
###
gulp.task 'default', ['watch']

