/*
 * Aipo is a groupware program developed by Aimluck,Inc.
 * http://aipostyle.com/
 *
 * Copyright(C) 2011 avanza Co.,Ltd. All rights reserved.
 * http://www.avnz.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 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.mail.util;

import java.io.UnsupportedEncodingException;

import javax.mail.MessagingException;
import javax.mail.Part;
import javax.mail.internet.HeaderTokenizer;
import javax.mail.internet.ParseException;

import com.sk_jp.mail.MailUtility;

/**
 * com.sk_jp.mail.MailUtilityクラスをRFC2231規格に合わせた改修をした拡張クラスです。
 */
public class MailUtilityRFC2231 {

  static class Encoding {
    String encoding;

    String lang;

    Encoding() {
      encoding = "us-ascii";
      lang = "";
    }
  }

  static class RFC2047Decoder {
    private void parse() {
      for (; hasEncodedWord(); parseWord()) {
        String s = source.substring(pos, startIndex);
        if (MailUtility.indexOfNonLWSP(s, 0, false) > -1) {
          buf.append(s);
        }
      }
      buf.append(source.substring(pos));
    }

    private boolean hasEncodedWord() {
      startIndex = source.indexOf("=?", pos);
      if (startIndex == -1) {
        return false;
      }
      // change start 運用障害96
      // endIndex = source.indexOf("?=", startIndex + 2);
      endIndex = indexOfIgnoreQEncodeSymbol(source);
      // change end
      if (endIndex == -1) {
        return false;
      } else {
        endIndex += 2;
        return true;
      }
    }

    // add start 運用障害96
    /**
     * String#indexOfで、Qエンコードの"?Q?="を無視するように拡張
     * 
     * @param s
     *            ソース文字列
     * 
     * @return 位置
     * 
     */
    private int indexOfIgnoreQEncodeSymbol(String s) {
      int index = s.indexOf("?Q?=", startIndex + 2);
      if (index == -1) {
        return s.indexOf("?=", startIndex + 2);
      }
      return s.indexOf("?=", index + 4);
    }

    // add end

    private void parseWord() {
      int i = startIndex + 2;
      int j = source.indexOf('?', i);
      if (j == endIndex - 2) {
        throw new RuntimeException();
      }
      i = j + 1;
      j = source.indexOf('?', i);
      if (j == endIndex - 2) {
        throw new RuntimeException();
      }
      source.substring(j + 1, endIndex - 2);
      buf.append(source.substring(startIndex, endIndex));
      pos = endIndex;
    }

    public String get() {
      return new String(buf);
    }

    private final String source;

    private final StringBuffer buf;

    private int pos;

    private int startIndex;

    private int endIndex;

    public RFC2047Decoder(String s) {
      pos = 0;
      source = s;
      buf = new StringBuffer(s.length());
      parse();
    }
  }

  public static String getFileName(Part part) throws MessagingException, UnsupportedEncodingException {

    String as[] = part.getHeader("Content-Disposition");
    String s;
    if (as == null || as.length < 1 || (s = getParameter(as[0], "filename")) == null) {
      s = part.getFileName();
      if (s != null) {
        return decodeParameterSpciallyJapanese(s);
      } else {
        return null;
      }
    } else {
      // add start 2012.2.15
      // \t,\r,\nを削除
      s = unfold(s);
      // add end
      return s;
    }
  }

  public static String getParameter(String s, String s1) throws javax.mail.internet.ParseException, UnsupportedEncodingException {
    HeaderTokenizer headertokenizer;
    StringBuffer stringbuffer;
    Encoding encoding;
    if (s == null) {
      return null;
    }
    s = decodeParameterSpciallyJapanese(s);
    headertokenizer = new HeaderTokenizer(s, ";=\t ", true);
    stringbuffer = new StringBuffer();
    encoding = new Encoding();
    String s2 = null;
    String s3 = null;

    return getL1(s1, s2, s3, headertokenizer, stringbuffer, encoding);
  }

  private static String getL1(String s1, String s2, String s3, HeaderTokenizer headertokenizer, StringBuffer stringbuffer, Encoding encoding)
      throws javax.mail.internet.ParseException, UnsupportedEncodingException {

    javax.mail.internet.HeaderTokenizer.Token token;
    do {
      token = headertokenizer.next();
      if (token.getType() == -4) {
        // break;
        return getL3(stringbuffer, encoding);
      } /* Loop/switch isn't completed */
    } while (token.getType() != 59);
    token = headertokenizer.next();
    checkType(token);
    s2 = token.getValue();
    token = headertokenizer.next();

    if (token.getType() != 61) {
      throw new javax.mail.internet.ParseException("Illegal token : " + token.getValue());
    }

    token = headertokenizer.next();
    checkType(token);
    s3 = token.getValue();
    if (s2.equalsIgnoreCase(s1)) {
      return s3;
    }

    int i = s1.length();

    if (s2.startsWith(s1) && s2.charAt(i) == '*') {
      return getL2(s1, s2, s3, headertokenizer, stringbuffer, encoding, i);
    } else {
      return getL1(s1, s2, s3, headertokenizer, stringbuffer, encoding);
    }
  }

