package aipo.webservice.util;

/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2011 Aimluck,Inc.
 * http://www.aipo.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

import java.io.FileInputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Properties;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletContext;

import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.commons.codec.binary.Base64;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;

/**
 * <HR>
 * リマインダー用ユーティリティクラス
 * <p>
 * 
 * Webサービスのユーティリティ
 * <P>
 * <HR>
 * <P>
 * 
 */

public class WsUtils {

  /** ロガー */
  private static final JetspeedLogger logger =
    JetspeedLogFactoryService.getLogger(WsUtils.class.getName());

  /** Axis2メッセージコンテキスト */
  private static MessageContext mc = MessageContext.getCurrentMessageContext();

  /** [yyyy/MM/dd HH:mm:ss]形式(24時間表記)の日付フォーマット */
  private static final String YMDHHMISE_DATEFT = "yyyy/MM/dd HH:mm:ss";

  /** [yyyy/MM/dd HH:mm]形式(24時間表記)の日付フォーマット */
  private static final String YMDHHMI_DATEFT = "yyyy/MM/dd HH:mm";

  /** [yyyy/MM/dd]形式の日付フォーマット */
  private static final String YMD_DATEFT = "yyyy/MM/dd";

  /** [HH:mm]形式(24時間表記)の日付フォーマット */
  private static final String HHMI_DATEFT = "HH:mm";

  /** [HH:mm:ss]形式(24時間表記)の日付フォーマット */
  private static final String HHMISS_DATEFT = "HH:mm:ss";

  /** 暗号化キー(24byte) */
  // private static final String CRYPT_KEY = "3DESIsEncrypt3timesByDES";
  private static final String CRYPT_KEY = "AipoReminderInformation.";

  /** 暗号化形式 */
  private static final String CRYPT_TRANSFORMATION = "DESede";

  /** 暗号化アルゴリズム */
  private static final String CRYPT_ALGORITHM = "DESede";

  /**
   * メッセージコンテキスト取得
   * <p>
   * メッセージコンテキストを取得して返す。<br>
   * 
   * @return メッセージコンテキスト
   */
  public static MessageContext getMessageContext() {
    return mc;
  }

  /**
   * サーブレットコンテキスト取得
   * <p>
   * サーブレットのコンテキストを取得して返す。<br>
   * 
   * @return サーブレットのコンテキスト
   */
  public static ServletContext getServletContext() {
    return (ServletContext) mc
      .getProperty(HTTPConstants.MC_HTTP_SERVLETCONTEXT);
  }

  /**
   * プロパティファイル取得
   * <p>
   * プロパティファイルを読み込みます。<br>
   * 
   * @param path
   *            プロパティファイルのパス
   * @return Properties プロパティクラスのインスタンス
   */
  public static Properties getProperties(String path) {
    String ppath = null;
    Properties properties = new Properties();
    ppath = getServletContext().getRealPath(path);
    try {
      properties.load(new FileInputStream(ppath));
    } catch (Exception e) {
      logger.error("プロパティファイル[" + path + "]が見つかりません。", e);
    }
    return properties;
  }

  /**
   * 文字列⇒日付変換(年月日時分秒)
   * <p>
   * [yyyy/MM/dd HH:mm:ss]形式(24時間表記)の文字列を日付型に変換して返します。<br>
   * 
   * @param value
   *            日付文字列
   * @return Date 変換後の日付型。変換できない場合はnullを返す。
   * @throws Exception
   */
  public static Date getDateFromYMDHHMISE(String value) throws Exception {
    return getDate(YMDHHMISE_DATEFT, value);
  }

  /**
   * 文字列⇒日付変換(年月日時分)
   * <p>
   * [yyyy/MM/dd HH:mm]形式(24時間表記)の文字列を日付型に変換して返します。<br>
   * 
   * @param value
   *            日付文字列
   * @return Date 変換後の日付型（秒なし）。変換できない場合はnullを返す。
   * @throws Exception
   */
  public static Date getDateFromYMDHHMI(String value) {
    try {
      return getDate(YMDHHMI_DATEFT, value);
    } catch (Exception e) {
    }
    return null;
  }

  /**
   * 文字列⇒日付変換(年月日)
   * <p>
   * [yyyy/MM/dd]形式の文字列を日付型に変換して返します。<br>
   * 
   * @param value
   *            日付文字列
   * @return Date 変換後の日付型（時分秒なし）。変換できない場合はnullを返す。
   * @throws Exception
   */
  public static Date getDateFromYMD(String value) throws Exception {
    return getDate(YMD_DATEFT, value);
  }

