/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * Copyright (C) 2004-2008 Aimluck,Inc.
 * http://aipostyle.com/
 * 
 * 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 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.aimluck.eip.util;

import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.StringTokenizer;
import java.util.zip.CRC32;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import org.apache.cayenne.DataRow;
import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.query.SelectQuery;
import org.apache.jetspeed.capability.CapabilityMap;
import org.apache.jetspeed.capability.CapabilityMapFactory;
import org.apache.jetspeed.om.profile.Entry;
import org.apache.jetspeed.om.profile.Portlets;
import org.apache.jetspeed.om.registry.MediaTypeEntry;
import org.apache.jetspeed.services.Registry;
import org.apache.jetspeed.services.logging.JetspeedLogFactoryService;
import org.apache.jetspeed.services.logging.JetspeedLogger;
import org.apache.jetspeed.services.resources.JetspeedResources;
import org.apache.jetspeed.services.rundata.JetspeedRunData;
import org.apache.jetspeed.util.Base64;
import org.apache.jetspeed.util.MimeType;
import org.apache.jetspeed.util.template.JetspeedLink;
import org.apache.jetspeed.util.template.JetspeedLinkFactory;
import org.apache.turbine.util.DynamicURI;
import org.apache.turbine.util.RunData;

import sun.misc.BASE64Decoder;

import com.aimluck.eip.cayenne.om.account.EipMCompany;
import com.aimluck.eip.cayenne.om.portlet.EipMMybox;
import com.aimluck.eip.common.ALEipConstants;
import com.aimluck.eip.common.ALEipUser;
import com.aimluck.eip.orm.DatabaseOrmService;

/**
 * Aimluck EIP のユーティリティクラスです。
 * 
 */
public class ALCellularUtils {

  /** logger */
  private static final JetspeedLogger logger = JetspeedLogFactoryService
      .getLogger(ALCellularUtils.class.getName());

  /**
   * 携帯電話の固有 ID によるログイン認証時のアクセス URL に付加する値を取得します。
   * 
   * @param username
   * @return
   */
  public static String getCheckValueForCellLogin(String username, String userid) {
    if (username == null || username.length() == 0 || userid == null) {
      return "";
    }

    String marge = username + userid;
    CRC32 crc32 = new CRC32();
    crc32.update(marge.getBytes());
    long value = crc32.getValue();
    String base64value = null;
    try {
      base64value = Base64.encodeAsString(String.valueOf(value));
    } catch (Exception e) {
    }

    return (base64value == null) ? "" : base64value.toLowerCase();
  }

  /**
   * 携帯電話の固有 ID を取得します。
   * 
   * @param rundata
   * @return
   */
  public static String getCellularUid(RunData rundata) {
    String password = "";
    JetspeedRunData data = (JetspeedRunData) rundata;
    String useragent = data.getRequest().getHeader("User-Agent");
    if (useragent != null && useragent.length() > 0) {
      useragent = useragent.trim();
      CapabilityMap cm = CapabilityMapFactory.getCapabilityMap(useragent);
      MimeType mime = cm.getPreferredType();
      if (mime != null) {
        MediaTypeEntry media = (MediaTypeEntry) Registry.getEntry(
            Registry.MEDIA_TYPE, cm.getPreferredMediaType());
        if ("docomo_imode".equals(media.getName())) {
          int lastindex = useragent.lastIndexOf("ser");
          if (lastindex >= 0) {
            password = useragent.substring(lastindex, useragent.length());
          }
        } else if ("docomo_foma".equals(media.getName())) {
          StringTokenizer st = new StringTokenizer(useragent, ";");
          String token = null;
          while (st.hasMoreTokens()) {
            if ((token = st.nextToken()).startsWith("ser")) {
              password = token.trim();
              break;
            }
          }
        } else if ("au".equals(media.getName())) {
          String header = data.getRequest().getHeader("x-up-subno");
          if (header != null && header.length() > 0) {
            int index = header.indexOf("_");
            if (index >= 0) {
              password = header.substring(0, index);
            }
          }
        } else if ("vodafone".equals(media.getName())) {
          int index = useragent.indexOf("SN");
          int delta = -1;
          if (useragent.startsWith("J-PHONE/4")) {
            delta = 10;
          } else if (useragent.startsWith("J-PHONE/5")) {
            delta = 15;
          } else if (useragent.startsWith("Vodafone")) {
            delta = 15;
          } else if (useragent.startsWith("SoftBank")) {
            delta = 15;
          }
          if (index >= 0 && delta > 0) {
            password = useragent.substring(index, index + 2 + delta);
          }
        }
      }
    }
    return password;
  }

  /**
   * アクセス元の端末が携帯電話であるかを判定します。
   * 
   * @param data
   * @return
   */
  public static boolean isCellularPhone(RunData data) {
    boolean isCellularPhone = false;
    CapabilityMap cm = CapabilityMapFactory.getCapabilityMap(data.getRequest()
        .getHeader("User-Agent"));
    MimeType mime = cm.getPreferredType();
    if (mime != null) {
      MediaTypeEntry media = (MediaTypeEntry) Registry.getEntry(
          Registry.MEDIA_TYPE, cm.getPreferredMediaType());
      String mediatype = media.getName();
      if ("docomo_imode".equals(mediatype) || "docomo_foma".equals(mediatype)
          || "au".equals(mediatype) || "vodafone".equals(mediatype)) {
        isCellularPhone = true;
      }
    }
    return isCellularPhone;
  }

