/*
 * Copyright 2006 Mask@ Project.
 *
 * 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.
 */
 
 /**
 * デフォルトのエラー処理関数(alertで全部のエラーを表示)
 * maskat.validationErrorHandlerカスタマイズすることができます。
 */
maskat.validationErrorHandler=function(errors){
	for (var i=0;i<errors.length;i++){
		var error = errors[i];
		alert(error.vTarget.desc+ " " + error.message);
	}
}

maskat.getValidator = function(throwOnErrorPar){
    var validator = maskat.globalVar.validator;
    validator.throwOnError = throwOnErrorPar;
    validator.exValidators = new Object;
    validator.errors = new Array;
    return validator;
}

/**
 * validationエラー
 */
maskat.ValidationError = function(vTarget,message){
    this.vTarget = vTarget;
    this.message = message;
}
maskat.ValidationError.prototype = new Error;

/**
 * NullValidatorはvalidation操作何もしない
 * それを使えば、validationを無効にすることができます
 */
maskat.NullValidator=function(){
    this.throwOnError=false;
    this.errors = new Array;
};
maskat.NullValidator.prototype.acceptError = function(error){
    //何もしない
}
maskat.NullValidator.prototype.validate=function(objName,value,type,desc){
    //何もしない。
}
maskat.NullValidator.prototype.regValidator=function(value,type){
    //何もしない。
}

/**
 * デフォルトvalidationオブジェクト
 */
maskat.DefaultValidator=function(){
    this.throwOnError = true;
    //true:バリデーションエラーがあったら、すぐthrow new Error
    //false:バリデーションエラーがあったら、validatorに保存する
    this.exValidators = new Object;
    this.errors = new Array;
}

maskat.DefaultValidator.prototype.acceptError = function(error){
    this.errors.push(error);
}

/**
 * カスタマイズvalidatorを登録することができます。
 * 拡張validatorを登録する
 */
maskat.DefaultValidator.prototype.regValidator=function(typeName,validator){
    this.exValidators[typeName] = validator;
}

maskat.DefaultValidator.prototype.validate=function(objName,value,type,desc,min,max,regexp){
    var vTarget = new Object;//validation target
    vTarget.objName = objName;
    vTarget.value = value;
    vTarget.desc = desc;
    vTarget.minVal = parseInt(min);
    vTarget.maxVal = parseInt(max);
    vTarget.regexp = regexp;
    try{
        if (type=="byte"){
            this.validateByte(vTarget);
        }
        if (type=="short"){
            this.validateShort(vTarget);
        }
        if (type=="int"){
            this.validateInt(vTarget);
        }
        if (type=="long"){
            this.validateLong(vTarget);
        }
        if (type=="float"){
            this.validateFloat(vTarget);
        }
        if (type=="string"){
            this.validateString(vTarget);
        }
        if (type=="date"){
            this.validateDate(vTarget);
        }
        if (type=="time"){
            this.validateTime(vTarget);
        }
        //拡張validatorでバリデーション
        var exValidator = this.exValidators[type];
        if (exValidator)
            exValidator.validate(vTarget);
    }catch(e){
       if (e instanceof maskat.ValidationError){
           if (!this.throwOnError){
              this.acceptError(e);
              return;
           }
       }
       throw e;
    }

}

maskat.DefaultValidator.prototype.validateIntegralNumRange=function(vTarget,minVal,maxVal,minLength,maxLength){
    if (!this.isIntegralNum(vTarget.value)) {
        throw new maskat.ValidationError(vTarget,"数字チェックに失敗しました。");
    }else{
        if (!isNaN(minLength)){
            if (vTarget.value.length<minLength)
               throw new maskat.ValidationError(vTarget,"長さチェック失敗。最小桁数："+minLength);
        }
        if (!isNaN(maxLength)){
            if (vTarget.value.length>maxLength)
                throw new maskat.ValidationError(vTarget,"長さチェック失敗。最大桁数："+maxLength);
        }
        var iValue = parseInt(vTarget.value);
        if (isNaN(iValue) || !(iValue >= minVal && iValue <= maxVal)) {
            throw new maskat.ValidationError(vTarget,"データの範囲不正(" + 
                minVal + " ... " + maxVal + ")。");
        }
    }
}

maskat.DefaultValidator.prototype.validateByte=function(vTarget){
    this.validateIntegralNumRange(vTarget,-128,127,vTarget.minVal,vTarget.maxVal);
}

