var
  // gulp
  gulp            = require('gulp'),
  cleancss        = require('gulp-clean-css'),
  concat          = require('gulp-concat'),
  frontmatter     = require('gulp-front-matter'),
  imagemin        = require('gulp-imagemin'),
  newer           = require('gulp-newer'),
  rename          = require('gulp-rename'),

  // components
  del             = require('del'),
  ejs             = require('ejs'),
  fs              = require('fs'),
  md              = require('markdown-it')({
                      html:        true,
                      xhtmlOut:    true,
                      linkify:     true,
                      typographer: true
                    }),
  path            = require('path'),
  pump            = require('pump'),
  seq             = require('run-sequence'),
  through         = require('through2'),

  // config
  site            = require('./site.json'),
  pkg             = require('./package.json'),

  // variables
  dest_dir        = 'www',
  css_src_dir     = 'assets/styles',
  js_src_dir      = 'assets/scripts',
  pages_src_dir   = 'content/pages',
  support_src_dir = 'content/support',

  // functions
  readFileIfExists = function (file_path){
    try {
      return fs.readFileSync(file_path, {encoding: 'utf8'});
    } catch(err) {
      if (err.code == 'ENOENT') return undefined;
      throw err;
    }
  },

  collectPages = function(base_path) {
    var
      fm = require('front-matter'),
      pages = [],
      tags = [];
    return through.obj(function (file, enc, cb) {

      var
        data = fm.test(file.contents.toString()) ? fm(file.contents.toString()) : {},
        contents = data && data.body ? data.body : file.contents.toString(),
        page = file.page,
        name = file.path.replace(file.base,'');
      if (data && data.attributes) {
        Object.assign(data.attributes, page)
      }
      page.content = contents;
      page.path = file.path.replace(base_path, '');
      page.path = page.path.substring(0, page.path.lastIndexOf('.')) + '.html';
      page.name = page.path.substring(page.path.lastIndexOf('/') + 1, page.path.lastIndexOf('.'));

      if (file.path.endsWith('.md')) {
        page.content = md.render(contents);
      } else if (file.path.endsWith('ejs')) {
        page.content = ejs.render(contents, page, {});
      }

      pages.push(page);

      if (page.tags) {
        page.tags.split(' ').forEach(function (tag) {
          if (tags.indexOf(tag) == -1) {
            tags.push(tag);
          }
        });
      }
      this.push(file);
      cb();
    },
    function (cb) {
      if (site.pages) {
        site.pages = site.pages.concat(pages);
      } else {
        site.pages = pages;
      }
      if (site.tags) {
        site.tags = site.tags.concat(tags.filter(function (item) {
            return tags.indexOf(item) < 0;
        }));
      } else {
        site.tags = tags;
      }

      cb();
    });
  },

  applyMarkdown = function() {
    return through.obj(function (file, enc, cb) {
      file.contents = new Buffer(md.render(file.contents.toString()), 'utf8');
      this.push(file);
      cb();
    });

  },

  applyTemplate = function (template_file) {
    // read template_file into a string
    // pass string to ejs.render
    var
      filename = path.join(__dirname, template_file),
      options = {
        "filename": filename
      },
      template_str = fs.readFileSync(filename, 'utf8'),
      tpl = ejs.compile(template_str, options);

    return through.obj(function (file, enc, cb) {
      var
        name = file.path.replace(file.base,''),
        data = {
            name: name.substring(0, name.lastIndexOf('.')),
            site: site,
            page: file.page,
            content: file.contents.toString()
        };

      file.contents = new Buffer(tpl(data), 'utf8');
      this.push(file);
      cb();
    });
  },

  readJson = function () {
    return through.obj(function (file, enc, cb) {
      var data = readFileIfExists(path.join(path.dirname(file.path), path.basename(file.path)) + '.json');
      if (data !== undefined) {
        file.page = JSON.parse(data);
      }
      this.push(file);
      cb();
    });
  },

  parseEJS = function(){
    return through.obj(function (file, enc, cb) {
      var data = {
          site: site,
          page: file.page
      };
      file.contents = new Buffer(ejs.render(file.contents.toString(), data, {}));
      this.push(file);
      cb();
    });
  },

  buildSupportFiles = function() {
    var dest = dest_dir;
    console.log('Building Support Files...');
    return gulp
      .src(path.join(support_src_dir,'**/*.ejs'))
      .pipe(parseEJS())
      .pipe(rename({
        extname: ''
      }))
      .pipe(gulp.dest(dest));
  }
  ;