  /**
   * データベースの検索結果から、指定したキーに対応する値を取得します。
   * 
   * @param dataRow
   * @param key
   * @return
   */
  public static Object getObjFromDataRow(DataRow dataRow, String key) {
    String lowerKey = key.toLowerCase();
    if (dataRow.containsKey(lowerKey)) {
      return dataRow.get(lowerKey);
    } else {
      return dataRow.get(key.toUpperCase());
    }
  }

  /**
   * 携帯電話からのアクセス用 URL を取得します。
   * 
   * @param useraddr
   * @return
   */
  public static String getCellularUrl(RunData rundata, ALEipUser eipUser) {
    String url;

    String servlet_name = rundata.getServletConfig().getServletName();
    String key = eipUser.getName().getValue()
        + "_"
        + getCheckValueForCellLogin(eipUser.getName().getValue(), eipUser
            .getUserId().toString());
    EipMCompany record = ALEipUtils.getEipMCompany("1");
    String domain = ALEipUtils.getUrl(record.getIpaddress(), record.getPort()
        .intValue(), servlet_name);
    if (domain != null && domain.length() > 0) {
      url = domain + "?key=" + key;
    } else {
      url = "";
    }
    return url;
  }

  /**
   * Triple DES で文字列を暗号化します。
   * 
   * @param plain
   *            暗号化対象文字列
   * @return 暗号化文字列
   * @throws Exception
   *             暗号化時の例外
   */
  public static String crypt3Des(String key, String plain) throws Exception {
    String KEY_CRPTY_ALGORITHM = "DESede";

    // 24文字までをキーにする。
    byte[] tripleDesKeyData = new byte[24];
    byte[] kyebyte = key.getBytes();
    int len = kyebyte.length;
    for (int i = 0; i < len; i++) {
      tripleDesKeyData[i] = kyebyte[i];
    }
    SecretKey secretKey = new SecretKeySpec(tripleDesKeyData,
        KEY_CRPTY_ALGORITHM);

    Cipher cipher = Cipher.getInstance(KEY_CRPTY_ALGORITHM);
    cipher.init(Cipher.ENCRYPT_MODE, secretKey);
    byte[] encryptedMessage = cipher.doFinal(plain.getBytes());

    sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
    return encoder.encodeBuffer(encryptedMessage);
  }

  /**
   * Triple DES で文字列を復号します。
   * 
   * @param plain
   *            復号対象文字列
   * @return 復号文字列
   * @throws Exception
   *             復号時の例外
   */
  private static String decrypt3Des(String key, String plain) throws Exception {
    String KEY_CRPTY_ALGORITHM = "DESede";

    // 24文字までをキーにする。
    byte[] tripleDesKeyData = new byte[24];
    byte[] kyebyte = key.getBytes();
    int len = kyebyte.length;
    for (int i = 0; i < len; i++) {
      tripleDesKeyData[i] = kyebyte[i];
    }
    SecretKey secretKey = new SecretKeySpec(tripleDesKeyData,
        KEY_CRPTY_ALGORITHM);

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

    BASE64Decoder decoder = new sun.misc.BASE64Decoder();
    byte[] decParam = decoder.decodeBuffer(plain.trim());

    return String.valueOf(cipher.doFinal(decParam));
  }

  /**
   * 指定したエントリー名のポートレットへの URI を取得します。
   * 
   * @param rundata
   * @param portletEntryName
   *            PSML ファイルに記述されているタグ entry の要素 parent
   * @return
   */
  public static String getPortletURIForCell(RunData rundata,
      String portletEntryId) {
    try {
      Portlets portlets = ((JetspeedRunData) rundata).getProfile()
          .getDocument().getPortlets();
      if (portlets == null) {
        return null;
      }

      Entry[] entries = portlets.getEntriesArray();
      if (entries == null || entries.length <= 0) {
        return null;
      }

      int ent_length = entries.length;
      for (int j = 0; j < ent_length; j++) {
        if (entries[j].getId().equals(portletEntryId)) {
          JetspeedLink jsLink = JetspeedLinkFactory.getInstance(rundata);
          DynamicURI duri = jsLink.getLink(JetspeedLink.CURRENT, null, null,
              JetspeedLink.CURRENT, null);
          duri = duri.addPathInfo(JetspeedResources.PATH_PORTLETID_KEY,
              entries[j].getId()).addQueryData(
              JetspeedResources.PATH_ACTION_KEY, "controls.Maximize")
              .addQueryData(ALEipConstants.MODE, ALEipConstants.MODE_LIST);
          return duri.toString();
        }
      }
    } catch (Exception ex) {
      logger.error("Exception", ex);
      return null;
    }
    return null;
  }

}
