/*
 * Copyright 2021 Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags.
 * 
 * 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.
 */
package org.keycloak.models.map.common;

import java.security.SecureRandom;
import java.util.UUID;

/**
 *  Converts given storage key from and to {@code String} representation.
 *
 *  @author hmlnarik
 */
public interface StringKeyConverter<K> {

    /**
     * Returns String representation of the key from native representation
     * @param key 
     * @throws IllegalArgumentException if the string format is not recognized
     * @throws NullPointerException if the parameter is {@code null}
     * @return See above
     */
    default String keyToString(K key)   { return key == null ? null : key.toString(); }

    /**
     * Returns a new unique primary key for the storage that
     * this {@link StringKeyConverter} belongs to. The uniqueness
     * needs to be guaranteed by e.g. using database sequences or
     * using a random value that is proved sufficiently improbable
     * to be repeated.
     * 
     * @return
     */
    K yieldNewUniqueKey();

    /**
     * Returns native representation of the key from String representation
     * @param key
     * @throws IllegalArgumentException if the string format is not recognized
     * @throws NullPointerException if the parameter is {@code null}
     * @return See above
     */
    K fromString(String key);

    /**
     * Exception-free variant of {@link #fromString} method.
     * Returns native representation of the key from String representation,
     * or {@code null} if the {@code key} is either {@code null} or invalid.
     * @param key
     * @return See above
     */
    default K fromStringSafe(String key) {
        try {
            return fromString(key);
        } catch (Exception ex) {
            return null;
        }
    }

    class UUIDKey implements StringKeyConverter<UUID> {

        public static final UUIDKey INSTANCE = new UUIDKey();

        @Override
        public UUID yieldNewUniqueKey() {
            return UUID.randomUUID();
        }

        @Override
        public UUID fromString(String key) {
            return UUID.fromString(key);
        }
    }

    class StringKey implements StringKeyConverter<String> {

        public static final StringKey INSTANCE = new StringKey();

        @Override
        public String fromString(String key) {
            return key;
        }

        @Override
        public String yieldNewUniqueKey() {
            return fromString(UUID.randomUUID().toString());
        }
    }

    class ULongKey implements StringKeyConverter<Long> {

        public static final ULongKey INSTANCE = new ULongKey();

        /*
         * The random number generator used by this class to create random
         * based UUIDs. In a holder class to defer initialization until needed.
         */
        private static class Holder {
            static final SecureRandom numberGenerator = new SecureRandom();
        }

        @Override
        public String keyToString(Long key) {
            return Long.toUnsignedString(key);
        }

        @Override
        public Long fromString(String key) {
            return key == null ? null : Long.parseUnsignedLong(key);
        }

        @Override
        public Long yieldNewUniqueKey() {
            return Holder.numberGenerator.nextLong();
        }
    }

}