  private static String getL2(String s1, String s2, String s3, HeaderTokenizer headertokenizer, StringBuffer stringbuffer, Encoding encoding, int i)
      throws javax.mail.internet.ParseException, UnsupportedEncodingException {
    int j;
    j = s2.length() - 1;
    if (s2.charAt(j) == '*') {
      if (i == j || s2.charAt(i + 1) == '0') {
        // stringbuffer.append(decodeRFC2231(s3, encoding, true));
        stringbuffer.append(s3);
      } else {
        // stringbuffer.append(decodeRFC2231(s3, encoding, false));
        stringbuffer.append(s3);
      }
    } else {
      stringbuffer.append(s3);
    }
    if (i != j) {
      return getL1(s1, s2, s3, headertokenizer, stringbuffer, encoding);
    } else {
      return getL3(stringbuffer, encoding);
    }
  }

  private static String getL3(StringBuffer stringbuffer, Encoding encoding) throws ParseException, UnsupportedEncodingException {
    if (stringbuffer.length() == 0) {
      return null;
    }
    String er = new String(stringbuffer);
    er = decodeRFC2231(er, encoding, true);
    return er;
  }

  private static void checkType(javax.mail.internet.HeaderTokenizer.Token token) throws javax.mail.internet.ParseException {
    int i = token.getType();
    if (i != -1 && i != -2) {
      throw new javax.mail.internet.ParseException("Illegal token : " + token.getValue());
    } else {
      return;
    }
  }

  private static String decodeRFC2231(String s, Encoding encoding, boolean flag) throws javax.mail.internet.ParseException, UnsupportedEncodingException {
    StringBuffer stringbuffer;
    int i;
    stringbuffer = new StringBuffer();
    i = 0;
    if (flag) {
      int j = s.indexOf('\'');
      if (j > 0) {
        encoding.encoding = s.substring(0, j);
        j++;
        i = s.indexOf('\'', j);
        if (i < 0) {
          throw new javax.mail.internet.ParseException("lang tag area was missing.");
        }
        encoding.lang = s.substring(j, i);
        i++;
      }
    }
    for (; i < s.length(); i++) {
      if (s.charAt(i) == '%') {
        stringbuffer.append((char) Integer.parseInt(s.substring(i + 1, i + 3), 16));
        i += 2;
      } else {
        stringbuffer.append(s.charAt(i));
      }
    }

    // ファイル名文字化け対応
    // 2012.2.3
    String encode_char = encoding.encoding;
    encode_char = encode_char.toLowerCase();
    // change start 2012.6.21 文字化け対応 運用障害83
    // if (ALMailUtils.CHARSET_GB2312.equals(encode_char)
    // || ALMailUtils.CHARSET_GB18030.equals(encode_char)) {
    // return new String(
    // (new String(stringbuffer)).getBytes("ISO-8859-1"),
    // encoding.encoding);
    // } else {
    // byte[] sjisByte =
    // ALMailUtils.JIStoSJIS(new String(stringbuffer).getBytes());
    // return new String(sjisByte, ALMailUtils.WINDOWS_31J);
    // }
    if (!"iso-2022-jp".equals(encode_char)) {
      // add start 2次開発 要件No.20 キャラクタセットCP932メール対応
      if (ALMailUtils.CP932.equals(encode_char)) {
        encoding.encoding = ALMailUtils.WINDOWS_31J;
      }
      // add end
      return new String((new String(stringbuffer)).getBytes("ISO-8859-1"), encoding.encoding);
    } else {
      byte[] sjisByte = ALMailUtils.JIStoSJIS(new String(stringbuffer).getBytes());
      return new String(sjisByte, ALMailUtils.WINDOWS_31J);
    }
    // change end
  }

  private static String decodeParameterSpciallyJapanese(String s) throws javax.mail.internet.ParseException, UnsupportedEncodingException {
    s = (new RFC2047Decoder(s)).get();
    return ALMailUtils.decodeText(s);
  }

  // add start 2012.2.15
  public static String unfold(String s) {
    if (s == null) {
      return null;
    }
    StringBuffer stringbuffer = new StringBuffer();
    boolean flag = false;
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
      if (flag) {
        if (MailUtility.isLWSP(c)) {
          continue;
        }
        flag = false;
      }
      if (c != '\r' && c != '\n' && c != '\t') {
        stringbuffer.append(c);
      } else {
        stringbuffer.append("");
        flag = true;
      }
    }
    return new String(stringbuffer);
  }

  // add end

  public static int indexOfLWSP(String s, int i, boolean flag, char c) {
    byte byte0 = 1;
    if (flag) {
      byte0 = -1;
    }
    for (int j = i; j >= 0 && j < s.length(); j += byte0) {
      char c1 = s.charAt(j);
      if (isLWSP(c1) || c1 == c) {
        return j;
      }
    }
    return -1;
  }

  public static boolean isLWSP(char c) {
    return c == '\r' || c == '\n' || c == ' ' || c == '\t';
  }
}