/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 *
 * Copyright 2005-2016 The Kuali Foundation
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.vnd.service.impl;

import org.kuali.kfs.coreservice.framework.parameter.ParameterService;
import org.kuali.kfs.krad.util.ObjectUtils;
import org.kuali.kfs.sys.KFSKeyConstants;
import org.kuali.kfs.vnd.VendorConstants;
import org.kuali.kfs.vnd.VendorParameterConstants;
import org.kuali.kfs.vnd.businessobject.VendorDetail;
import org.kuali.kfs.vnd.service.TaxNumberService;
import org.kuali.rice.core.web.format.FormatException;

import java.util.ArrayList;
import java.util.List;

public class TaxNumberServiceImpl implements TaxNumberService {

    public ParameterService parameterService;


    public void setParameterService(ParameterService parameterService) {
        this.parameterService = parameterService;
    }


    public static List<String> taxNumberFormats;
    public static List<String> feinNumberFormats;
    public static List<String> notAllowedTaxNumbers;


    public String formatToDefaultFormat(String taxNbr) throws FormatException {
        String digits = taxNbr.replaceAll("\\D", "");

        Integer defaultTaxNumberDigits = new Integer(parameterService.getParameterValueAsString(VendorDetail.class, VendorParameterConstants.DEFAULT_TAX_NUMBER_DIGITS));

        if (digits.length() < defaultTaxNumberDigits) {
            throw new FormatException("Tax number has fewer than " + defaultTaxNumberDigits + " digits.", KFSKeyConstants.ERROR_CUSTOM, taxNbr);
        } else if (digits.length() > defaultTaxNumberDigits) {
            throw new FormatException("Tax number has more than " + defaultTaxNumberDigits + " digits.", KFSKeyConstants.ERROR_CUSTOM, taxNbr);
        } else {
            return digits;
        }
    }

    /**
     * A predicate to determine if a String field is all numbers
     *
     * @param field A String tax number
     * @return True if String is numeric
     */
    public boolean isStringAllNumbers(String field) {
        if (!isStringEmpty(field)) {
            field = field.trim();
            for (int x = 0; x < field.length(); x++) {
                char c = field.charAt(x);
                if (!Character.isDigit(c)) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

    /**
     * A predicate to determine if a String field is null or empty
     *
     * @param field A String tax number
     * @return True if String is null or empty
     */
    public boolean isStringEmpty(String field) {
        if (field == null || field.equals("")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * A predicate to determine the validity of tax numbers We're using regular expressions stored in the business rules table to
     * validate whether the tax number is in the correct format. The regular expressions are : (please update this javadoc comment
     * when the regular expressions change) 1. For SSN : (?!000)(?!666)(\d{3})([ \-]?)(?!00)(\d{2})([\-]?)(?!0000)(\d{4}) 2. For
     * FEIN : (?!00)(\d{3})([ \-]?)(\d{2})([\-]?)(?!0000)(\d{4})
     *
     * @param taxNbr  A tax number String (SSN or FEIN)
     * @param taxType determines SSN or FEIN tax number type
     * @return True if the tax number is known to be in a valid format
     */
    public boolean isValidTaxNumber(String taxNbr, String taxType) {
        String[] ssnFormats = parseSSNFormats();
        String[] feinFormats = parseFEINFormats();
        Integer defaultTaxNumberDigits = new Integer(parameterService.getParameterValueAsString(VendorDetail.class, "DEFAULT_TAX_NUMBER_DIGITS"));

        if (taxNbr.length() != defaultTaxNumberDigits || !isStringAllNumbers(taxNbr)) {
            return false;
        }

        if (taxType.equals(VendorConstants.TAX_TYPE_SSN)) {

            for (int i = 0; i < ssnFormats.length; i++) {
                if (taxNbr.matches(ssnFormats[i])) {
                    return true;
                }
            }
            return false;
        } else if (taxType.equals(VendorConstants.TAX_TYPE_FEIN)) {
            for (int i = 0; i < feinFormats.length; i++) {
                if (taxNbr.matches(feinFormats[i])) {
                    return true;
                }
            }
            return false;
        }

        return true;
    }


    /**
     * Someday we'll have to use the rules table instead of using constants. This method will return true if the tax number is an
     * allowed tax number and return false if it's not allowed.
     *
     * @param taxNbr The tax number to be processed.
     * @return boolean true if the tax number is allowed and false otherwise.
     */
    public boolean isAllowedTaxNumber(String taxNbr) {
        String[] notAllowedTaxNumbers = parseNotAllowedTaxNumbers();
        for (int i = 0; i < notAllowedTaxNumbers.length; i++) {
            if (taxNbr.matches(notAllowedTaxNumbers[i])) {
                return false;
            }
        }
        return true;
    }

    /**
     * Splits the set of tax number formats which are returned from the rule service as a semicolon-delimeted String into a String
     * array.
     *
     * @return A String array of the tax number format regular expressions.
     */
    public String[] parseSSNFormats() {
        if (ObjectUtils.isNull(taxNumberFormats)) {
            taxNumberFormats = new ArrayList<String>(parameterService.getParameterValuesAsString(VendorDetail.class, VendorParameterConstants.TAX_SSN_NUMBER_FORMATS));
        }
        return taxNumberFormats.toArray(new String[]{});
    }

    /**
     * Splits the set of tax fein number formats which are returned from the rule service as a semicolon-delimeted String into a
     * String array.
     *
     * @return A String array of the tax fein number format regular expressions.
     */
    public String[] parseFEINFormats() {
        if (ObjectUtils.isNull(feinNumberFormats)) {
            feinNumberFormats = new ArrayList<String>(parameterService.getParameterValuesAsString(VendorDetail.class, VendorParameterConstants.TAX_FEIN_NUMBER_FORMATS));
        }
        return feinNumberFormats.toArray(new String[]{});
    }

    /**
     * Splits the set of not allowed tax number formats which are returned from the rule service as a semicolon-delimeted String
     * into a String array.
     *
     * @return A String array of the not allowed tax number format regular expressions.
     */
    public String[] parseNotAllowedTaxNumbers() {
        if (ObjectUtils.isNull(notAllowedTaxNumbers)) {
            notAllowedTaxNumbers = new ArrayList<String>(parameterService.getParameterValuesAsString(VendorDetail.class, VendorParameterConstants.NOT_ALLOWED_TAX_NUMBERS));
        }
        return notAllowedTaxNumbers.toArray(new String[]{});
    }

}
