<commit_before>/* jslint node: true */
'use strict';

//	ENiGMA½
const Config	= require('./config.js').config;
const miscUtil	= require('./misc_util.js');
const ansi		= require('./ansi_term.js');
const aep		= require('./ansi_escape_parser.js');
const sauce		= require('./sauce.js');

//	deps
const fs		= require('graceful-fs');
const paths		= require('path');
const assert	= require('assert');
const iconv		= require('iconv-lite');
const _			= require('lodash');
const farmhash	= require('farmhash');

exports.getArt							= getArt;
exports.getArtFromPath					= getArtFromPath;
exports.display							= display;
exports.defaultEncodingFromExtension	= defaultEncodingFromExtension;

//	:TODO: Return MCI code information
//	:TODO: process SAUCE comments
//	:TODO: return font + font mapped information from SAUCE

const SUPPORTED_ART_TYPES = {
	//	:TODO: the defualt encoding are really useless if they are all the same ...
	//	perhaps .ansamiga and .ascamiga could be supported as well as overrides via conf
	'.ans'	: { name : 'ANSI',		defaultEncoding : 'cp437',	eof : 0x1a	},
	'.asc'	: { name : 'ASCII',		defaultEncoding : 'cp437',	eof : 0x1a  },
	'.pcb'	: { name : 'PCBoard',	defaultEncoding : 'cp437',	eof : 0x1a  },
	'.bbs'	: { name : 'Wildcat',	defaultEncoding : 'cp437',	eof : 0x1a  },

	'.amiga'	: { name : 'Amiga',			defaultEncoding	: 'amiga',	eof : 0x1a }, 
	'.txt'		: { name : 'Amiga Text',	defaultEncoding : 'cp437',	eof : 0x1a  },
	//	:TODO: extentions for wwiv, renegade, celerity, syncronet, ...
	//	:TODO: extension for atari
	//	:TODO: extension for topaz ansi/ascii.
};

function getFontNameFromSAUCE(sauce) {
	if(sauce.Character) {
		return sauce.Character.fontName;
	}
}

function sliceAtEOF(data, eofMarker) {
	let eof			= data.length;
	const stopPos	= Math.max(data.length - (256), 0);	//	256 = 2 * sizeof(SAUCE)

	for(let i = eof - 1; i > stopPos; i--) {
		if(eofMarker === data[i]) {
			eof = i;
			break;
		}
	}
	return data.slice(0, eof);
}

function getArtFromPath(path, options, cb) {
	fs.readFile(path, (err, data) => {
		if(err) {
			return cb(err);
		}

		//
		//	Convert from encodedAs -> j
		//
		const ext		= paths.extname(path).toLowerCase();
		const encoding	= options.encodedAs || defaultEncodingFromExtension(ext);

		//	:TODO: how are BOM's currently handled if present? Are they removed? Do we need to?

		function sliceOfData() {
			if(options.fullFile === true) {
				return iconv.decode(data, encoding);
			} else {
				const eofMarker = defaultEofFromExtension(ext);
				return iconv.decode(eofMarker ? sliceAtEOF(data, eofMarker) : data, encoding);
			}
		}

		function getResult(sauce) {
			const result = {
				data		: sliceOfData(),
				fromPath	: path,
			};

			if(sauce) {
				result.sauce = sauce;
			}

			return result;
		}	

		if(options.readSauce === true) {
			sauce.readSAUCE(data, (err, sauce) => {
				if(err) {
					return cb(null, getResult());
				}

				//
				//	If a encoding was not provided & we have a mapping from
				//	the information provided by SAUCE, use that.
				//
				if(!options.encodedAs) {
					/*
					if(sauce.Character && sauce.Character.fontName) {
						var enc = SAUCE_FONT_TO_ENCODING_HINT[sauce.Character.fontName];
						if(enc) {
							encoding = enc;
						}
					}
					*/
				}
				return cb(null, getResult(sauce));
			});
		} else {
			return cb(null, getResult());
		}
	});
}

