var async = require('async'),
    config = require('./config'),
    child_process = require('child_process'),
    db = require('./db'),
    etc = require('../etc'),
    fs = require('fs'),
    hooks = require('../hooks'),
    path = require('path'),
    winston = require('winston');

exports.Onegai = db.Onegai;
exports.config = config;

var image_attrs = ('src thumb dims size MD5 hash imgnm spoiler realthumb vint'
		+ ' apng mid audio video duration').split(' ');
exports.image_attrs = image_attrs;

exports.send_dead_image = function (kind, filename, resp) {
	filename = dead_path(kind, filename);
	var stream = fs.createReadStream(filename);
	stream.once('error', function (err) {
		if (err.code == 'ENOENT') {
			resp.writeHead(404);
			resp.end('Image not found');
		}
		else {
			winston.error(err);
			resp.end();
		}
	});
	stream.once('open', function () {
		var h = {
			'Cache-Control': 'no-cache, no-store',
			'Expires': 'Thu, 01 Jan 1970 00:00:00 GMT',
		};
		try {
			h['Content-Type'] = require('mime').lookup(filename);
		} catch (e) {}
		resp.writeHead(200, h);
		stream.pipe(resp);
	});
};

hooks.hook_sync('extractPost', function (post) {
	if (!is_image(post))
		return;
	var image = {};
	image_attrs.forEach(function (key) {
		if (key in post) {
			image[key] = post[key];
			delete post[key];
		}
	});
	if (image.dims.split)
		image.dims = image.dims.split(',').map(parse_number);
	image.size = parse_number(image.size);
	delete image.hash;
	post.image = image;
});

function parse_number(n) {
	return parseInt(n, 10);
}

hooks.hook_sync('inlinePost', function (info) {
	var post = info.dest, image = info.src.image;
	if (!image)
		return;
	image_attrs.forEach(function (key) {
		if (key in image)
			post[key] = image[key];
	});
});

function publish(alloc, cb) {
	var mvs = [];
	var haveComp = alloc.tmps.comp && alloc.image.realthumb;
	for (var kind in alloc.tmps) {
		var src = media_path('tmp', alloc.tmps[kind]);

		// both comp and thumb go in thumb/
		var destDir = (kind == 'comp') ? 'thumb' : kind;
		// hack for stupid thumb/realthumb business
		var destKey = kind;
		if (haveComp) {
			if (kind == 'thumb')
				destKey = 'realthumb';
			else if (kind == 'comp')
				destKey = 'thumb';
		}
		var dest = media_path(destDir, alloc.image[destKey]);

		mvs.push(etc.cpx.bind(etc, src, dest));
	}
	async.parallel(mvs, cb);
}

function validate_alloc(alloc) {
	if (!alloc || !alloc.image || !alloc.tmps)
		return;
	for (var dir in alloc.tmps) {
		var fnm = alloc.tmps[dir];
		if (!/^[\w_]+$/.test(fnm)) {
			winston.warn("Suspicious filename: "
					+ JSON.stringify(fnm));
			return;
		}
	}
	return true;
}


hooks.hook("buryImage", function (info, callback) {
	if (!info.src)
		return callback(null);
	/* Just in case */
	var m = /^\d+\w*\.\w+$/;
	if (!info.src.match(m))
		return callback(etc.Muggle('Invalid image.'));
	var mvs = [mv.bind(null, 'src', info.src)];
	function try_thumb(path, t) {
		if (!t)
			return;
		if (!t.match(m))
			return callback(etc.Muggle('Invalid thumbnail.'));
		mvs.push(mv.bind(null, path, t));
	}
	try_thumb('thumb', info.thumb);
	try_thumb('thumb', info.realthumb);
	try_thumb('mid', info.mid);
	async.parallel(mvs, callback);
	function mv(p, nm, cb) {
		etc.movex(media_path(p, nm), dead_path(p, nm), cb);
	}
});

function is_image(image) {
	return image && (image.src || image.vint);
}

function media_path(dir, filename) {
	return path.join(config.MEDIA_DIRS[dir], filename);
}
exports.media_path = media_path;

function dead_path(dir, filename) {
	return path.join(config.MEDIA_DIRS.dead, dir, filename);
}

function make_dir(base, key, cb) {
	var dir = base ? path.join(base, key) : config.MEDIA_DIRS[key];
	etc.checked_mkdir(dir, cb);
}
exports._make_media_dir = make_dir;

exports.make_media_dirs = function (cb) {
	var keys = ['src', 'thumb', 'vint', 'dead'];
	if (!is_standalone())
		keys.push('tmp');
	if (config.EXTRA_MID_THUMBNAILS)
		keys.push('mid');
	async.forEach(keys, make_dir.bind(null, null), function (err) {
		if (err)
			return cb(err);
		var dead = config.MEDIA_DIRS.dead;
		var keys = ['src', 'thumb'];
		if (config.EXTRA_MID_THUMBNAILS)
			keys.push('mid');
		async.forEach(keys, make_dir.bind(null, dead), cb);
	});
}

exports.serve_image = function (req, resp) {
	var m = /^\/(src|thumb|mid|vint)(\/\d+\.\w+)$/.exec(req.url);
	if (!m)
		return false;
	var root = config.MEDIA_DIRS[m[1]];
	if (!root)
		return false;
	require('send')(req, m[2], {root: root}).pipe(resp);
	return true;
};

exports.squish_MD5 = function (hash) {
	if (typeof hash == 'string')
		hash = new Buffer(hash, 'hex');
	return hash.toString('base64').replace(/\//g, '_').replace(/=*$/, '');
};

exports.obtain_image_alloc = function (id, cb) {
	var onegai = new db.Onegai;
	onegai.obtain_image_alloc(id, function (err, alloc) {
		onegai.disconnect();
		if (err)
			return cb(err);

		if (validate_alloc(alloc))
			cb(null, alloc);
		else
			cb("Invalid image alloc");
	});
};

exports.commit_image_alloc = function (alloc, cb) {
	publish(alloc, function (err) {
		if (err)
			return cb(err);

		var o = new db.Onegai;
		o.commit_image_alloc(alloc, function (err) {
			o.disconnect();
			cb(err);
		});
	});
};

exports.note_hash = function (hash, num) {
	if (!config.DUPLICATE_COOLDOWN)
		return;
	var key = 'hash:' + hash;
	db.connect().setex(key, config.DUPLICATE_COOLDOWN, num, function (err) {
		if (err)
			winston.warn("note hash: " + err);
	});
};

var is_standalone = exports.is_standalone = db.is_standalone;
