/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.apache.sis.storage;

import org.opengis.util.GenericName;
import org.apache.sis.util.iso.Names;

// Test dependencies
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.apache.sis.test.Assertions.assertMessageContains;
import org.apache.sis.test.TestCase;


/**
 * Tests {@link FeatureNaming}.
 *
 * @author  Martin Desruisseaux (Geomatys)
 */
public final class FeatureNamingTest extends TestCase {
    /**
     * Name for the tests.
     */
    private final GenericName A, B, otherA;

    /**
     * Creates a new test case.
     */
    public FeatureNamingTest() {
        A      = Names.parseGenericName(null, null, "myNS:A");
        B      = Names.createLocalName (null, null, "B");
        otherA = Names.parseGenericName(null, null, "other:A");
    }

    /**
     * Tests {@link FeatureNaming#add(DataStore, GenericName, Object)} followed by
     * {@link FeatureNaming#get(DataStore, String)} in a simple case where there is no ambiguity.
     *
     * @throws IllegalNameException if an unexpected error occurred while adding or getting an element.
     */
    @Test
    public void testSimple() throws IllegalNameException {
        final DataStoreMock store = new DataStoreMock("testDataStore");
        final FeatureNaming<Integer> map = new FeatureNaming<>();
        map.add(store, A, 1);
        map.add(store, B, 2);
        assertEquals(Integer.valueOf(1), map.get(store, "myNS:A"));
        assertEquals(Integer.valueOf(1), map.get(store, "A"));
        assertEquals(Integer.valueOf(2), map.get(store, "B"));
        /*
         * Above code tested normal usage. Now test error conditions.
         * First, searching a non-existent entry should raise an exception.
         */
        IllegalNameException e;
        e = assertThrows(IllegalNameException.class, () -> map.get(store, "C"),
                         "Should not find a non-existent entry.");
        assertMessageContains(e, "C", "testDataStore");
        /*
         * Attempt to overwrite an existing entry should raise an exception
         * without modifying the existing value.
         */
        e = assertThrows(IllegalNameException.class, () -> map.add(store, B, 3),
                         "Should not overwrite an existing entry.");
        assertMessageContains(e, "B");
        assertEquals(Integer.valueOf(2), map.get(store, "B"),
                     "Existing value should not have been modified.");
    }

    /**
     * Same test as {@link #testSimple()}, with the addition of an ambiguous name.
     *
     * @throws IllegalNameException if an unexpected error occurred while adding or getting an element.
     */
    @Test
    public void testAmbiguity() throws IllegalNameException {
        final var store = new DataStoreMock("testDataStore");
        final var map   = new FeatureNaming<Integer>();
        map.add(store, A, 1);
        map.add(store, B, 2);
        map.add(store, otherA, 3);
        assertEquals(Integer.valueOf(1), map.get(store, "myNS:A"));
        assertEquals(Integer.valueOf(2), map.get(store, "B"));
        assertEquals(Integer.valueOf(3), map.get(store, "other:A"));
        /*
         * Attempt to query using only the "A" value was used to succeed in 'testSimple()' but
         * should now fail because this shortcut could apply to "other:A" as well as "myNS:A".
         */
        var e = assertThrows(IllegalNameException.class, () -> map.get(store, "A"),
                             "Should not find an ambiguous entry.");
        assertMessageContains(e, "myNS:A", "other:A", "testDataStore");
    }

    /**
     * Tests two names having the same tip, but where only one of the two names have a namespace.
     *
     * @throws IllegalNameException if an unexpected error occurred while adding or getting an element.
     */
    @Test
    public void testQualifiedAndUnqualifiedName() throws IllegalNameException {
        final var store = new DataStoreMock("testDataStore");
        final var local = Names.createLocalName(null, null, "A");
        FeatureNaming<Integer> map = new FeatureNaming<>();
        map.add(store, local,  3);
        map.add(store, A,      2);
        map.add(store, B,      5);
        map.add(store, otherA, 7);
        assertEquals(Integer.valueOf(5), map.get(store, "B"));
        assertEquals(Integer.valueOf(3), map.get(store, "A"));
        assertEquals(Integer.valueOf(2), map.get(store, "myNS:A"));
        assertEquals(Integer.valueOf(7), map.get(store, "other:A"));
        /*
         * Same tests, but with elements added in different order.
         */
        map = new FeatureNaming<>();
        map.add(store, otherA, 7);
        map.add(store, B,      5);
        map.add(store, A,      2);
        map.add(store, local,  3);
        assertEquals(Integer.valueOf(5), map.get(store, "B"));
        assertEquals(Integer.valueOf(3), map.get(store, "A"));
        assertEquals(Integer.valueOf(2), map.get(store, "myNS:A"));
        assertEquals(Integer.valueOf(7), map.get(store, "other:A"));
    }

    /**
     * Tests removing an entry. It should change the state about whether an alias is ambiguous or not.
     *
     * @throws IllegalNameException if an unexpected error occurred while adding or getting an element.
     */
    @Test
    public void testRemove() throws IllegalNameException {
        final var store = new DataStoreMock("testDataStore");
        final var map   = new FeatureNaming<Integer>();
        map.add(store, A, 1);
        map.add(store, B, 2);
        map.add(store, otherA, 3);
        /*
         * Verify that "myNS:A" exists before the removal, then does not exist anymore after the removal.
         */
        assertEquals(Integer.valueOf(3), map.get(store, "other:A"));
        assertEquals(Integer.valueOf(1), map.get(store, "myNS:A"));
        assertTrue(map.remove(store, A));
        var e = assertThrows(IllegalNameException.class, () -> map.get(store, "myNS:A"),
                             "Should not find a non-existent entry.");
        assertMessageContains(e, "myNS:A", "testDataStore");
        /*
         * The "A" shortcut should not be ambiguous anymore at this point since we removed the other name
         * ("myNS:A") which was causing the ambiguity;
         */
        assertEquals(Integer.valueOf(3), map.get(store, "A"));
        assertEquals(Integer.valueOf(2), map.get(store, "B"));
        assertEquals(Integer.valueOf(3), map.get(store, "other:A"));
    }
}
