/*jshint node:true */
const clone           = require( 'gulp-clone' ),
      concat          = require( 'gulp-concat' ),
      connect         = require( 'gulp-connect' ),
      download        = require( 'gulp-download' ),
      gulp            = require( 'gulp' ),
      header          = require( 'gulp-header' ),
      jasmine         = require( 'gulp-jasmine' ),
      jshint          = require( 'gulp-jshint' ),
      merge           = require( 'merge-stream' ),
      preprocess      = require( 'gulp-preprocess' ),
      punycode        = require( 'punycode' ),
      rename          = require( 'gulp-rename' ),
      through2        = require( 'through2' ),
      transform       = require( 'gulp-transform' ),
      typescript      = require( 'gulp-typescript' ),
      uglify          = require( 'gulp-uglify' ),
      umd             = require( 'gulp-umd' ),
      JsDuck          = require( 'gulp-jsduck' ),
      KarmaServer     = require( 'karma' ).Server;



// Project configuration
const pkg = require( './package.json' ),
      banner = createBanner(),
      srcFiles = createSrcFilesList(),
      srcFilesGlob = './src/**/*.js',
      testFilesGlob = './tests/**/*.js',
      distFolder = './dist/',
      distFilename = 'Autolinker.js',
      minDistFilename = 'Autolinker.min.js',
      minDistFilePath = distFolder + minDistFilename;


gulp.task( 'default', [ 'doc', 'test' ] );
gulp.task( 'lint', lintTask );
gulp.task( 'build', [ 'lint' ], buildTask );
gulp.task( 'test', [ 'build' ], testTask );
gulp.task( 'doc', [ 'build', 'typescript' ], docTask );
gulp.task( 'serve', [ 'typescript', 'doc' ], serveTask );
gulp.task( 'typescript', typescriptTask );  // for examples
gulp.task( 'update-tld-list', updateTldRegex );


function buildTask() {
	var combinedSrcFile = gulp.src( srcFiles )
		.pipe( concat( distFilename ) )
		.pipe( umd() )
		.pipe( header( banner, { pkg: pkg } ) );

	var unminifiedFile = combinedSrcFile
		.pipe( clone() )
		.pipe( preprocess( { context: { VERSION: pkg.version, DEBUG: true } } ) )
		.pipe( gulp.dest( distFolder ) );

	var minifiedFile = combinedSrcFile
		.pipe( clone() )
		.pipe( preprocess( { context: { VERSION: pkg.version, DEBUG: false } } ) )  // removes DEBUG tagged code
		.pipe( uglify( { preserveComments: 'license' } ) )
		.pipe( rename( minDistFilename ) )
		.pipe( gulp.dest( distFolder ) );

	return merge( unminifiedFile, minifiedFile );
}


function docTask() {
	var jsduck = new JsDuck( [
		'--out',               './docs/api',
		'--title',             'Autolinker v' + pkg.version + ' API Docs',
		'--examples',          './docs/examples.json',
		'--examples-base-url', './docs/'
	] );

	return merge(
		// Move dist files into the docs/ folder so they can be served
		// by GitHub pages
		gulp.src( `${distFolder}/**/*` )
			.pipe( gulp.dest( './docs/dist' ) ),

		gulp.src( srcFilesGlob )
			.pipe( jsduck.doc() )
	);
}


function lintTask() {
	return gulp.src( [ srcFilesGlob, testFilesGlob ] )
		.pipe( jshint() )
		.pipe( jshint.reporter( 'jshint-stylish' ) )
		.pipe( jshint.reporter( 'fail' ) );  // fail the task if errors
}


function serveTask() {
	gulp.watch( './docs/examples/live-example/src/**', [ 'typescript' ] );
	gulp.watch( './src/**', [ 'doc' ] );

	connect.server();
}


function testTask( done ) {
	return new KarmaServer( {
		frameworks : [ 'jasmine' ],
		reporters  : [ 'spec' ],
		browsers   : [ 'PhantomJS' ],
		files : [
			'node_modules/lodash/lodash.js',  // spec helper
			minDistFilePath,
			testFilesGlob
		],
		singleRun : true
	}, done ).start();
}


