/*
 * blancoIg
 * Copyright (C) 2004-2005 Yasuo Nakanishi
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 */
package blanco.struts.runtime.validator;

import java.io.UnsupportedEncodingException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

/**
 * @author Yasuo Nakanishi
 */
public class Validator {

    private static final DateFormat _yyyyMmDd = new SimpleDateFormat("yyyyMMdd");

    private static final DateFormat _yyMmDd = new SimpleDateFormat("yyMMdd");

    private static final DateFormat _mmDd = new SimpleDateFormat("MMdd");

    private String getIlleagalCharactor(String value, String format, String name) {
        StringBuffer sb = new StringBuffer();
        sb.append(name);
        sb.append("に");
        sb.append(format);
        sb.append("以外の文字が渡されました。データ:");
        sb.append(value);
        return sb.toString();
    }

    private String getIlleagalDateFormat(String value, String format,
            String name) {
        StringBuffer sb = new StringBuffer();
        sb.append(name);
        sb.append("に");
        sb.append(format);
        sb.append("不正な日付が渡されました。データ:");
        sb.append(value);
        return sb.toString();
    }

    public static boolean validateHalfWidth(String data) {
        boolean result = false;
        if (data != null) {
            int byteSize = getByteLength(data);
            result = data.length() == byteSize;
        } else {
            result = true;
        }
        return result;
    }

    public static boolean validateFullWidth(String data) {
        boolean result = false;
        if (data != null) {
            int byteSize = getByteLength(data);
            result = data.length() == byteSize / 2;
        } else {
            result = true;
        }
        return result;
    }

