/*
 * MosP - Mind Open Source Project    http://www.mosp.jp/
 * Copyright (C) MIND Co., Ltd.       http://www.e-mind.co.jp/
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
package jp.mosp.common.common;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import jp.getset.gae.driver.java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.TimeZone;

import jp.mosp.se.SeUtility;

/**
 * MosPアプリケーションを開発する上で有用なメソッドを提供する。<br><br>
 */
public final class MospUtility {

	/**
	 * mosp.jarバージョン取得。<br>
	 * @return mosp.japバージョン
	 */
	public static String getMospJarVersion() {
		return MospConst.MOSP_JAR_VERSION;
	}

	/**
	 * クラスインスタンス取得。<br>
	 * @param ctl 設定ファイル
	 * @param key 設定ファイルに登録されているキー
	 * @return キーに対応するクラスのインスタンス
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws MosPException
	 */
	public static Object getInstance(
			Properties ctl, String key
	) throws IllegalAccessException, InstantiationException, MospException {
		return getInstance(ctl.getProperty(key));
	}

	/**
	 * クラスインスタンス取得。<br>
	 * @param className クラス名
	 * @return 対応するクラスのインスタンス
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws MosPException
	 */
	public static Object getInstance(
			String className
	) throws IllegalAccessException, InstantiationException, MospException {
		Object obj = null;
		if (className == null) {
			throw new MospException(MospConst.EX_CMD_INVALID);
		}
		try {
			obj = Thread.currentThread().getContextClassLoader().loadClass(className).newInstance();
		} catch (ClassNotFoundException e) {
			throw new MospException(MospConst.EX_CTL_INVALID);
		}
		return obj;
	}

	/**
	 * 設定ファイル読込処理。<br>
	 * @param path 設定ファイルパス(絶対パス)
	 * @return 設定ファイル 
	 * @throws IOException
	 */
	public static Properties getResource(String path) throws IOException {
		Properties pr = new Properties();
		FileInputStream is = new FileInputStream(path);
		pr.loadFromXML(is);
		is.close();
		return pr;
	}

	/**
	 * 設定ファイル読込処理(追加)。<br><br>
	 * pathで指定されるディレクトリからファイル名がtargetで終わるファイルを読み込む。<br>
	 * 但し、ファイル名がファイル名がtargetで始まるファイルは対象外とする。<br>
	 * 読み込まれるファイルは、ファイル名の昇順。<br>
	 * この際、別ファイルに同じ設定があった場合は、
	 * 後に読まれたファイルの設定が有効となる。<br>
	 * @param pr     追加対象プロパティオブジェクト
	 * @param path   設定ファイルルートパス(絶対パス)
	 * @param target 対象文字列
	 * @throws IOException
	 */
	public static void addResources(Properties pr, String path, String target) throws IOException {
		String[] files = new File(path).list();
		List<String> list = new ArrayList<String>();
		for (int i = 0; i < files.length; i++) {
			String name = files[i];
			if (name.endsWith(target) && !name.startsWith(target)) {
				list.add(files[i]);
			}
		}
		Collections.sort(list);
		for (int i = 0; i < list.size(); i++) {
			FileInputStream is = new FileInputStream(path + list.get(i));
			pr.loadFromXML(is);
			is.close();
		}
	}

	/**
	 * メッセージ取得。<br>
	 * @param msg     メッセージ一覧
	 * @param msgCode メッセージコード
	 * @param rep     置換文字列(複数ある場合はカンマ区切り)
	 * @return メッセージ文字列
	 * @throws MospException
	 */
	public static String getMessage(Properties msg, String msgCode, String rep) throws MospException {
		String strMsg = msg.getProperty(msgCode);
		if (strMsg == null) {
			throw new MospException(MospConst.EX_NO_MESSAGE);
		}
		return replace(strMsg, rep);
	}