maskat.DefaultValidator.prototype.validateShort=function(vTarget){
    this.validateIntegralNumRange(vTarget,-32768,32767,vTarget.minVal,vTarget.maxVal);
}

maskat.DefaultValidator.prototype.validateInt=function(vTarget){
    this.validateIntegralNumRange(vTarget,-2147483648,2147483647,vTarget.minVal,vTarget.maxVal);
}

maskat.DefaultValidator.prototype.validateLong=function(vTarget){
    this.validateIntegralNumRange(vTarget,-9223372036854775808,9223372036854775807,vTarget.minVal,vTarget.maxVal);
    //注：javascriptの整数はdouble-precision、16位のprecisionだけ。
    //だから、longチェックは-9223372036854776000~9223372036854776000の範囲になってしまう
}

maskat.DefaultValidator.prototype.validateFloat=function(vTarget){
    //vTargetは正しい数字であろうかチェック....
    // remove '.' before checking digits
    var tempArray = vTarget.value.split('.');
    if (tempArray.length>2){
        throw new maskat.ValidationError(vTarget,"floatチェック失敗。");
    }
    //Strip off leading '0'
    var zeroIndex = 0;
    var joinedString= tempArray.join('');
    while (joinedString.charAt(zeroIndex) == '0') {
        zeroIndex++;
    }
    var noZeroString = joinedString.substring(zeroIndex,joinedString.length);

    if (!this.isIntegralNum(noZeroString)) {
        throw new maskat.ValidationError(vTarget,"floatチェック失敗。");
    }
    var iValue = parseFloat(vTarget.value);
    if (isNaN(iValue)) {
        throw new maskat.ValidationError(vTarget,"floatチェック失敗。");
    }
}

maskat.DefaultValidator.prototype.isIntegralNum = function(argvalue) {
    argvalue = argvalue.toString();
    var validChars = "0123456789";
    var startFrom = 0;
    if (argvalue.charAt(0) == "0") {
        return false;
    }
    if (argvalue.charAt(0) == "-") {
        startFrom = 1;
    }

    for (var n = startFrom; n < argvalue.length; n++) {
        if (validChars.indexOf(argvalue.substring(n, n+1)) == -1) return false;
    }
    return true;
}

maskat.DefaultValidator.prototype.validateTime=function(vTarget){
    var bValid = true;
    var timeRegexp = new RegExp("^(\\d{2})[:](\\d{2})[:](\\d{2})$");
    var matched = timeRegexp.exec(vTarget.value);
    if (matched!=null){
        if (!isValidTime(matched[1],matched[2],matched[3])){
            bValid=false;
        }
    } else {
        bValid=false;
    }
     if (!bValid)
        throw new maskat.ValidationError(vTarget,"time チェック失敗。hh:mm:ss");
}

maskat.DefaultValidator.prototype.validateString=function(vTarget){
    if (!vTarget.value instanceof String){
        alert("vTarget.valueはStringではありません。チェックできません。");
        return;
    }
    if (!isNaN(vTarget.minVal)){
        if (vTarget.value.length<vTarget.minVal)
           throw new maskat.ValidationError(vTarget,"長さチェック失敗。最小文字数："+vTarget.minVal);
    }
    if (!isNaN(vTarget.maxVal)){
        if (vTarget.value.length>vTarget.maxVal)
            throw new maskat.ValidationError(vTarget,"長さチェック失敗。最大文字数："+vTarget.maxVal);
    }
    
    var regexp = vTarget.regexp;
    if (regexp){
    	var regexpObj = new RegExp(regexp);
    	if (!vTarget.value.match(regexpObj)){
    		throw new maskat.ValidationError(vTarget,"入力フォーマット不正です。");
    	}
    }
//  throw new Error("定義不正：type=stringの場合、必ずmin或いはmaxを数字値で定義してください。");
}