  /**
   * 文字列⇒日付変換(時分)
   * <p>
   * [HH:mm]形式(24時間表記)の文字列を日付型に変換して返します。<br>
   * 
   * @param value
   *            日付文字列
   * @return Date 変換後の日付型（時分のみ）。変換できない場合はnullを返す。
   * @throws Exception
   */
  public static Date getDateFromHHMI(String value) throws Exception {
    return getDate(HHMI_DATEFT, value);
  }

  /**
   * 文字列⇒日付変換(時分秒)
   * <p>
   * [HH:mm:ss]形式(24時間表記)の文字列を日付型に変換して返します。<br>
   * 
   * @param value
   *            日付文字列
   * @return Date 変換後の日付型（時分秒のみ）。変換できない場合はnullを返す。
   * @throws Exception
   */
  public static Date getDateFromHHMISS(String value) throws Exception {
    return getDate(HHMISS_DATEFT, value);
  }

  /**
   * 文字列⇒日付変換(指定フォーマット)
   * <p>
   * 指定フォーマットの文字列を日付型に変換して返します。<br>
   * 
   * @param formatter
   *            日付フォーマット
   * @param value
   *            日付文字列（日付フォーマットと一致する必要がある）
   * @return Date 変換後の日付型。変換できない場合はnullを返す。
   * @throws Exception
   */
  private static Date getDate(String formatter, String value) throws Exception {
    Date result = null;
    try {
      SimpleDateFormat df = new SimpleDateFormat(formatter);
      result = df.parse(value);
    } catch (Exception e) {
      logger.error(
        formatter + "形式文字列から日付型への変換でエラーが発生しました。引数:[" + value + "]",
        e);
      throw e;
    }
    return result;
  }

  /**
   * 日付⇒文字列変換(年月日時分秒)
   * <p>
   * 日付型を[yyyy/MM/dd HH:mm:ss]形式(24時間表記)の文字列に変換して返します。<br>
   * 
   * @param value
   *            日付オブジェクト
   * @return String 変換後文字列
   */
  public static String getYMDHHMISEFromDate(Date value) {
    return getStrFromDate(YMDHHMISE_DATEFT, value);
  }

  /**
   * 日付⇒文字列変換(年月日時分)
   * <p>
   * 日付型を[yyyy/MM/dd HH:mm]形式(24時間表記)の文字列に変換して返します。<br>
   * 
   * @param value
   *            日付オブジェクト
   * @return String 変換後文字列
   */
  public static String getYMDHHMIFromDate(Date value) {
    return getStrFromDate(YMDHHMI_DATEFT, value);
  }

  /**
   * 日付⇒文字列変換(年月日)
   * <p>
   * 日付型を[yyyy/MM/dd]形式の文字列に変換して返します。<br>
   * 
   * @param value
   *            日付オブジェクト
   * @return String 変換後文字列
   */
  public static String getYMDFromDate(Date value) {
    return getStrFromDate(YMD_DATEFT, value);
  }

  /**
   * 日付⇒文字列変換(時分)
   * <p>
   * 日付型を[HH:mm]形式(24時間表記)の文字列に変換して返します。<br>
   * 
   * @param value
   *            日付オブジェクト
   * @return String 変換後文字列
   */
  public static String getHHMIFromDate(Date value) {
    return getStrFromDate(HHMI_DATEFT, value);
  }

  /**
   * 日付⇒文字列変換(時分秒)
   * <p>
   * 日付型を[HH:mm]形式(24時間表記)の文字列に変換して返します。<br>
   * 
   * @param value
   *            日付オブジェクト
   * @return String 変換後文字列
   */
  public static String getHHMISSFromDate(Date value) {
    return getStrFromDate(HHMISS_DATEFT, value);
  }

  /**
   * 日付⇒文字列変換(指定フォーマット)
   * <p>
   * 日付型を指定フォーマットの文字列に変換して返します。<br>
   * 
   * @param formatter
   *            日付フォーマット
   * @param value
   *            日付オブジェクト
   * @return String 変換後文字列
   */
  public static String getStrFromDate(String formatter, Date value) {
    String result = null;
    try {
      SimpleDateFormat df = new SimpleDateFormat(formatter);
      result = df.format(value);
    } catch (Exception e) {
      logger.error("日付型から"
        + formatter
        + "形式文字列への変換でエラーが発生しました。引数:["
        + value
        + "]", e);
    }
    return result;
  }

  /**
   * 日付変換(秒除外)
   * <p>
   * 秒とミリ秒を除いた日付を返す。<br>
   * 
   * @param value
   *            日付オブジェクト
   * @return Date 秒とミリ秒を除いた日付オブジェクト
   * @throws Exception
   */
  public static Date getSecondDeletedDate(Date value) throws Exception {
    Date result = null;
    String secondDeletedDateStr = getYMDHHMIFromDate(value);
    if (null != secondDeletedDateStr) {
      result = getDateFromYMDHHMI(secondDeletedDateStr);
    }
    return result;
  }

