/**
 * @name WWW work crawler sub-functions
 * 
 * @fileoverview WWW work crawler functions: part of image
 * 
 * @since 2019/10/13 拆分自 CeL.application.net.work_crawler
 */

'use strict';

// --------------------------------------------------------------------------------------------

if (typeof CeL === 'function') {
	// 忽略沒有 Windows Component Object Model 的錯誤。
	CeL.env.ignore_COM_error = true;

	CeL.run({
		// module name
		name : 'application.net.work_crawler.image',

		require : 'application.net.work_crawler.',

		// 設定不匯出的子函式。
		no_extend : 'this,*',

		// 為了方便格式化程式碼，因此將 module 函式主體另外抽出。
		code : module_code
	});
}

function module_code(library_namespace) {

	// requiring
	var Work_crawler = library_namespace.net.work_crawler, crawler_namespace = Work_crawler.crawler_namespace;

	var gettext = library_namespace.locale.gettext,
	/** node.js file system module */
	node_fs = library_namespace.platform.nodejs && require('fs');

	// --------------------------------------------------------------------------------------------

	function image_path_to_url(path, server) {
		if (path.includes('://')) {
			return path;
		}

		if (!server.includes('://')) {
			// this.get_URL_options.headers.Host = server;
			server = 'http://' + server;
		}
		return server + path;
	}

	function EOI_error_path(path, XMLHttp) {
		return path.replace(/(\.[^.]*)$/, this.EOI_error_postfix
		// + (XMLHttp && XMLHttp.status ? ' ' + XMLHttp.status : '')
		+ '$1');
	}

	// 下載單一個圖片。
	// callback(image_data, status)
	function get_image(image_data, callback, images_archive) {
		// console.log(image_data);
		if (!image_data || !image_data.file || !image_data.url) {
			if (image_data) {
				image_data.has_error = true;
				image_data.done = true;
			}
			// 注意: 此時 image_data 可能是 undefined
			if (this.skip_error) {
				// gettext_config:{"id":"unspecified-image-data"}
				this.onwarning(gettext('未指定圖片資料'), image_data);
			} else {
				// gettext_config:{"id":"unspecified-image-data"}
				this.onerror(gettext('未指定圖片資料'), image_data);
			}
			if (typeof callback === 'function')
				callback(image_data, 'invalid_data');
			return;
		}

		/**
		 * 每張圖片都要檢查實際存在的圖片檔案。當之前已存在完整的圖片時，就不再嘗試下載圖片。<br />
		 * 工作機制：<br />
		 * 檢核`image_data.file`是否存在。`image_data.file`由圖片的網址URL來判別可能的延伸檔名。猜不出的會採用預設的圖片延伸檔名/副檔名.default_image_extension。
		 * 
		 * @see function process_images() @ CeL.application.net.work_crawler.chapter
		 * 
		 * 若`image_data.file`不存在，將會檢核所有可接受的圖片類別副檔名(.acceptable_types)。
		 * 每張圖片都要檢核所有可接受的圖片類別，會加大硬碟讀取負擔。 會用到 .overwrite_old_file 這個選項的，應該都是需要提報
		 * issue 的，因此這個選項不會列出來。麻煩請在個別網站遇到此情況時提報 issue，列出作品名稱以及圖片類別，以供這邊確認圖片類別。
		 * 只要存在完整無損害的預設圖片類別或是可接受的圖片類別，就直接跳出，不再嘗試下載這張圖片。否則會重新下載圖片。
		 * 當下載的圖片以之前的圖片更大時，就會覆蓋原先的圖片。
		 * 若下載的圖片類別並非預設的圖片類別(.default_image_extension)，例如預設 JPG 但得到 PNG
		 * 檔案時，會將副檔名改為實際得到的圖像格式。因此下一次下載時，需要設定 .acceptable_types 才能找得到圖片。
		 */
		var image_downloaded = node_fs.existsSync(image_data.file)
				|| this.skip_existed_bad_file
				// 檢查是否已有上次下載失敗，例如 server 上本身就已經出錯的檔案。
				&& node_fs.existsSync(this.EOI_error_path(image_data.file)), acceptable_types;

		if (!image_downloaded) {
			// 正規化 acceptable_types
			acceptable_types = image_data.acceptable_types
					|| this.acceptable_types;
			if (!acceptable_types) {
				// 未設定將不作檢查。
			} else if (typeof acceptable_types === 'string') {
				acceptable_types = acceptable_types.trim();
				if (acceptable_types === 'images') {
					// 將會測試是否已經下載過一切可接受的檔案類別。
					acceptable_types = Object.keys(this.image_types);
				} else {
					acceptable_types = acceptable_types.split('|');
				}
			} else if (!Array.isArray(acceptable_types)) {
				library_namespace.warn({
					// gettext_config:{"id":"invalid-acceptable_types-$1"}
					T : [ 'Invalid acceptable_types: %1', acceptable_types ]
				});
				acceptable_types = null;
			}

			if (acceptable_types) {
				// 檢核所有可接受的圖片類別(.acceptable_types)。
				image_downloaded = acceptable_types.some(function(extension) {
					var alternative_filename = image_data.file.replace(
							/\.[a-z\d]+$/, '.' + extension);
					if (node_fs.existsSync(alternative_filename)) {
						image_data.file = alternative_filename;
						return true;
					}
				});
			}
		}

		// 檢查壓縮檔裡面的圖片檔案。
		var image_archived, bad_image_archived;
		if (images_archive && images_archive.fso_path_hash
		// 檢查壓縮檔，看是否已經存在圖像檔案。
		&& image_data.file.startsWith(images_archive.work_directory)) {
			image_archived = image_data.file
					.slice(images_archive.work_directory.length);
			bad_image_archived = images_archive.fso_path_hash[this
					.EOI_error_path(image_archived)];
			if (image_archived && bad_image_archived) {
				images_archive.to_remove.push(bad_image_archived);
			}

			if (false) {
				console.log([ images_archive.fso_path_hash, acceptable_types,
						image_archived,
						images_archive.fso_path_hash[image_archived] ]);
			}
			image_downloaded = image_downloaded
					|| images_archive.fso_path_hash[image_archived]
					|| this.skip_existed_bad_file
					// 檢查是否已有上次下載失敗，例如 server 上本身就已經出錯的檔案。
					&& bad_image_archived;

			if (!image_downloaded
			// 可以接受的圖片類別/圖片延伸檔名/副檔名/檔案類別 acceptable file extensions
			&& acceptable_types) {
				image_downloaded = acceptable_types.some(function(extension) {
					var alternative_filename = image_archived.replace(
							/\.[a-z\d]+$/, '.' + extension);
					return images_archive.fso_path_hash[alternative_filename];
				});
			}
		}

		if (image_downloaded) {
			// console.log('get_image: Skip ' + image_data.file);
			image_data.done = true;
			if (typeof callback === 'function')
				callback(image_data, 'image_downloaded');
			return;
		}

		// --------------------------------------

		var _this = this,
		// 漫畫圖片的 URL。
		image_url = image_data.url, server = this.server_list;
		if (server) {
			server = server[server.length * Math.random() | 0];
			image_url = this.image_path_to_url(image_url, server, image_data);
		} else {
			image_url = this.full_URL(image_url);
		}
		image_data.parsed_url = image_url;
		if (!crawler_namespace.PATTERN_non_CJK.test(image_url)) {
			// 工具檔應先編碼URL。
			library_namespace.warn({
				// gettext_config:{"id":"invalid-url-must-encode-first-$1"}
				T : [ '必須先將URL編碼：%1', image_url ]
			});
			// console.trace(image_url);
			if (!/%[\dA-F]{2}/i.test(image_url))
				image_url = encodeURI(image_url);
		}

		if (!image_data.file_length) {
			image_data.file_length = [];
		}
		// console.log('get_image: image_url: ' + image_url);
		// library_namespace.set_debug(3);

		this.get_URL(image_url, function(XMLHttp) {
			// console.trace(XMLHttp.status);
			// console.log(image_data);
			if (image_data.url !== XMLHttp.responseURL) {
				// 紀錄最後實際下載的圖片網址。
				image_data.responseURL = XMLHttp.responseURL;
			}

			/** {Buffer}圖片數據的內容。 */
			var contents = XMLHttp.buffer;

			// 修正圖片結尾 tail 非正規格式之情況。
			// TODO: 應該檢測刪掉後是正確的圖片檔，才刪掉 trailing new line。
			if (_this.trim_trailing_newline && contents && contents.length > 4
			// 去掉最後的換行符號：有些圖片在檔案最後會添加上換行符號 "\r\n"，因此被判別為非正規圖片檔。
			&& contents.at(-2) === 0x0D && contents.at(-1) === 0x0A) {
				contents = contents.slice(0, -2);
			}

			if (_this.image_preprocessor) {
				// 圖片前處理程序 預處理器 image pre-processing
				// 例如修正圖片結尾非正規格式之情況。
				// 必須自行確保不會 throw，需檢查 contents 是否非 {Buffer}。
				try {
					contents = _this.image_preprocessor(contents, image_data);
				} catch (e) {
					has_error = has_error || e;
				}
				// if _this.image_preprocessor() returns `false`,
				// will treat as bad file.
				if (contents === undefined)
					contents = XMLHttp.buffer;
			}

			var has_error = !contents || !(contents.length >= _this.MIN_LENGTH)
					|| (XMLHttp.status / 100 | 0) !== 2, verified_image;

			// console.trace([ image_url, XMLHttp.responseURL ]);
			if (!image_data.is_bad
			// image_data.is_bad may be set by _this.image_preprocessor()
			&& typeof _this.is_limited_image_url === 'function') {
				// 處理特殊圖片: 檢查是否下載到 padding 用的 404 檔案。
				image_data.is_bad = _this.is_limited_image_url(
						XMLHttp.responseURL, image_data);
				if (!image_data.is_bad)
					delete image_data.is_bad;
				else if (image_data.is_bad === true)
					image_data.is_bad = 'is limited image';
			}

			if (!has_error) {
				image_data.file_length.push(contents.length);
				library_namespace.debug({
					// gettext_config:{"id":"completed-image-testing-$1"}
					T : [ '測試圖片是否完整：%1', image_data.file ]
				}, 2, 'get_image');
				var file_type = library_namespace.file_type(contents);
				verified_image = file_type && !file_type.damaged;
				if (verified_image) {
					// console.log(_this.image_types);
					if (!file_type.extensions
					//
					|| !file_type.extensions.some(function(extension) {
						return extension in _this.image_types;
					})) {
						verified_image = false;
						library_namespace
								.warn({
									T : [
									// gettext_config:{"id":"unable-to-process-image-file-of-type-$2-$1"}
									file_type.type ? '無法處理類型為 %2 之圖片檔：%1'
									// gettext_config:{"id":"unable-to-determine-the-type-of-image-file-$1"}
									: '無法判別圖片檔之類型：%1', image_data.file,
											file_type.type ]
								});
					}

					var original_extension
					//
					= image_data.file.match(/[^.]*$/)[0].toLowerCase();
					if (file_type.extensions ?
					//
					!image_data.file.endsWith('.' + file_type.extension)
					// accept '.jpeg' as alias of '.jpg'
					&& !file_type.extensions.includes(original_extension)
					// 無法判別圖片檔類型時，若原副檔名為圖片檔案類別則採用之。
					: !(original_extension in _this.image_types)) {
						// 依照所驗證的檔案格式改變副檔名。
						image_data.file = image_data.file.replace(/[^.]+$/,
						// e.g. .png
						file_type.extension
						// 若是沒有辦法判別延伸檔名，那麼就採用預設的圖片延伸檔名。
						|| _this.default_image_extension);
					}
				}
			}
			// verified_image===true 則必然(!!has_error===false)
			// has_error表示下載過程發生錯誤，光是檔案損毀，不會被當作has_error!
			// has_error則必然(!!verified_image===false)

			if (false) {
				console.log([ _this.skip_error, _this.MAX_ERROR_RETRY,
				//
				_this.MIN_LENGTH, has_error, _this.skip_error
				//
				&& image_data.error_count === _this.MAX_ERROR_RETRY ]);
				// 出錯次數
				library_namespace.log({
					// gettext_config:{"id":"number-of-errors-$1"}
					T : [ '{{PLURAL:%1|%1}} 次錯誤', image_data.error_count ]
				});
			}
			if (verified_image || image_data.is_bad || _this.skip_error
			// 有出問題的話，最起碼都需retry足夠次數。
			&& image_data.error_count === _this.MAX_ERROR_RETRY
			//
			|| _this.allow_EOI_error
			//
			&& image_data.file_length.length > _this.MAX_EOI_ERROR) {
				// console.log(image_data.file_length);
				if (verified_image || image_data.is_bad || _this.skip_error
				// skip error 的話，不管有沒有下載/獲取過檔案(包括404圖像不存在)，依然 pass。
				// && image_data.file_length.length === 0
				//
				|| image_data.file_length.cardinal_1()
				// ↑ 若是每次都得到相同的檔案長度，那就當作來源檔案本來就有問題。
				&& (_this.skip_error || _this.allow_EOI_error
				//
				&& image_data.file_length.length > _this.MAX_EOI_ERROR)) {
					// 圖片下載過程結束，不再嘗試下載圖片:要不是過關，要不就是錯誤太多次了。
					var bad_file_path = _this.EOI_error_path(image_data.file,
							XMLHttp);
					if (has_error || image_data.is_bad
							|| verified_image === false) {
						image_data.file = bad_file_path;
						image_data.has_error = true;
						if (_this.preserve_bad_image) {
							library_namespace.warn([ {
								T : has_error ? contents
								// gettext_config:{"id":"force-non-image-files-to-be-saved-as-images"}
								? '強制將非圖片檔儲存為圖片。'
								// gettext_config:{"id":"force-empty-content-to-be-saved-as-an-image"}
								: '強制將空內容儲存為圖片。'
								// assert: (!!verified_image===false)
								// 圖檔損壞: e.g., Do not has EOI
								// gettext_config:{"id":"force-storage-of-damaged-image"}
								: '強制儲存損壞的圖片。'
							}, XMLHttp.status
							// 狀態碼正常就不顯示。
							&& (XMLHttp.status / 100 | 0) !== 2 ? {
								// gettext_config:{"id":"http-status-code-$1"}
								T : [ 'HTTP status code %1.', XMLHttp.status ]
							} : '',
							// 顯示 crawler 程式指定的錯誤。
							image_data.is_bad ? {
								// gettext_config:{"id":"error-$1"}
								T : [ 'Error: %1.', image_data.is_bad ]
							} : '', contents ? {
								// gettext_config:{"id":"file-size-$1"}
								T : [ 'File size: %1.',
								//
								CeL.to_KiB(contents.length) ]
							} : '',
							//
							': ' + image_data.file + '\n← ' + image_url ]);
						}
						if (!contents
						// 404之類，就算有內容，也不過是錯誤訊息頁面。
						|| (XMLHttp.status / 100 | 0) === 4) {
							contents = '';
						}
					} else {
						// pass, 過關了。
						if (node_fs.existsSync(bad_file_path)) {
							library_namespace.info({
								// gettext_config:{"id":"delete-corrupted-old-image-file-$1"}
								T : [ '刪除損壞的舊圖片檔：%1', bad_file_path ]
							});
							library_namespace.fs_remove(bad_file_path);
						}
						if (bad_image_archived) {
							// 登記壓縮檔內可以刪除的損壞圖檔。
							images_archive.to_remove.push(bad_image_archived);
						}
					}

					var old_file_status, old_archived_file =
					// image_data.has_error?bad_image_archived:image_archived
					image_archived || bad_image_archived;
					try {
						old_file_status = node_fs.statSync(image_data.file);
					} catch (e) {
						// old/bad file not exist
					}

					if (old_archived_file && (!old_file_status
					//
					|| old_archived_file.size > old_file_status.size)) {
						// 壓縮檔內的圖像質量更好的情況，那就採用壓縮檔的。
						if (old_file_status
								&& old_archived_file.size < contents.length) {
							library_namespace.warn({
								T : [ _this.archive_images
								// gettext_config:{"id":"the-quality-of-the-image-in-the-archive-is-better-than-in-the-directory-but-will-be-overwritten-after-downloading-$1"}
								? '壓縮檔內的圖片品質比目錄中的更好，但在下載完後將可能在壓縮時被覆蓋：%1'
								// gettext_config:{"id":"the-quality-of-the-image-in-the-archive-is-better-than-in-the-directory-$1"}
								: '壓縮檔內的圖片品質比目錄中的更好：%1',
								//
								old_archived_file.path ]
							});
						}

						old_file_status = old_archived_file;
					}

					if (!old_file_status || _this.overwrite_old_file
					// 得到更大的檔案，寫入更大的檔案。
					&& !(old_file_status.size >= contents.length)) {
						if (_this.image_post_processor) {
							// 圖片後處理程序 image post-processing
							contents = _this.image_post_processor(contents,
									image_data
							// , images_archive
							)
									|| contents;
						}

						if (!image_data.has_error || _this.preserve_bad_image) {
							library_namespace.debug({
								// gettext_config:{"id":"save-image-data-to-your-hard-drive-$1"}
								T : [ '保存圖片數據到硬碟上：%1', image_data.file ]
							}, 1, 'get_image');
							// TODO: 檢查舊的檔案是不是文字檔。例如有沒有包含 HTML 標籤。
							try {
								node_fs
										.writeFileSync(image_data.file,
												contents);
							} catch (e) {
								library_namespace.error(e);
								// gettext_config:{"id":"unable-to-write-to-image-file-$1"}
								var message = [ gettext('無法寫入圖片檔案 [%1]。',
										image_data.file) ];
								if (e.code === 'ENOENT') {
									message.push(gettext(
									// TODO: show chapter_directory 當前作品章節目錄：
									// gettext_config:{"id":"it-may-be-because-the-download-directory-of-the-work-has-changed-and-the-cache-data-points-to-the-old-location-that-does-not-exist"}
									'可能因為作品下載目錄改變了，而快取資料指向不存在的舊位置。'));
								} else {
									message.push(gettext(
									// gettext_config:{"id":"it-may-be-because-the-work-information-cache-is-different-from-the-structure-of-the-work-chapter-on-the-current-website"}
									'可能因為作品資訊快取與當前網站上之作品章節結構不同。'));
								}
								message.push(gettext(
								// https://github.com/kanasimi/work_crawler/issues/278
								// gettext_config:{"id":"if-you-have-downloaded-this-work-before-please-save-the-original-work-catalog-or-rename-the-work-cache-file-(the-work-id.json-under-the-work-directory)-and-try-the-new-download"}
								'若您之前曾經下載過本作品的話，請封存原有作品目錄，或將作品資訊快取檔（作品目錄下的 作品id.json）改名之後嘗試全新下載。'
								//
								));
								_this.onerror(message.join('\n'), image_data);
								if (typeof callback === 'function') {
									callback(image_data,
											'image_file_write_error');
								}
								return Work_crawler.THROWED;
							}
						}
					} else if (old_file_status
							&& old_file_status.size > contents.length) {
						library_namespace.log({
							T : [
									// gettext_config:{"id":"there-is-a-large-old-file-($2)-that-will-not-be-overwritten-$1"}
									'存在較大的舊檔 (%2)，將不覆蓋：%1',
									image_data.file,
									old_file_status.size + '>'
											+ contents.length ]
						});
					}
					image_data.done = true;
					if (typeof callback === 'function')
						callback(image_data/* , 'OK' */);
					return;
				}
			}

			// 有錯誤。下載圖像錯誤時報錯。
			var message;
			if (verified_image === false) {
				// 圖檔損壞: e.g., Do not has EOI
				message = [ {
					// gettext_config:{"id":"image-damaged"}
					T : '圖檔損壞：'
				} ];
			} else {
				// 圖檔沒資格驗證。
				message = [ {
					// gettext_config:{"id":"failed-to-get-image"}
					T : '無法取得圖片。'
				}, XMLHttp.status ? {
					// gettext_config:{"id":"http-status-code-$1"}
					T : [ 'HTTP status code %1.', XMLHttp.status ]
				} : '', {
					// gettext_config:{"id":"image-without-content"}
					T : !contents ? '圖片無內容：' : [
					//
					contents.length < _this.MIN_LENGTH
					// gettext_config:{"id":"$1-bytes-too-small"}
					? '檔案過小，僅 %1 {{PLURAL:%1|位元組}}：'
					// gettext_config:{"id":"$1-bytes"}
					: '檔案僅 %1 {{PLURAL:%1|位元組}}：', contents.length ]
				} ];
			}
			message.push(image_url + '\n→ ' + image_data.file);
			library_namespace.warn(message);
			// Release memory. 釋放被占用的記憶體。
			message = null;

			if (image_data.error_count === _this.MAX_ERROR_RETRY) {
				image_data.has_error = true;
				// throw new Error(_this.id + ': ' +
				// gettext('MESSAGE_NEED_RE_DOWNLOAD'));
				library_namespace.log(_this.id + ': '
				// gettext_config:{"id":"message_need_re_download"}
				+ gettext('MESSAGE_NEED_RE_DOWNLOAD'));
				// console.log('error count: ' + image_data.error_count);
				if (contents && contents.length > 10
				//
				&& contents.length < _this.MIN_LENGTH
				// 檔案有驗證過，只是太小時，應該不是 false。
				&& verified_image !== false
				// 就算圖像是完整的，只是比較小，HTTP status code 也應該是 2xx。
				&& (XMLHttp.status / 100 | 0) === 2) {
					library_namespace.warn([ {
						// gettext_config:{"id":"perhaps-the-image-is-complete-just-too-small-and-not-up-to-standard-such-as-an-almost-blank-image"}
						T : '或許圖片是完整的，只是過小而未達標，例如幾乎為空白之圖片。'
					}, {
						// gettext_config:{"id":"work_crawler-skip-image-error-prompt"}
						T : [ 'work_crawler-skip-image-error-prompt',
						//
						contents.length,
						//
						JSON.stringify(_this.EOI_error_postfix) ]
					} ]);

				} else if (image_data.file_length.length > 1
						&& !image_data.file_length.cardinal_1()) {
					library_namespace.warn([ {
						// gettext_config:{"id":"the-downloaded-image-is-different-in-size-$1"}
						T : [ '下載所得的圖片大小不同：%1。', image_data.file_length ]
					}, {
						// gettext_config:{"id":"if-it-is-not-because-the-website-cuts-off-the-connection-early-then-you-may-need-to-increase-the-time-limit-to-provide-enough-time-to-download-the-image"}
						T : '若非因網站提早截斷連線，那麼您或許需要增長時限來提供足夠的時間下載圖片？'
					} ]);
					// TODO: 提供續傳功能。
					// e.g., for 9mdm.js→dagu.js 魔剑王 第59话 4392-59-011.jpg

				} else if (!_this.skip_error) {
					library_namespace.info([ {
						// gettext_config:{"id":"if-the-error-persists-you-can-set-skip_error=true-to-ignore-the-image-error"}
						T : '若錯誤持續發生，您可以設定 skip_error=true 來忽略圖片錯誤。'
					}, {
						// gettext_config:{"id":"you-must-set-the-skip_error-or-allow_eoi_error-option-to-store-corrupted-files"}
						T : '您必須設定 skip_error 或 allow_EOI_error 選項，才會儲存損壞的檔案。'
					}, {
						// gettext_config:{"id":"if-you-need-to-re-download-the-section-that-failed-to-download-before-turn-on-the-recheck-option"}
						T : '若您需要重新下載之前下載失敗的章節，請開啟 recheck 選項。'
					} ]);
				}

				// gettext_config:{"id":"failed-to-download-image"}
				_this.onerror(gettext('圖片下載錯誤'), image_data);
				// image_data.done = false;
				if (typeof callback === 'function')
					callback(image_data, 'image_download_error');
				return Work_crawler.THROWED;
				// 網頁介面不可使用process.exit()，會造成白屏
				// process.exit(1);
			}

			image_data.error_count = (image_data.error_count | 0) + 1;
			library_namespace.log([ 'get_image: ', {
				// gettext_config:{"id":"retry-$1-$2"}
				T : [ 'Retry %1/%2',
				//
				image_data.error_count, _this.MAX_ERROR_RETRY ]
			}, '...' ]);
			var get_image_again = function() {
				_this.get_image(image_data, callback, images_archive);
			}
			if (image_data.time_interval > 0) {
				library_namespace.log_temporary([ 'get_image: ', {
					// gettext_config:{"id":"waiting-for-$2-and-retake-the-image-$1"}
					T : [ '等待 %2 之後再重新取得圖片：%1', image_data.url,
					//
					library_namespace.age_of(0, image_data.time_interval, {
						digits : 1
					}) ]
				} ]);
				setTimeout(get_image_again, image_data.time_interval);
			} else
				get_image_again();

		}, null, Object.assign({
			/**
			 * 最多平行下載/獲取檔案(圖片)的數量。
			 * 
			 * <code>
			incase "MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 connect listeners added. Use emitter.setMaxListeners() to increase limit"
			</code>
			 */
			max_listeners : 300,
			fetch_type : 'image'
		}, this.get_URL_options, image_data.get_URL_options), 'buffer');
	}

	// --------------------------------------------------------------------------------------------

	// export 導出.

	// @instance
	Object.assign(Work_crawler.prototype, {
		// 可接受的圖片類別（延伸檔名）。以 "|" 字元作分隔，如 "webp|jpg|png"。未設定將不作檢查。輸入 "images"
		// 表示接受所有圖片。
		// 若下載的圖片不包含在指定類型中，則會視為錯誤。本工具只能下載特定幾種圖片類型。本選項僅供檢查圖片，非用來挑選想下載的圖片類型。
		// {Array|String}可以接受的圖片類別/圖片延伸檔名/副檔名/檔案類別 acceptable file extensions。
		// acceptable_types : 'images',
		// acceptable_types : 'png',
		// acceptable_types : 'webp|png',
		// acceptable_types : ['webp', 'png'],

		// 當圖片不存在 EOI (end of image) 標記，或是被偵測出非圖片時，依舊強制儲存檔案。
		// allow image without EOI (end of image) mark. default:false
		// allow_EOI_error : true,
		// 圖片檔案下載失敗處理方式：忽略/跳過圖片錯誤。當404圖片不存在、檔案過小，或是被偵測出非圖片(如不具有EOI)時，依舊強制儲存檔案。default:false
		// skip_error : true,
		//
		// 若已經存在壞掉的圖片，就不再嘗試下載圖片。default:false
		// skip_existed_bad_file : true,
		//
		// 循序逐個、一個個下載圖片。僅對漫畫有用，對小說無用。小說章節皆為逐個下載。 Download images one by one.
		// default: 同時下載本章節中所有圖片。 Download ALL images at the same time.
		// 若設成{Natural}大於零的數字(ms)或{String}時間長度，那會當成下載每張圖片之時間間隔 time_interval。
		// cf. .chapter_time_interval
		// one_by_one : true,
		//
		// e.g., '2-1.jpg' → '2-1 bad.jpg'
		EOI_error_postfix : ' bad',
		// 加上有錯誤檔案之註記。
		EOI_error_path : EOI_error_path,

		image_path_to_url : image_path_to_url,

		get_image : get_image
	});

	// 不設定(hook)本 module 之 namespace，僅執行 module code。
	return library_namespace.env.not_to_extend_keyword;
}