maskat.DefaultValidator.prototype.validateDate=function(vTarget,datePattern) {
     var bValid = true;
     var MONTH = "MM";
     var DAY = "dd";
     var YEAR = "yyyy";
     var i = 0;
     var fields = new Array();

     if (datePattern == null)
         datePattern="yyyy-MM-dd";//デフォルトdatePattern

     var orderMonth = datePattern.indexOf(MONTH);
     var orderDay = datePattern.indexOf(DAY);
     var orderYear = datePattern.indexOf(YEAR);
     if ((orderDay < orderYear && orderDay > orderMonth)) {
         var iDelim1 = orderMonth + MONTH.length;
         var iDelim2 = orderDay + DAY.length;
         var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
         var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
         if (iDelim1 == orderDay && iDelim2 == orderYear) {
            dateRegexp = new RegExp("^(\\d{2})(\\d{2})(\\d{4})$");
         } else if (iDelim1 == orderDay) {
            dateRegexp = new RegExp("^(\\d{2})(\\d{2})[" + delim2 + "](\\d{4})$");
         } else if (iDelim2 == orderYear) {
            dateRegexp = new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})(\\d{4})$");
         } else {
            dateRegexp = new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{4})$");
         }
         var matched = dateRegexp.exec(vTarget.value);
         if(matched != null) {
            if (!isValidDate(matched[2], matched[1], matched[3])) {
               bValid =  false;
            }
         } else {
            bValid =  false;
         }
     } else if ((orderMonth < orderYear && orderMonth > orderDay)) {
         var iDelim1 = orderDay + DAY.length;
         var iDelim2 = orderMonth + MONTH.length;
         var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
         var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
         if (iDelim1 == orderMonth && iDelim2 == orderYear) {
             dateRegexp = new RegExp("^(\\d{2})(\\d{2})(\\d{4})$");
         } else if (iDelim1 == orderMonth) {
             dateRegexp = new RegExp("^(\\d{2})(\\d{2})[" + delim2 + "](\\d{4})$");
         } else if (iDelim2 == orderYear) {
             dateRegexp = new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})(\\d{4})$");
         } else {
             dateRegexp = new RegExp("^(\\d{2})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{4})$");
         }
         var matched = dateRegexp.exec(vTarget.value);
         if(matched != null) {
             if (!isValidDate(matched[1], matched[2], matched[3])) {
                 bValid =  false;
              }
         } else {
             bValid =  false;
         }
     } else if ((orderMonth > orderYear && orderMonth < orderDay)) {
         var iDelim1 = orderYear + YEAR.length;
         var iDelim2 = orderMonth + MONTH.length;
         var delim1 = datePattern.substring(iDelim1, iDelim1 + 1);
         var delim2 = datePattern.substring(iDelim2, iDelim2 + 1);
         if (iDelim1 == orderMonth && iDelim2 == orderDay) {
             dateRegexp = new RegExp("^(\\d{4})(\\d{2})(\\d{2})$");
         } else if (iDelim1 == orderMonth) {
             dateRegexp = new RegExp("^(\\d{4})(\\d{2})[" + delim2 + "](\\d{2})$");
         } else if (iDelim2 == orderDay) {
             dateRegexp = new RegExp("^(\\d{4})[" + delim1 + "](\\d{2})(\\d{2})$");
         } else {
             dateRegexp = new RegExp("^(\\d{4})[" + delim1 + "](\\d{2})[" + delim2 + "](\\d{2})$");
         }
         var matched = dateRegexp.exec(vTarget.value);
         if(matched != null) {
             if (!isValidDate(matched[3], matched[2], matched[1])) {
                 bValid =  false;
             }
         } else {
              bValid =  false;
         }
     } 
     if (!bValid)
        throw new maskat.ValidationError(vTarget,"dateチェック失敗。"+datePattern);
}

function isValidDate(day, month, year) {
    if (month < 1 || month > 12) {
        return false;
    }
    if (day < 1 || day > 31) {
        return false;
    }
    if ((month == 4 || month == 6 || month == 9 || month == 11) &&
        (day == 31)) {
        return false;
    }
    if (month == 2) {
        var leap = (year % 4 == 0 &&
           (year % 100 != 0 || year % 400 == 0));
        if (day>29 || (day == 29 && !leap)) {
            return false;
        }
    }
    return true;
}

function isValidTime(hour,minute,second){
    if (hour < 0 || hour > 23)
        return false;
    if (minute < 0 || minute > 59)
        return false;
    if (second < 0 || second > 59)
        return false;
    return true;
}

maskat.globalVar.validator = new maskat.DefaultValidator();

/**
 * validation errorが検出されたら、onValidationErrorに
 */
//maskat.DefaultValidator.prototype.onValidationError=function(outputObj){
//    this.context.accept(outputObj);
//}

/**
 * 
 */
//maskat.ValidationContext=function(){
//    this.errors = new Array();
//}

//maskat.ValidationContext.prototype.accept=function(outputObj){
//    this.errors.push(outputObj);
//}

//maskat.ValidationContext.prototype.validationFeedback=function(){
//    for (var i=0;i<this.errors.length;i++){
//        alert(this.errors[i].objName+"のバリデーションに以下のようなエラー："+this.errors[i].message);
//    }
//}