site.time = new Date();
site.base_uri = (readFileIfExists('./base_uri') || '').trim();
site.version = pkg.version;

gulp
  .task('default', function() {
    // place code for your default task here
  })
  // start with the files from the old web site
  .task('css', function() {
    var dest = path.join(dest_dir,'styles');
    console.log('Concat & cache-bust CSS');
    return gulp
      // set source (src/**/*.css)
      .src(path.join(css_src_dir,'*.css'))
      // write to site.<pkg-ver>.css
      .pipe(concat('styles.' + pkg.version + '.css'))
      // write to dest/content
      .pipe(gulp.dest(dest));
  })
  .task('files', function(){
    var dest = path.join(dest_dir,'files');
    console.log('Copy files');
    return gulp
      .src('assets/files/*')
      .pipe(newer(dest))
      .pipe(gulp.dest(dest));
  })
  .task('fonts', function(){
    var dest = path.join(dest_dir,'fonts');
    console.log('Copy fonts');
    return gulp
      .src('assets/fonts/**')
      .pipe(newer(dest))
      .pipe(gulp.dest(dest));
  })
  .task('images', function(){
    var dest = path.join(dest_dir,'images');
    console.log('Compress, cache and copy images');
    return gulp
      .src('assets/images/**/*')
      .pipe(newer(dest))
      .pipe(imagemin({
        optimizationLevel: 3
      }))
      .pipe(gulp.dest(dest));
  })
  .task('js', function (cb) {
    var dest = path.join(dest_dir,'scripts');
    console.log('Cache-bust JS');
    pump([
        // set source
        gulp.src(path.join(js_src_dir,'*.js')),
        // write to scripts.<pkg-ver>.js
        concat(`scripts.${pkg.version}.js`),
        // write to dest
        gulp.dest(dest)
      ],
      cb
    );
  })
  .task('pages:md', function () {
    var dest = dest_dir;
    console.log('Building Pages (MD)...');
    return gulp
      .src(path.join(pages_src_dir,'**/*.md'))
      .pipe(frontmatter({
          property: 'page',
          remove: true
        }))
      .pipe(collectPages(path.join(process.cwd(), pages_src_dir)))
      .pipe(applyMarkdown())
      .pipe(applyTemplate('./templates/default.ejs'))
      .pipe(rename({extname: '.html'}))
      .pipe(gulp.dest(dest));
  })
  .task('pages:ejs', function () {
    var dest = dest_dir;
    console.log('Building Pages (EJS)...');
    return gulp
      .src(path.join(pages_src_dir,'**/*.ejs'))
      .pipe(frontmatter({
          property: 'page',
          remove: true
        }))
      .pipe(collectPages(path.join(process.cwd(), pages_src_dir)))
      .pipe(parseEJS())
      .pipe(applyTemplate('./templates/default.ejs'))
      .pipe(rename({
        extname: '.html'
      }))
      .pipe(gulp.dest(dest));
  })
  .task('public', function(){
    var dest = path.join(dest_dir);
    console.log('Copy public');
    return gulp
      .src('assets/public/*')
      .pipe(newer(dest))
      .pipe(gulp.dest(dest));
  })
  .task('support', buildSupportFiles)
  .task('pages', function() {
  	seq(
  		'pages:md',
      'pages:ejs'
  	);
  })

  .task('build', [
    'css', 'files', 'fonts', 'images', 'js', 'public', 'pages'
  ], buildSupportFiles)
  .task('clean', function () {
    var dest = dest_dir;
    return del([
      dest
    ]);
  })

  .task('watch', function() {
      gulp.watch(css_src_dir, ['css']);
      gulp.watch(js_src_dir, ['js']);
      gulp.watch(pages_src_dir, ['pages']);
      gulp.watch(support_src_dir, ['support']);
  })

  .task('test', function (cb) {
    var dest = dest_dir;
    console.log('Testing...');
    pump([
      gulp.src('content/pages/**/*.md'),
      (function(){
        return through.obj(function (file, enc, cb) {
          var file_name = file.path.replace(file.base,'');
          console.log(JSON.stringify(file_name.substring(0, file_name.lastIndexOf('.')), null, 2));
          cb();
        });
      })()
      ],
      cb
    );
  })

  ;