  /**
   * 日付変換(時分秒のみ)
   * <p>
   * 時分秒のみの日付を返す。<br>
   * 
   * @param value
   *            日付オブジェクト
   * @return Date 時分秒のみの日付オブジェクト
   * @throws Exception
   */
  public static Date getHHmissDate(Date value) throws Exception {
    Date result = null;
    String dateDeletedStr = getHHMISSFromDate(value);
    if (null != dateDeletedStr) {
      result = getDateFromHHMISS(dateDeletedStr);
    }
    return result;
  }

  /**
   * 年月日比較
   * <p>
   * 2つの日付を比較した結果を返す。<br>
   * 
   * @param date1
   *            比較対象の日付1
   * @param date2
   *            比較対象の日付2
   * @return int 年月日のみ比較する。日付1<日付2の場合：-1以下、日付1=日付2の場合：0、日付1<日付2の場合：1以上
   */
  public static int compareToYMD(Date date1, Date date2) {
    int result = 0;
    Calendar cal = Calendar.getInstance();
    cal.setTime(date1);
    int date1Year = cal.get(Calendar.YEAR);
    int date1Month = cal.get(Calendar.MONTH) + 1;
    int date1Day = cal.get(Calendar.DATE);
    cal.setTime(date2);
    int date2Year = cal.get(Calendar.YEAR);
    int date2Month = cal.get(Calendar.MONTH) + 1;
    int date2Day = cal.get(Calendar.DATE);

    result = date1Year - date2Year;
    if (0 == result) {
      result = date1Month - date2Month;
      if (0 == result) {
        result = date1Day - date2Day;
      }
    }
    return result;
  }

  /**
   * 3DESで暗号化する
   * 
   * @param clearText
   * @return
   * @throws NoSuchAlgorithmException
   * @throws NoSuchPaddingException
   * @throws InvalidKeyException
   * @throws IllegalBlockSizeException
   * @throws BadPaddingException
   * @throws InvalidAlgorithmParameterException
   */
  public static String encryptDes(String clearText)
      throws NoSuchAlgorithmException, NoSuchPaddingException,
      InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
      InvalidAlgorithmParameterException {

    SecretKey secretKey =
      new SecretKeySpec(CRYPT_KEY.getBytes(), CRYPT_ALGORITHM);

    byte[] clearTextByte = clearText.getBytes();

    Cipher cipher = Cipher.getInstance(CRYPT_TRANSFORMATION);
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);

    byte[] encryptedBytes = cipher.doFinal(clearTextByte);
    String encryptedHexString = encryptBASE64(encryptedBytes);

    return encryptedHexString;
  }

  /**
   * 3DESで暗号化されたパスワードを復号する。
   * 
   * @param encryptedHexText
   * @return
   * @throws NoSuchAlgorithmException
   * @throws NoSuchPaddingException
   * @throws InvalidKeyException
   * @throws IllegalBlockSizeException
   * @throws BadPaddingException
   * @throws IOException
   * @throws InvalidAlgorithmParameterException
   */
  public static String decryptDes(String encryptedHexText)
      throws NoSuchAlgorithmException, NoSuchPaddingException,
      InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
      IOException, InvalidAlgorithmParameterException {

    SecretKey secretKey =
      new SecretKeySpec(CRYPT_KEY.getBytes(), CRYPT_ALGORITHM);

    Cipher cipher = Cipher.getInstance(CRYPT_TRANSFORMATION);
    cipher.init(Cipher.DECRYPT_MODE, secretKey);

    byte[] decryptedByte = cipher.doFinal(decryptBASE64(encryptedHexText));
    String decryptedText = new String(decryptedByte);

    return decryptedText;
  }

  /**
   * BASE64エンコード
   * 
   * @param bytes
   * @return
   */
  private static String encryptBASE64(byte[] bytes) {
    return new String(Base64.encodeBase64(bytes));
  }

  /**
   * BASE64デコード
   * 
   * @param value
   * @return
   * @throws IOException
   */
  private static byte[] decryptBASE64(String value) throws IOException {
    return Base64.decodeBase64(value.getBytes());
  }

  public static String getMD5Digest(String key) throws NoSuchAlgorithmException {
    String strKey = key;
    MessageDigest md = MessageDigest.getInstance("MD5");
    md.update(strKey.getBytes());
    byte byDig[] = md.digest();
    String strOut = "";
    for (int i = 0; i < byDig.length; i++) {
      int d = byDig[i];
      if (d < 0) { // byte型では128～255が負値になっているので補正
        d += 256;
      }
      if (d < 16) { // 0～15は16進数で1けたになるので、2けたになるよう頭に0を追加
        strOut += "0";
      }
      strOut += Integer.toString(d, 16); // ダイジェスト値の1バイトを16進数2けたで表示
    }

    return strOut;
  }
}