    protected static int getByteLength(String value) {
        int result = 0;
        try {
            result = value.getBytes("Windows-31J").length;
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    public static boolean validateHalfWidthNumber(String value) {
        return isEmpty(value) || !value.matches(".*[\\D]+.*");
    }

    public static boolean validateHalfWidthAlphabet(String value) {
        return isEmpty(value) || !value.matches(".*[^a-zA-Z]+.*");
    }

    public static boolean validateHalfWidthUpperCaseAlphabet(String value) {
        return !value.matches(".*[^A-Z]+.*");
    }

    public static boolean validateHalfWidthLowerCaseAlphabet(String value) {
        return isEmpty(value) || !value.matches(".*[^a-z]+.*");
    }

    public static boolean validateHalfWidthAlphabetNumber(String value) {
        return isEmpty(value) || !value.matches(".*[^a-zA-Z0-9]+.*");
    }

    public static boolean validateHalfWidthAlphabetNumberSymbol(String value) {
        return isEmpty(value) || !value.matches(".*[^\\x20-~]+.*");
    }

    public static boolean validateHalfWidthKatakana(String value) {
        return isEmpty(value) || !value.matches(".*[^\uFF61-\uFF9F]+.*");
    }

    public static boolean validateFullWidthUpperCaseAlphabet(String value) {
        return isEmpty(value) || !value.matches(".*[^\uFF21-\uFF3A]+.*");
    }

    public static boolean validateFullWidthLowerCaseAlphabet(String value) {
        return isEmpty(value) || !value.matches(".*[^\uFF41-\uFF5A]+.*");
    }

    public static boolean validateFullWidthAlphabet(String value) {
        return isEmpty(value)
                || !value.matches(".*[^\uFF21-\uFF3A\uFF41-\uFF5A]+.*");
    }

    public static boolean validateFullWidthNumber(String value) {
        return isEmpty(value) || !value.matches(".*[^\uFF10-\uFF19]+.*");
    }

    public static boolean validateFullWidthAlphabetNumber(String value) {
        return isEmpty(value)
                || !value
                        .matches(".*[^\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A]+.*");
    }

    public static boolean validateFullWidthAlphabetNumberSymbol(String value) {
        String symbol = "\uFF01-\uFF0F\uFF1A-\uFF20\uFF3B-\uFF40\uFF5B-\uFF5E";
        return !value.matches(".*[^" + symbol
                + "\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A]+.*");
    }

    public static boolean validateFullWidthHiragana(String value) {
        return isEmpty(value) || !value.matches(".*[^\\p{InHiragana}]+.*");
    }

    public static boolean validateFullWidthKatakana(String value) {
        return isEmpty(value) || !value.matches(".*[^\\p{InKatakana}]+.*");
    }

    public static boolean validateFullWidthHiraganaKatakana(String value) {
        return isEmpty(value)
                || !value.matches(".*[^\\p{InHiragana}\\p{InKatakana}]+.*");
    }

    public static boolean validateYYYYMMDD(String value) {
        return isEmpty(value) || checkDate(value, _yyyyMmDd);
    }

    private static boolean checkDate(String date, DateFormat format) {
        boolean result = true;
        try {
            Date d = format.parse(date);
            Calendar c = new GregorianCalendar();
            c.setTime(d);

            if (checkSameDate(date, c, format)) {
                result = true;
            } else {
                result = false;
            }
        } catch (ParseException e) {
            result = false;
        } catch (NumberFormatException nfe) {
            result = false;
        }
        return result;
    }

    private static boolean checkSameDate(String date, Calendar calendar,
            DateFormat format) {
        boolean result = true;
        int yyyy = 0;
        int mm = 0;
        int dd = 0;
        if (format.equals(_yyyyMmDd)) {
            yyyy = Integer.parseInt(date.substring(0, 4));
            mm = Integer.parseInt(date.substring(4, 6));
            dd = Integer.parseInt(date.substring(6, 8));
        } else if (format.equals(_yyMmDd)) {
            // TODO もっと詳細に調査する必要がある。
            int y = Integer.parseInt(date.substring(0, 2));
            yyyy = getCentury(y) + y;
            mm = Integer.parseInt(date.substring(2, 4));
            dd = Integer.parseInt(date.substring(4, 6));
        } else if (format.equals(_mmDd)) {
            yyyy = 1970;
            mm = Integer.parseInt(date.substring(0, 2));
            dd = Integer.parseInt(date.substring(2, 4));
        }

        int cYyyy = calendar.get(Calendar.YEAR);
        int cMm = calendar.get(Calendar.MONTH) + 1;
        int cDd = calendar.get(Calendar.DATE);

        if (yyyy == cYyyy && mm == cMm && dd == cDd) {
            result = true;
        } else {
            result = false;
        }
        return result;
    }

    private static int getCentury(int year) {
        int result = 0;
        int nowYear = new GregorianCalendar().get(Calendar.YEAR);
        if (year <= 50) {
            result = nowYear / 100 * 100;
        } else {
            result = (nowYear - 100) / 100 * 100;
        }
        return result;
    }

    public static boolean validateYYMMDD(String value) {
        return isEmpty(value) || checkDate(value, _yyMmDd);
    }

    public static boolean validateMMDD(String value) {
        return isEmpty(value) || checkDate(value, _mmDd);
    }

    public static boolean validateRequired(Object value) {
        return !isEmpty(value);
    }

    public static boolean validateIntRange(int value, String min, String max) {
        boolean result = false;

        int minRange = Integer.MIN_VALUE;
        int maxRange = Integer.MAX_VALUE;

        if (!min.equals("")) {
            minRange = Integer.parseInt(min);
        }
        if (!max.equals("")) {
            maxRange = Integer.parseInt(max);
        }

        if (value >= minRange && value <= maxRange) {
            result = true;
        }

        return result;
    }

    public static boolean validateStringSizeByBytes(String value, String min,
            String max) {
        boolean result = false;

        int minSize = Integer.MIN_VALUE;
        int maxSize = Integer.MAX_VALUE;

        if (!min.equals("")) {
            minSize = Integer.parseInt(min);
        }
        if (!max.equals("")) {
            maxSize = Integer.parseInt(max);
        }

        int size = 0;
        try {
            if (value != null) {
                size = value.getBytes("Windows-31J").length;
            }
        } catch (UnsupportedEncodingException e) {
            String message = "Stringのデコードに失敗。";
            RuntimeException re = new RuntimeException(message, e);
            throw re;
        }

        if (isEmpty(value)) {
            result = true;
        } else if (size >= minSize && size <= maxSize) {
            result = true;
        }

        return result;
    }

    public static boolean validateStringSizeByLength(String value, String min,
            String max) {
        boolean result = false;

        int minSize = Integer.MIN_VALUE;
        int maxSize = Integer.MAX_VALUE;

        if (!min.equals("")) {
            minSize = Integer.parseInt(min);
        }
        if (!max.equals("")) {
            maxSize = Integer.parseInt(max);
        }

        int size = 0;
        if (value != null) {
            size = value.length();
        }

        if (isEmpty(value)) {
            result = true;
        } else if (size >= minSize && size <= maxSize) {
            result = true;
        }

        return result;
    }

    public static boolean validateRegex(String value, String regex, String key) {
        boolean result = false;

        if (isEmpty(value)) {
            result = true;
        } else if (value.matches(regex)) {
            result = true;
        }
        return result;
    }

    public static boolean isEmpty(Object value) {
        return value == null || value.equals("");
    }
}