	/**
	 * メッセージの文字列置換を行う。<br>
	 * @param message 文字列
	 * @param rep     置換文字列(複数ある場合はカンマ区切り)
	 * @return メッセージ文字列
	 * @throws MospException
	 */
	public static String replace(String message, String rep) {
		if (message == null) {
			return null;
		}
		String[] aryRep = rep.split(MospConst.DATA_SEPARATOR);
		String replaced = message;
		for (int i = 0; i < aryRep.length; i++ ) {
			replaced = replaced.replaceAll("%" + String.valueOf(i + 1) + "%", aryRep[i]);
		}
		return replaced;
	}
	
	/**
	 * 処理シーケンス発行。<br>
	 * @param hasMospSeJar mosp-se.jar有無
	 * @param stored       保存されているProcSeq
	 * @return 処理シーケンス
	 */
	public static String generateProcSeq(boolean hasMospSeJar, String stored) {
		if (hasMospSeJar) {
			return SeUtility.generateProcSeq();
		}
		String procSeq = "0";
		if (stored != null && ValidateUtility.chkNumeric(stored)) {
			procSeq = String.valueOf(Integer.parseInt(stored) + 1);
		}
		return procSeq;
	}
	
	/**
	 * 暗号化(複合不可)。<br>
	 * @param value 対象文字列
	 * @return 暗号化文字列
	 * @throws NoSuchAlgorithmException
	 */
	public static String getDigest(String value) throws NoSuchAlgorithmException {
		StringBuffer digest = new StringBuffer();
		MessageDigest md = MessageDigest.getInstance("MD5");
		byte[] bytes = value.getBytes();
		md.update(bytes);
		bytes = md.digest();
		for (int i = 0; i < bytes.length; i++) {
			int b = bytes[i];
			if (b < 0) {
				b += 256;
			}
			if (b < 16) {
				digest.append("0");
			}
			digest.append(Integer.toString(b, 16));
		}
		return digest.toString();
	}


	/**
	 * 暗号化(mosp-se.jar利用)。<br>
	 * @param value        対象文字列
	 * @param hasMospSeJar mosp-se.jar有無
	 * @return 暗号化文字列
	 * @throws NoSuchAlgorithmException
	 */
	public static String encrypt(
			String value, boolean hasMospSeJar
	) throws NoSuchAlgorithmException {
		if (hasMospSeJar) {
			return SeUtility.encrypt(value);
		}
		return value;
	}


	/**
	 * 設定ファイル→JavaScriptファイル変換。<br>
	 * @param ppt 設定ファイル
	 * @param path 出力ファイルパス
	 * @param name オブジェクト名
	 * @throws FileNotFoundException
	 * @throws UnsupportedEncodingException
	 * @throws IOException
	 */
	public static void convProperties(
			Properties ppt,
			String     path,
			String     name
	) throws FileNotFoundException, UnsupportedEncodingException, IOException {
		FileOutputStream fos = new FileOutputStream(path, false);
		OutputStreamWriter osw = new OutputStreamWriter(fos, "Windows-31J");
		StringBuffer sb = new StringBuffer();
		sb.append("var ");
		sb.append(name);
		sb.append(" = new Object();");
		sb.append(MospConst.LINE_SEPARATOR);
		for (Enumeration<?> e = ppt.propertyNames(); e.hasMoreElements() ;) {
			String key = e.nextElement().toString();
			sb.append(name);
			sb.append("[\"");
			sb.append(key);
			sb.append("\"] = \"");
			sb.append(ppt.getProperty(key));
			sb.append("\";");
			sb.append(MospConst.LINE_SEPARATOR);
		}
		osw.write(sb.toString());
		osw.close();
		fos.close();
	}

	/**
	 * 日付取得。<br>
	 * @param strDate 日付文字列
	 * @return 日付(Date)
	 */
	public static Date getDate(String strDate) throws MospException {
		try{
			DateFormat df = DateFormat.getDateInstance();
			return df.parse(strDate);
		} catch(ParseException e) {
			throw new MospException(MospConst.EX_DATE_INVALID);
		}
	}

