/**
 *
 */
package javax.jmdns.impl;

import java.net.InetAddress;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 *
 */
public interface NameRegister {

    /**
     *
     */
    public enum NameType {
        /**
         * This name represents a host name
         */
        HOST,
        /**
         * This name represents a service name
         */
        SERVICE,
    }

    public static abstract class BaseRegister implements NameRegister {

        protected String incrementNameWithDash(String name) {
            final StringBuilder givenName = new StringBuilder(name.length() + 5);
            int hostNameCount = 0;
            int plocal = name.indexOf(".local.");
            int punder = name.lastIndexOf('-');
            if (punder < 0) {
                // This is the first increment
                hostNameCount = 1;
                givenName.append(name.substring(0, plocal));
            } else {
                try {
                    int value = Integer.parseInt(name.substring(punder + 1, plocal));
                    hostNameCount = value + 1;
                    givenName.append(name.substring(0, punder));
                } catch (Exception e) {
                    // If we got an exception this means that we have a name with a "-"
                    hostNameCount = 1;
                    givenName.append(name.substring(0, plocal));
                }
            }
            givenName.append('-');
            givenName.append(hostNameCount);
            givenName.append(".local.");
            return givenName.toString();
        }

        protected String incrementNameWithParentesis(String name) {
            StringBuilder givenName = new StringBuilder(name.length() + 5);
            final int l = name.lastIndexOf('(');
            final int r = name.lastIndexOf(')');
            if ((l >= 0) && (l < r)) {
                try {
                    givenName.append(name.substring(0, l));
                    givenName.append('(');
                    givenName.append(Integer.parseInt(name.substring(l + 1, r)) + 1);
                    givenName.append(')');
                } catch (final NumberFormatException e) {
                    givenName.setLength(0);
                    givenName.append(name);
                    givenName.append(" (2)");
                }
            } else {
                givenName.append(name);
                givenName.append(" (2)");
            }
            return givenName.toString();
        }

    }

    public static class UniqueNamePerInterface extends BaseRegister {

        private final ConcurrentMap<InetAddress, String>      _hostNames;
        private final ConcurrentMap<InetAddress, Set<String>> _serviceNames;

        public UniqueNamePerInterface() {
            super();
            _hostNames = new ConcurrentHashMap<InetAddress, String>();
            _serviceNames = new ConcurrentHashMap<InetAddress, Set<String>>();
        }

        /*
         * (non-Javadoc)
         * @see javax.jmdns.impl.NameRegister#register(java.net.InetAddress, java.lang.String, javax.jmdns.impl.NameRegister.NameType)
         */
        @Override
        public void register(InetAddress networkInterface, String name, NameType type) {
            switch (type) {
                case HOST:
                    break;
                case SERVICE:
                    break;
                default:
                    // this is trash to keep the compiler happy
            }
        }

        /*
         * (non-Javadoc)
         * @see javax.jmdns.impl.NameRegister#checkName(java.net.InetAddress, java.lang.String, javax.jmdns.impl.NameRegister.NameType)
         */
        @Override
        public boolean checkName(InetAddress networkInterface, String name, NameType type) {
            switch (type) {
                case HOST:
                    String hostname = _hostNames.get(networkInterface);
                    return hostname != null && hostname.equals(name);
                case SERVICE:
                    Set<String> names = _serviceNames.get(networkInterface);
                    return names != null && names.contains(name);
                default:
                    // this is trash to keep the compiler happy
                    return false;
            }
        }

        /*
         * (non-Javadoc)
         * @see javax.jmdns.impl.NameRegister#incrementHostName(java.net.InetAddress, java.lang.String, javax.jmdns.impl.NameRegister.NameType)
         */
        @Override
        public String incrementName(InetAddress networkInterface, String name, NameType type) {
            switch (type) {
                case HOST:
                    return this.incrementNameWithDash(name);
                case SERVICE:
                    return this.incrementNameWithParentesis(name);
                default:
                    // this is trash to keep the compiler happy
                    return name;
            }
        }

    }

    public static class UniqueNameAcrossInterface extends BaseRegister {

        /*
         * (non-Javadoc)
         * @see javax.jmdns.impl.NameRegister#register(java.net.InetAddress, java.lang.String, javax.jmdns.impl.NameRegister.NameType)
         */
        @Override
        public void register(InetAddress networkInterface, String name, NameType type) {
            switch (type) {
                case HOST:
                    break;
                case SERVICE:
                    break;
                default:
                    // this is trash to keep the compiler happy
            }
        }

        /*
         * (non-Javadoc)
         * @see javax.jmdns.impl.NameRegister#checkName(java.net.InetAddress, java.lang.String, javax.jmdns.impl.NameRegister.NameType)
         */
        @Override
        public boolean checkName(InetAddress networkInterface, String name, NameType type) {
            switch (type) {
                case HOST:
                    return false;
                case SERVICE:
                    return false;
                default:
                    // this is trash to keep the compiler happy
                    return false;
            }
        }

        /*
         * (non-Javadoc)
         * @see javax.jmdns.impl.NameRegister#incrementHostName(java.net.InetAddress, java.lang.String, javax.jmdns.impl.NameRegister.NameType)
         */
        @Override
        public String incrementName(InetAddress networkInterface, String name, NameType type) {
            switch (type) {
                case HOST:
                    return this.incrementNameWithDash(name);
                case SERVICE:
                    return this.incrementNameWithParentesis(name);
                default:
                    // this is trash to keep the compiler happy
                    return name;
            }
        }

    }

    public static class Factory {

        private static volatile NameRegister _register;

        /**
         * Register a Name register.
         *
         * @param register
         *            new register
         * @throws IllegalStateException
         *             the register can only be set once
         */
        public static void setRegistry(NameRegister register) throws IllegalStateException {
            if (_register != null) {
                throw new IllegalStateException("The register can only be set once.");
            }
            if (register != null) {
                _register = register;
            }
        }

        /**
         * Returns the name register.
         *
         * @return name register
         */
        public static NameRegister getRegistry() {
            if (_register == null) {
                _register = new UniqueNamePerInterface();
            }
            return _register;
        }

    }

    /**
     * Registers a name that is defended by this group of mDNS.
     *
     * @param networkInterface
     *            IP address to handle
     * @param name
     *            name to register
     * @param type
     *            name type to register
     */
    public abstract void register(InetAddress networkInterface, String name, NameType type);

    /**
     * Checks a name that is defended by this group of mDNS.
     *
     * @param networkInterface
     *            IP address to handle
     * @param name
     *            name to check
     * @param type
     *            name type to check
     * @return <code>true</code> if the name is not in conflict, <code>flase</code> otherwise.
     */
    public abstract boolean checkName(InetAddress networkInterface, String name, NameType type);

    /**
     * Increments a name that is defended by this group of mDNS after it has been found in conflict.
     *
     * @param networkInterface
     *            IP address to handle
     * @param name
     *            name to increment
     * @param type
     *            name type to increments
     * @return new name
     */
    public abstract String incrementName(InetAddress networkInterface, String name, NameType type);

}
