/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt' which is part of this source code package
 */
package com.scytl.cryptolib.certificates.bean;

import com.scytl.cryptolib.api.exceptions.GeneralCryptoLibException;
import com.scytl.cryptolib.certificates.constants.X509CertificateConstants;
import com.scytl.cryptolib.commons.utils.validations.Validate;
import com.scytl.cryptolib.primitives.securerandom.constants.SecureRandomConstants;

/**
 * Container class for the attributes of a distinguished name. The attributes
 * are added to the distinguished name by using an internally defined builder
 * class.
 */
public final class X509DistinguishedName {

    private final String _commonName;

    private final String _organizationalUnit;

    private final String _organization;

    private final String _locality;

    private final String _country;

    /**
     * Builder class for the distinguished name.
     */
    public static class Builder {

        // Required attributes.
        private final String _commonName;

        private final String _country;

        // Optional attributes.
        private String _organizationalUnit = "";

        private String _organization = "";

        private String _locality = "";

        /**
         * Creates a new object using the arguments provided.
         * 
         * @param commonName
         *            common name to add to builder.
         * @param country
         *            country to add to builder.
         * @throws GeneralCryptoLibException
         *             if common name or country argument is null or blank or if
         *             it exceeds the maximum required length.
         */
        public Builder(final String commonName, final String country)
                throws GeneralCryptoLibException {

            Validate.notNullOrBlank(commonName, "Common name");
            Validate
                .notGreaterThan(
                    commonName.length(),
                    X509CertificateConstants.X509_DISTINGUISHED_NAME_ATTRIBUTE_MAX_SIZE,
                    "Common name length", "");
            Validate.isAsciiPrintable(commonName, "Common name");
            Validate.notNullOrBlank(country, "Country");
            Validate.notGreaterThan(country.length(), 2, "Country length",
                "");
            Validate.onlyContains(country,
                SecureRandomConstants.ALPHABET_BASE64, "Country");

            _commonName = commonName;
            _country = country;
        }

        /**
         * Adds an organizational unit to the builder.
         * 
         * @param organizationalUnit
         *            organizational unit to builder.
         * @return updated builder.
         * @throws GeneralCryptoLibException
         *             if the organizational unit argument is null or blank or
         *             if it exceeds the maximum required length.
         */
        public Builder addOrganizationalUnit(
                final String organizationalUnit)
                throws GeneralCryptoLibException {

            Validate.notNull(organizationalUnit, "Organizational unit");
            Validate
                .notGreaterThan(
                    organizationalUnit.length(),
                    X509CertificateConstants.X509_DISTINGUISHED_NAME_ATTRIBUTE_MAX_SIZE,
                    "Organizational unit length", "");
            Validate.isAsciiPrintable(organizationalUnit,
                "Organizational unit");

            _organizationalUnit = organizationalUnit;

            return this;
        }

        /**
         * Adds an organization to the builder.
         * 
         * @param organization
         *            organization to add to builder.
         * @return updated builder.
         * @throws GeneralCryptoLibException
         *             if the organization argument is null or blank or if it
         *             exceeds the maximum required length.
         */
        public Builder addOrganization(final String organization)
                throws GeneralCryptoLibException {

            Validate.notNull(organization, "Organization");
            Validate
                .notGreaterThan(
                    organization.length(),
                    X509CertificateConstants.X509_DISTINGUISHED_NAME_ATTRIBUTE_MAX_SIZE,
                    "Organization length", "");
            Validate.isAsciiPrintable(organization, "Organization");

            _organization = organization;

            return this;
        }

        /**
         * Adds a locality to the builder.
         * 
         * @param locality
         *            locality to add to builder.
         * @return updated builder.
         * @throws GeneralCryptoLibException
         *             if the locality argument is null or blank or if it
         *             exceeds the maximum required length.
         */
        public Builder addLocality(final String locality)
                throws GeneralCryptoLibException {

            Validate.notNull(locality, "Locality");
            Validate
                .notGreaterThan(
                    locality.length(),
                    X509CertificateConstants.X509_DISTINGUISHED_NAME_ATTRIBUTE_MAX_SIZE,
                    "Locality length", "");
            Validate.isAsciiPrintable(locality, "Locality");

            _locality = locality;

            return this;
        }

        /**
         * Builds the distinguished name based on the attributes that have been
         * added to the builder.
         * 
         * @return distinguished name with added attributes.
         * @throws GeneralCryptoLibException
         *             if the distinguished name build process fails.
         */
        public X509DistinguishedName build()
                throws GeneralCryptoLibException {

            return new X509DistinguishedName(this);
        }
    }

    /**
     * Creates a new object using the attributes provided by {@code builder}.
     * 
     * @param builder
     *            distinguished name builder containing all attributes to be
     *            included in distinguished name.
     * @throws GeneralCryptoLibException
     *             if the attributes are invalid.
     */
    private X509DistinguishedName(final Builder builder)
            throws GeneralCryptoLibException {

        _commonName = builder._commonName;
        _organizationalUnit = builder._organizationalUnit;
        _organization = builder._organization;
        _locality = builder._locality;
        _country = builder._country;
    }

    public String getCommonName() {

        return _commonName;
    }

    public String getOrganizationalUnit() {

        return _organizationalUnit;
    }

    public String getOrganization() {

        return _organization;
    }

    public String getLocality() {

        return _locality;
    }

    public String getCountry() {

        return _country;
    }
    
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
            + ((_commonName == null) ? 0 : _commonName.hashCode());
        result = prime * result
            + ((_country == null) ? 0 : _country.hashCode());
        result = prime * result
            + ((_locality == null) ? 0 : _locality.hashCode());
        result = prime * result
            + ((_organization == null) ? 0 : _organization.hashCode());
        result = prime * result + ((_organizationalUnit == null) ? 0
                : _organizationalUnit.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        X509DistinguishedName other = (X509DistinguishedName) obj;
        if (_commonName == null) {
            if (other._commonName != null) {
                return false;
            }
        } else if (!_commonName.equals(other._commonName)) {
            return false;
        }
        if (_country == null) {
            if (other._country != null) {
                return false;
            }
        } else if (!_country.equals(other._country)) {
            return false;
        }
        if (_locality == null) {
            if (other._locality != null) {
                return false;
            }
        } else if (!_locality.equals(other._locality)) {
            return false;
        }
        if (_organization == null) {
            if (other._organization != null) {
                return false;
            }
        } else if (!_organization.equals(other._organization)) {
            return false;
        }
        if (_organizationalUnit == null) {
            if (other._organizationalUnit != null) {
                return false;
            }
        } else if (!_organizationalUnit
            .equals(other._organizationalUnit)) {
            return false;
        }
        return true;
    }
}
