/*
 * Copyright 2004-2012 the Seasar Foundation and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.seasar.mayaa.impl.engine;

import org.seasar.mayaa.impl.CONST_IMPL;
import org.seasar.mayaa.impl.util.StringUtil;

/**
 * テンプレート上のmetaタグでのcharset表記、およびHTTPレスポンスのcharset表記を
 * テンプレートで指定したcharset、およびmayaaファイルのm:contentTypeで指定した
 * charsetから異なるcharset表記にするためのクラス。
 *
 * 用途はWindows-31JとShift_JISの組み合わせ以外ないと想定しているため、現在は
 * 最低限の実装。もし汎用性が必要ならばこのクラスをAPIに昇格し、ServiceProvider
 * で取得できるようにする。
 *
 * @author Koji Suga (Gluegent Inc.)
 * @version 1.1.12
 */
public class CharsetConverter {

    /** 変換が有効か */
    private static volatile boolean _enabled = false;

    /**
     * 変換の有効/無効を設定します。
     *
     * @param enabled 有効にするならtrue
     */
    public static void setEnabled(boolean enabled) {
        _enabled = enabled;
    }

    /**
     * contentType中のcharsetの値を探し、前後のindexを返します。
     * 戻り値をindexとすると、contentType.substring(index[0], index[1])で
     * charsetの値 (UTF-8など) を取り出せます。
     * 見つからない場合はnullを返します。
     *
     * @param contentType charsetを探すcontent-type
     * @return charsetの前後のindex。見つからない場合はnull
     */
    private static int[] getCharsetIndex(String contentType) {
        if (StringUtil.hasValue(contentType)) {
            String lower = contentType.toLowerCase();
            int startIndex = lower.indexOf("charset");
            if (startIndex > 0) {
                startIndex += 7; /*7 = "charset".length()*/
                final int eqIndex = contentType.indexOf("=", startIndex);
                if (eqIndex > 0) {
                    int endIndex = contentType.indexOf(";", eqIndex);
                    if (endIndex < 0) {
                        endIndex = contentType.length();
                    }
                    return new int[] { eqIndex + 1, endIndex };
                }
            }
        }
        return null;
    }

    /**
     * contentTypeからcharsetを抽出して返します。
     * charsetが存在しない場合はデフォルトのUTF-8を返します。
     * また、必要ならばcharsetをencoding用に変換して返します。
     *
     * @param contentType charset表記を含むcontentType
     * @return 抽出したcharsetをencodingとして使うための名前。
     */
    public static String extractEncoding(String contentType) {
        int[] index = getCharsetIndex(contentType);
        if (index != null) {
            String charset = contentType.substring(index[0], index[1]).trim();
            return charsetToEncoding(charset);
        }
        return CONST_IMPL.TEMPLATE_DEFAULT_CHARSET;
    }

    /**
     * contentTypeのcharsetをencodingと見なし、charsetに変換したものを返します。
     * また、charsetを含まない場合はutf-8のcharsetを付与して返します。
     * 特に変換する必要がない場合は引数をそのまま返します。
     *
     * @param contentType 変換するcharset表記を含むcontentType
     * @return 変換後のcontentType。
     */
    public static String convertContentType(String contentType) {
        int[] index = getCharsetIndex(contentType);
        if (index == null) {
            if (StringUtil.hasValue(contentType)) {
                if (contentType.charAt(contentType.length() - 1) == ';') {
                    return contentType + " charset=UTF-8";
                }
                return contentType + "; charset=UTF-8";
            }
            return contentType;
        }
        if (_enabled) {
            String charset = contentType.substring(index[0], index[1]).trim();
            return contentType.substring(0, index[0]) +
                    encodingToCharset(charset) + contentType.substring(index[1]);
        }
        return contentType;
    }

    /**
     * charset表記に対応するencoding名を取得します。
     * 特に変換する必要がない場合は引数をそのまま返します。
     *
     * @param charset 変換するcharset表記
     * @return 引数に対応するencoding名、または引数そのもの。
     */
    public static String charsetToEncoding(String charset) {
        if (_enabled && "Shift_JIS".equalsIgnoreCase(charset)) {
            return "Windows-31J";
        }
        return charset;
    }

    /**
     * encoding名に対応するcharset表記を取得します。
     * 特に変換する必要がない場合は引数をそのまま返します。
     *
     * @param encoding 変換するencoding名
     * @return 引数に対応するcharset表記、または引数そのもの。
     */
    public static String encodingToCharset(String encoding) {
        if (_enabled && "Windows-31J".equalsIgnoreCase(encoding)) {
            return "Shift_JIS";
        }
        return encoding;
    }

}