function getArt(name, options, cb) {
	const ext = paths.extname(name);

	options.basePath	= miscUtil.valueWithDefault(options.basePath, Config.paths.art);
	options.asAnsi		= miscUtil.valueWithDefault(options.asAnsi, true);

	//	:TODO: make use of asAnsi option and convert from supported -> ansi

	if('' !== ext) {
		options.types = [ ext.toLowerCase() ];
	} else {
		if(_.isUndefined(options.types)) {
			options.types = Object.keys(SUPPORTED_ART_TYPES);
		} else if(_.isString(options.types)) {
			options.types = [ options.types.toLowerCase() ];
		}
	}

	//	If an extension is provided, just read the file now
	if('' !== ext) {
		const directPath = paths.join(options.basePath, name);
		return getArtFromPath(directPath, options, cb);
	}

	fs.readdir(options.basePath, (err, files) => {
		if(err) {
			return cb(err);
		}

		const filtered = files.filter( file => {
			//
			//  Ignore anything not allowed in |options.types|
			//
			const fext = paths.extname(file);
			if(!options.type.includes(fext.toLowerCase())) {
				return false;
			}

			const bn = paths.basename(file, fext).toLowerCase();
			if(options.random) {
				const suppliedBn = paths.basename(name, fext).toLowerCase();
			
				//
				//  Random selection enabled. We'll allow for
				//  basename1.ext, basename2.ext, ...
				//
				if(!bn.startsWith(suppliedBn)) {
					return false;
				}<commit_msg>Fix typo<commit_after>/* jslint node: true */
'use strict';

//	ENiGMA½
const Config	= require('./config.js').config;
const miscUtil	= require('./misc_util.js');
const ansi		= require('./ansi_term.js');
const aep		= require('./ansi_escape_parser.js');
const sauce		= require('./sauce.js');

//	deps
const fs		= require('graceful-fs');
const paths		= require('path');
const assert	= require('assert');
const iconv		= require('iconv-lite');
const _			= require('lodash');
const farmhash	= require('farmhash');

exports.getArt							= getArt;
exports.getArtFromPath					= getArtFromPath;
exports.display							= display;
exports.defaultEncodingFromExtension	= defaultEncodingFromExtension;

//	:TODO: Return MCI code information
//	:TODO: process SAUCE comments
//	:TODO: return font + font mapped information from SAUCE

const SUPPORTED_ART_TYPES = {
	//	:TODO: the defualt encoding are really useless if they are all the same ...
	//	perhaps .ansamiga and .ascamiga could be supported as well as overrides via conf
	'.ans'	: { name : 'ANSI',		defaultEncoding : 'cp437',	eof : 0x1a	},
	'.asc'	: { name : 'ASCII',		defaultEncoding : 'cp437',	eof : 0x1a  },
	'.pcb'	: { name : 'PCBoard',	defaultEncoding : 'cp437',	eof : 0x1a  },
	'.bbs'	: { name : 'Wildcat',	defaultEncoding : 'cp437',	eof : 0x1a  },

	'.amiga'	: { name : 'Amiga',			defaultEncoding	: 'amiga',	eof : 0x1a }, 
	'.txt'		: { name : 'Amiga Text',	defaultEncoding : 'cp437',	eof : 0x1a  },
	//	:TODO: extentions for wwiv, renegade, celerity, syncronet, ...
	//	:TODO: extension for atari
	//	:TODO: extension for topaz ansi/ascii.
};

function getFontNameFromSAUCE(sauce) {
	if(sauce.Character) {
		return sauce.Character.fontName;
	}
}

function sliceAtEOF(data, eofMarker) {
	let eof			= data.length;
	const stopPos	= Math.max(data.length - (256), 0);	//	256 = 2 * sizeof(SAUCE)

	for(let i = eof - 1; i > stopPos; i--) {
		if(eofMarker === data[i]) {
			eof = i;
			break;
		}
	}
	return data.slice(0, eof);
}

function getArtFromPath(path, options, cb) {
	fs.readFile(path, (err, data) => {
		if(err) {
			return cb(err);
		}

		//
		//	Convert from encodedAs -> j
		//
		const ext		= paths.extname(path).toLowerCase();
		const encoding	= options.encodedAs || defaultEncodingFromExtension(ext);

		//	:TODO: how are BOM's currently handled if present? Are they removed? Do we need to?

		function sliceOfData() {
			if(options.fullFile === true) {
				return iconv.decode(data, encoding);
			} else {
				const eofMarker = defaultEofFromExtension(ext);
				return iconv.decode(eofMarker ? sliceAtEOF(data, eofMarker) : data, encoding);
			}
		}

		function getResult(sauce) {
			const result = {
				data		: sliceOfData(),
				fromPath	: path,
			};

			if(sauce) {
				result.sauce = sauce;
			}

			return result;
		}	

		if(options.readSauce === true) {
			sauce.readSAUCE(data, (err, sauce) => {
				if(err) {
					return cb(null, getResult());
				}

				//
				//	If a encoding was not provided & we have a mapping from
				//	the information provided by SAUCE, use that.
				//
				if(!options.encodedAs) {
					/*
					if(sauce.Character && sauce.Character.fontName) {
						var enc = SAUCE_FONT_TO_ENCODING_HINT[sauce.Character.fontName];
						if(enc) {
							encoding = enc;
						}
					}
					*/
				}
				return cb(null, getResult(sauce));
			});
		} else {
			return cb(null, getResult());
		}
	});
}

function getArt(name, options, cb) {
	const ext = paths.extname(name);

	options.basePath	= miscUtil.valueWithDefault(options.basePath, Config.paths.art);
	options.asAnsi		= miscUtil.valueWithDefault(options.asAnsi, true);

	//	:TODO: make use of asAnsi option and convert from supported -> ansi

	if('' !== ext) {
		options.types = [ ext.toLowerCase() ];
	} else {
		if(_.isUndefined(options.types)) {
			options.types = Object.keys(SUPPORTED_ART_TYPES);
		} else if(_.isString(options.types)) {
			options.types = [ options.types.toLowerCase() ];
		}
	}

	//	If an extension is provided, just read the file now
	if('' !== ext) {
		const directPath = paths.join(options.basePath, name);
		return getArtFromPath(directPath, options, cb);
	}

	fs.readdir(options.basePath, (err, files) => {
		if(err) {
			return cb(err);
		}

		const filtered = files.filter( file => {
			//
			//  Ignore anything not allowed in |options.types|
			//
			const fext = paths.extname(file);
			if(!options.types.includes(fext.toLowerCase())) {
				return false;
			}

			const bn = paths.basename(file, fext).toLowerCase();
			if(options.random) {
				const suppliedBn = paths.basename(name, fext).toLowerCase();
			
				//
				//  Random selection enabled. We'll allow for
				//  basename1.ext, basename2.ext, ...
				//
				if(!bn.startsWith(suppliedBn)) {
					return false;
				}
{'fraction_non_alphanumeric': 0.15227464195450716, 'fraction_numerical': 0.008213984835720303, 'mean_word_length': 3.1507867132867133, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7502305', 'n_tokens_mistral': 3959, 'n_tokens_neox': 3455, 'n_words': 787}