	/**
	 * 例外スタックトレースを取得する。<br>
	 * @param ex 例外オブジェクト
	 * @return 例外スタックトレース文字列
	 */
	public static String getStackTrace(Throwable ex) {
		StringWriter sw = new StringWriter();
		PrintWriter  pw = new PrintWriter(sw);
		ex.printStackTrace(pw);
		String msg = sw.toString();
		msg = msg.replaceAll(MospConst.LINE_SEPARATOR, "");
		return msg;
	}

	/**
	 * ASP設定取得取得。<br>
	 * @param cfg 設定ファイル
	 * @return ASP設定
	 * @throws MospException
	 */
	public static int getAspFlag(Properties cfg) throws MospException {
		try {
			return Integer.parseInt(cfg.getProperty(MospConst.PPT_ASP_FLAG));
		} catch(NumberFormatException e) {
			throw new MospException(MospConst.EX_ASP_PROPERTIES);
		}
	}

	// ここからはMosPに関係の無いユーティリティー

	/**
	 * 日付文字列取得。<br>
	 * @param date 時間
	 * @param format 時間
	 * @return 時間文字列(HH:MM)
	 */
	public static String getDateString(Date date, String format) {
		if (date == null) {
			return "";
		}
		SimpleDateFormat sdf = new SimpleDateFormat(format);
		return sdf.format(date);
	}

	/**
	 * 年取得。<br>
	 * @param date 日付
	 */
	public static int getYear(Date date) {
		return getCalendarValue(date, Calendar.YEAR);
	}

	/**
	 * 月取得。<br>
	 * @param date 日付
	 */
	public static int getMonth(Date date) {
		return getCalendarValue(date, Calendar.MONTH) + 1;
	}

	/**
	 * 日取得。<br>
	 * @param date 日付
	 */
	public static int getDay(Date date) {
		return getCalendarValue(date, Calendar.DATE);
	}

	/**
	 * 時間取得。<br>
	 * @param date 日付
	 */
	public static int getHour(Date date) {
		return getCalendarValue(date, Calendar.HOUR_OF_DAY);
	}

	/**
	 * 分取得。<br>
	 * @param date 日付
	 */
	public static int getMinute(Date date) {
		return getCalendarValue(date, Calendar.MINUTE);
	}

	/**
	 * カレンダーによるデータ取得。<br>
	 * @param date  日付
	 * @param field フィールド
	 */
	public static int getCalendarValue(Date date, int field) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		// GAE タイムゾーン設定
		calendar.setTimeZone(TimeZone.getTimeZone("JST"));
		return calendar.get(field);
	}

	/**
	 * 年操作。<br>
	 * @param date   操作日付
	 * @param amount 増減年数
	 * @return 操作後日付
	 */
	public static  Date addYear(Date date, int amount) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.YEAR, amount);
		return cal.getTime();
	}

	/**
	 * 月操作。<br>
	 * @param date   操作日付
	 * @param amount 増減月数
	 * @return 操作後日付
	 */
	public static  Date addMonth(Date date, int amount) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.MONTH, amount);
		return cal.getTime();
	}

	/**
	 * 日操作。<br>
	 * @param date   操作日付
	 * @param amount 増減日数
	 * @return 操作後日付
	 */
	public static  Date addDay(Date date, int amount) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		cal.add(Calendar.DAY_OF_MONTH, amount);
		return cal.getTime();
	}

	/**
	 * システム日付取得。<br>
	 * @return システム日付
	 */
	public static Date getSystemDate() {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTime();
	}

	/**
	 * 丸め処理(四捨五入等)。<br>
	 * @param value 対象値
	 * @param scale 対象位置(少数点以下：0で整数)
	 * @param mode  方法
	 * @throws Exception 
	 */
	public static double round(double value, int scale, int mode) {
		BigDecimal bd = new BigDecimal(value);
		return bd.setScale(scale, mode).doubleValue();
	}

}
