/*
 * Copyright (c) 2002-2015 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.neo4j.kernel.impl.api.index.inmemory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.neo4j.collection.primitive.PrimitiveLongSet;
import org.neo4j.collection.primitive.PrimitiveLongVisitor;
import org.neo4j.kernel.api.index.IndexEntryConflictException;
import org.neo4j.kernel.api.index.IndexUpdater;
import org.neo4j.kernel.api.index.NodePropertyUpdate;
import org.neo4j.kernel.api.index.PreexistingIndexEntryConflictException;
import org.neo4j.kernel.api.index.PropertyAccessor;
import org.neo4j.kernel.api.index.Reservation;
import org.neo4j.kernel.api.properties.Property;
import org.neo4j.kernel.impl.api.index.IndexUpdateMode;
import org.neo4j.kernel.impl.api.index.UniquePropertyIndexUpdater;

class UniqueInMemoryIndex extends InMemoryIndex
{
    private final int propertyKeyId;

    private final PrimitiveLongVisitor<RuntimeException> removeFromIndex = new PrimitiveLongVisitor<RuntimeException>()
    {
        @Override
        public boolean visited( long nodeId )
        {
            UniqueInMemoryIndex.this.remove( nodeId );
            return false;
        }
    };

    public UniqueInMemoryIndex( int propertyKeyId )
    {
        this.propertyKeyId = propertyKeyId;
    }

    @Override
    protected IndexUpdater newUpdater( final IndexUpdateMode mode, final boolean populating )
    {
        return new UniquePropertyIndexUpdater()
        {
            @Override
            protected void flushUpdates( Iterable<NodePropertyUpdate> updates )
                    throws IOException, IndexEntryConflictException
            {
                for ( NodePropertyUpdate update : updates )
                {
                    switch ( update.getUpdateMode() )
                    {
                        case CHANGED:
                        case REMOVED:
                            UniqueInMemoryIndex.this.remove( update.getNodeId(), update.getValueBefore() );
                    }
                }
                for ( NodePropertyUpdate update : updates )
                {
                    switch ( update.getUpdateMode() )
                    {
                        case ADDED:
                        case CHANGED:
                            add( update.getNodeId(), update.getValueAfter(), IndexUpdateMode.ONLINE == mode );
                    }
                }
            }

            @Override
            public Reservation validate( Iterable<NodePropertyUpdate> updates ) throws IOException
            {
                return Reservation.EMPTY;
            }

            @Override
            public void remove( PrimitiveLongSet nodeIds )
            {
                nodeIds.visitKeys( removeFromIndex );
            }
        };
    }

    @Override
    public void verifyDeferredConstraints( final PropertyAccessor accessor )
            throws IndexEntryConflictException, IOException
    {
        try
        {
            indexData.iterateAll( new InMemoryIndexImplementation.IndexEntryIterator()
            {
                @Override
                public void visitEntry( Object key, Set<Long> nodeIds ) throws Exception
                {
                    List<Entry> entries = new ArrayList<>();
                    for ( long nodeId : nodeIds )
                    {
                        Property property = accessor.getProperty( nodeId, propertyKeyId );
                        Object value = property.value();
                        for ( Entry current : entries )
                        {
                            if ( current.property.valueEquals( value ) )
                            {
                                throw new PreexistingIndexEntryConflictException( value, current.nodeId, nodeId );
                            }
                        }
                        entries.add( new Entry( nodeId, property ) );
                    }
                }
            } );
        } catch ( PreexistingIndexEntryConflictException e )
        {
            throw e;
        }
        catch ( Exception e )
        {
            throw new RuntimeException( e );
        }

    }

    private static class Entry {
        long nodeId;
        Property property;

        private Entry( long nodeId, Property property )
        {
            this.nodeId = nodeId;
            this.property = property;
        }
    }
}

@@ -885,25 +885,23 @@
 va.util.
-ArrayList
+HashMap
 ;%0Aimport
@@ -911,20 +911,19 @@
 va.util.
-List
+Map
 ;%0Aimport
@@ -1397,57 +1397,8 @@
 on;%0A
-import org.neo4j.kernel.api.properties.Property;%0A
 impo
@@ -3951,18 +3951,23 @@
     
-List%3CEntry
+Map%3CObject,Long
 %3E en
@@ -3982,17 +3982,15 @@
 new 
-ArrayList
+HashMap
 %3C%3E()
@@ -4087,33 +4087,34 @@
         
-Property property
+final Object value
  = acces
@@ -4157,57 +4157,8 @@
 Id )
-;%0A                        Object value = property
 .val
@@ -4191,37 +4191,41 @@
     
-for ( Entry current : entries
+if ( entries.containsKey( value )
  )%0A 
@@ -4281,41 +4281,41 @@
     
-if ( current.property.valueEquals
+long existingNodeId = entries.get
 ( va
@@ -4323,45 +4323,10 @@
 ue )
- )%0A                            %7B%0A    
+;%0A
     
@@ -4410,17 +4410,17 @@
 ue, 
-current.n
+existingN
 odeI
@@ -4424,36 +4424,32 @@
 deId, nodeId );%0A
-    
                 
@@ -4486,76 +4486,34 @@
     
-%7D%0A                        entries.add( new Entry( nodeId, property )
+entries.put( value, nodeId
  );%0A
@@ -4769,246 +4769,8 @@
    %7D
-%0A%0A    %7D%0A%0A    private static class Entry %7B%0A        long nodeId;%0A        Property property;%0A%0A        private Entry( long nodeId, Property property )%0A        %7B%0A            this.nodeId = nodeId;%0A            this.property = property;%0A        %7D
 %0A   

Improve complexity of UniqueInMemoryIndex.verifyDeferredConstraints