text: ﻿/**
   * The script is part of toolbox-wsh.
   * 
   * AUTHOR:
   *  natade (http://twitter.com/natadea)
   * 
   * LICENSE:
   *  The MIT license https://opensource.org/licenses/MIT
   */
  
  /// <reference path="../../include/SenkoWSH.d.ts" />
  
  import SQLite3Schema from "./SQLite3Schema.js";
  import SQLite3IF from "./SQLite3IF.js";
  
  /**
   * SQL のタイムアウト設定
   */
  const SQL_TIME_OUT = ".timeout 1000\n";
  
  /**
   * WSH で SQLite3 を使用するためのライブラリ
   * @requires SenkoWSH
   */
  export default class SQLite3 {
  	
  	/**
  	 * 利用するデータベースをセット
  	 * @param {SFile} db_file DBファイル
  	 * @returns {Object<string, SQLite3IF>} テーブル操作用データ
  	 */
  	static use(db_file) {
  		/**
  		 * @type {Object<string, SQLite3IF>}
  		 */
  		const output = {};
  
  		// テーブル名のリストを作成
  		const table_name = SQLite3.execSQL(db_file, ".tables", "-readonly");
  		if(!table_name) {
  			console.log("Error : tables " + db_file);
  			return null;
  		}
  		const table_name_list = table_name.trim().split(/\s+/);
  
  		// テーブル内の列データを作成する
  		/**
  		 * 列名
  		 * @type {string[]}
  		 */
  		const table_info_sql = [];
  		for(let i = 0; i < table_name_list.length; i++) {
  			const key = table_name_list[i];
  			table_info_sql.push("pragma table_info(" + key + ");");
  		}
  
  		// テーブル内の列データを全て取得する
  		const table_info_data = SQLite3.execSQL(db_file, table_info_sql.join(""), "-readonly -json");
  		if(!table_info_data) {
  			console.log("Error : table_info " + db_file);
  			return null;
  		}
  		// []で括られた1テーブルごとのJSON情報から、1テーブルずつ抜き出して、データを格納する
  
  		/**
  		 * 列情報
  		 * @type {SQLite3Schema[]}
  		 */
  		const type_obj_list = [];
  
  		/**
  		 * @param {string} table_info_text
  		 * @returns {string}
  		 * @private
  		 */
  		const create_table_info = function(table_info_text) {
  			type_obj_list.push(SQLite3Schema.create(table_info_text));
  			return "";
  		}
  		table_info_data.replace(/(\[[^\]]+\])/g, create_table_info);
  
  		// IFを作成していく
  		for(let i = 0; i < table_name_list.length; i++) {
  			const key = table_name_list[i];
  			const data = new SQLite3IF(db_file, key, type_obj_list[i]);
  			output[key] = data;
  		}
  		return output;
  	}
  
  	/**
  	 * データベースをバキュームする
  	 * @param {SFile} db_file DBファイル
  	 * @returns {boolean}
  	 */
  	static vaccum(db_file) {
  		return SQLite3.execSQL(db_file, "vacuum;") !== null;
  	}
  
  	/**
  	 * SQL文を実行する
  	 * @param {SFile} db_file DBファイル
  	 * @param {string} sql SQL文
  	 * @param {string} [option] 実行時のオプション
  	 * @returns {string | null} SQL実行結果
  	 */
  	 static execSQL(db_file, sql, option) {
  		if(!SQLite3.sqlite3) {
  			console.log("Error : execSQL " + db_file + "," + sql);
  			return null;
  		}
  		const option_ = option !== undefined ? option : "";
  		const sql_file = SFile.createTempFile();
  		const sqt_text = SQL_TIME_OUT + sql;
  		sql_file.writeString(sqt_text, "utf-8", "\r\n", false);
  		const script = "\"" + SQLite3.sqlite3.getAbsolutePath() + "\" \"" + db_file.getAbsolutePath() + "\" " + option_ + " < \"" + sql_file.getAbsolutePath() + "\"";
  		const output = System.execBatchScript(script, "utf-8");
  		sql_file.remove();
  		if(output.exit_code) {
  			console.log(output.error);
  			return null;
  		}
  		return output.out;
  	 }
  
  	/**
  	 * ツールをインストールする
  	 * - `./lib/sqlite3.exe` を利用する
  	 * - ファイルが存在しない場合は自動でダウンロードする
  	 * - 本ライブラリを使用する場合は、`setSQLite3` でツールを設定するか、`install` でダウンロードする必要がある
  	 * 
  	 * @returns {boolean}
  	 */
  	static install() {
  		if(SQLite3.sqlite3) {
  			return true;
  		}
  		const exe_path = "./lib/sqlite3.exe";
  		const exe_file = new SFile(exe_path);
  		if(exe_file.isFile()) {
  			// ファイルがある場合はそれを設定する
  			return SQLite3.setSQLite3(exe_file);
  		}
  		else {
  			// ファイルがない場合は、ダウンロードしてくれる
  			new SFile("./lib").mkdirs();
  			const download_url = "https://www.sqlite.org/download.html";
  			const download_text = new SFile(download_url).readString();
  			if(download_text) {
  				const match_data = download_text.match(/'[^']+\/sqlite-tools-win32[^']+'/);
  				if(match_data) {
  					const match_text = match_data[0].toString();
  					const zip_url = "https://www.sqlite.org/" + match_text.substr(1, match_text.length - 2);
  					const zip_binary = new SFile(zip_url).readBinary();
  					if(zip_binary.length !== 0) {
  						const temp_file_1 = SFile.createTempFile();
  						temp_file_1.mkdirs();
  						const zip_file = new SFile(temp_file_1 + "\\" + new SFile(zip_url).getName());
  						zip_file.writeBinary(zip_binary);
  						const temp_file_2 = SFile.createTempFile();
  						SFile.extract(zip_file, temp_file_2);
  						/**
  						 * @type {SFile}
  						 */
  						const sqlite3_file = SFile.findFile(temp_file_2, /\\sqlite3.exe$/i);
  						if(sqlite3_file) {
  							sqlite3_file.move("./lib");
  						}
  						temp_file_1.remove();
  						temp_file_2.remove();
  						return SQLite3.setSQLite3(sqlite3_file);
  					}
  				}
  			}
  		}
  		return false;
  	}
  
  	/**
  	 * ツールを設定する
  	 * - `*.db` を操作するための `sqlite-tools-win32` に含まれる `sqlite3.exe` を設定する
  	 * @param {SFile|string} sqlite_tool_path
  	 * @returns {boolean}
  	 */
  	static setSQLite3(sqlite_tool_path) {
  		const exe_file = new SFile(sqlite_tool_path);
  		if(!exe_file.isFile() || !/sqlite3.exe$/i.test(exe_file.getAbsolutePath()) ) {
  			return false;
  		}
  		SQLite3.sqlite3 = exe_file;
  		return true;
  	}
  	
  }
  
  /**
   *  `*.db` を操作するための `sqlite-tools-win32` に含まれる `sqlite3.exe`
   * @type {SFile}
   */
  SQLite3.sqlite3 = null;
  