function typescriptTask() {
	return gulp.src( [
		'./docs/examples/live-example/src/Option.ts',
		'./docs/examples/live-example/src/CheckboxOption.ts',
		'./docs/examples/live-example/src/RadioOption.ts',
		'./docs/examples/live-example/src/TextOption.ts',
		'./docs/examples/live-example/src/main.ts'
	] )
		.pipe( typescript( { noImplicitAny: true, out: 'live-example-all.js' } ) )
		.pipe( header( '// NOTE: THIS IS A GENERATED FILE - DO NOT MODIFY AS YOUR\n// CHANGES WILL BE OVERWRITTEN!!!\n\n' ) )
		.pipe( gulp.dest( './docs/examples/live-example/' ) );
}


/**
 * Creates the banner comment with license header that is placed over the
 * concatenated/minified files.
 *
 * @private
 * @return {String}
 */
function createBanner() {
	return [
		'/*!',
		' * Autolinker.js',
		' * <%= pkg.version %>',
		' *',
		' * Copyright(c) <%= new Date().getFullYear() %> <%= pkg.author %>',
		' * <%= pkg.license %> License',
		' *',
		' * <%= pkg.homepage %>',
		' */\n'
	].join( "\n" );
}


/**
 * Creates the source files list, in order.
 *
 * @private
 * @return {String[]}
 */
function createSrcFilesList() {
	return [
		'src/Autolinker.js',
		'src/Util.js',
		'src/HtmlTag.js',
		'src/RegexLib.js',
		'src/AnchorTagBuilder.js',
		'src/htmlParser/HtmlParser.js',
		'src/htmlParser/HtmlNode.js',
		'src/htmlParser/CommentNode.js',
		'src/htmlParser/ElementNode.js',
		'src/htmlParser/EntityNode.js',
		'src/htmlParser/TextNode.js',
		'src/match/Match.js',
		'src/match/Email.js',
		'src/match/Hashtag.js',
		'src/match/Phone.js',
		'src/match/Mention.js',
		'src/match/Url.js',
		'src/matcher/TldRegex.js',
		'src/matcher/Matcher.js',
		'src/matcher/Email.js',
		'src/matcher/Hashtag.js',
		'src/matcher/Phone.js',
		'src/matcher/Mention.js',
		'src/matcher/Url.js',
		'src/matcher/UrlMatchValidator.js',
		'src/truncate/TruncateEnd.js',
		'src/truncate/TruncateMiddle.js',
		'src/truncate/TruncateSmart.js'
	];
}

function dePunycodeDomain(d){
	d = d.toLowerCase();
	if (/xn--/.test(d)){
		return [d, punycode.toUnicode(d)];
	}
	return [d];
}

function notCommentLine(line){
	return !/^#/.test(line);
}

function compareLengthLongestFirst(a, b){
	var result = b.length - a.length
	if (result == 0) {
		result = a.localeCompare(b)
	}
	return result;
}

function domainsToRegex(contents){
	contents = contents
		.split('\n')
		.filter(notCommentLine)
		.map(dePunycodeDomain);
	contents = [].concat.apply([], contents);
	contents = contents.filter(function(s){ return !!s });
	contents.sort(compareLengthLongestFirst);
	contents = contents.join('|');
	contents = '/*global Autolinker */\nAutolinker.tldRegex = /(?:' + contents + ')/;\n';

	return contents;
}

function updateTldRegex(){
	return download('http://data.iana.org/TLD/tlds-alpha-by-domain.txt')
		.pipe(transform(domainsToRegex, { encoding: 'utf8' }))
		.pipe( header( '// NOTE: THIS IS A GENERATED FILE\n// To update with the latest TLD list, run `gulp update-tld-list`\n\n' ) )
		.pipe(rename(function(path){
			path.basename = "TldRegex";
			path.extname = '.js';
		}))
		.pipe(gulp.dest('./src/matcher/'));
}