/*
 * Copyright 2013 bits of proof zrt.
 *
 * 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 com.bitsofproof.supernode.api;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.bitsofproof.supernode.common.ByteVector;
import com.bitsofproof.supernode.common.Key;
import com.bitsofproof.supernode.common.ValidationException;

public class ExtendedKeyAccountManager extends BaseAccountManager implements TransactionListener
{
	private static final Logger log = LoggerFactory.getLogger (ExtendedKeyAccountManager.class);

	private final Map<ByteVector, Integer> keyIDForAddress = new HashMap<ByteVector, Integer> ();
	private ExtendedKey master;
	private int nextSequence;
	private int lookAhead = 100;

	public ExtendedKey getMaster ()
	{
		return master;
	}

	public void setMaster (ExtendedKey master)
	{
		this.master = master;
	}

	@Override
	public int getNumberOfKeys ()
	{
		return nextSequence;
	}

	@Override
	protected void notifyListener (Transaction t)
	{
		for ( TransactionOutput o : t.getOutputs () )
		{
			if ( isOwnAddress (o.getOutputAddress ()) )
			{
				int keyId = keyIDForAddress.get (new ByteVector (o.getOutputAddress ()));
				ensureLookAhead (keyId);
			}
		}
		super.notifyListener (t);
	}

	private void ensureLookAhead (int from)
	{
		while ( keyIDForAddress.size () < (from + lookAhead) )
		{
			Key key = null;
			try
			{
				key = master.getKey (keyIDForAddress.size ());
			}
			catch ( ValidationException e )
			{
			}
			keyIDForAddress.put (new ByteVector (key.getAddress ()), keyIDForAddress.size ());
		}
	}

	public Key getKey (int i) throws ValidationException
	{
		ensureLookAhead (i);
		return master.getKey (i);
	}

	public void setNextKey (int i)
	{
		nextSequence = i;
		ensureLookAhead (nextSequence);
	}

	@Override
	public Key getNextKey () throws ValidationException
	{
		return getKey (nextSequence++);
	}

	public Integer getKeyIDForAddress (byte[] address)
	{
		return keyIDForAddress.get (new ByteVector (address));
	}

	@Override
	public Key getKeyForAddress (byte[] address)
	{
		Integer keyId = getKeyIDForAddress (address);
		if ( keyId == null )
		{
			return null;
		}
		try
		{
			return getKey (keyId);
		}
		catch ( ValidationException e )
		{
			return null;
		}
	}

	@Override
	public Collection<byte[]> getAddresses ()
	{
		List<byte[]> addresses = new ArrayList<byte[]> ();
		for ( ByteVector v : keyIDForAddress.keySet () )
		{
			addresses.add (v.toByteArray ());
		}
		return addresses;
	}

	public ExtendedKeyAccountManager (String name, long created)
	{
		super (name, created);
	}

	public Set<Integer> sync (BCSAPI api, final int lookAhead) throws BCSAPIException, ValidationException
	{
		this.lookAhead = lookAhead;
		final Set<Integer> usedIndices = new HashSet<Integer> ();
		ensureLookAhead (0);
		log.trace ("Sync start " + getName ());
		api.scanTransactions (getMaster (), lookAhead, getCreated (), new TransactionListener ()
		{
			@Override
			public void process (Transaction t)
			{
				for ( TransactionOutput o : t.getOutputs () )
				{
					Integer thisKey = getKeyIDForAddress (o.getOutputAddress ());
					if ( thisKey != null )
					{
						usedIndices.add (thisKey);
						ensureLookAhead (thisKey);
						nextSequence = Math.max (nextSequence, thisKey + 1);
					}
				}
				updateWithTransaction (t);
			}
		});
		log.trace ("Sync finish " + getName () + " used: " + usedIndices.size () + " keys of " + getNumberOfKeys () + " look ahead:" + lookAhead);
		return usedIndices;
	}
}

@@ -721,34 +721,8 @@
 ap;%0A
-import java.util.HashSet;%0A
 impo
@@ -765,30 +765,8 @@
 Map;
-%0Aimport java.util.Set;
 %0A%0Aim
@@ -3219,28 +3219,20 @@
 %09public 
-Set%3CInteger%3E
+void
  sync (B
@@ -3346,125 +3346,93 @@
 ;%0A%09%09
-final Set%3CInteger%3E usedIndices = new HashSet%3CInteger%3E ();%0A%09%09ensureLookAhead (0);%0A%09%09log.trace (%22Sync start %22 + getName
+ensureLookAhead (0);%0A%09%09log.trace (%22Sync %22 + getName () + %22 nkeys: %22 + getNumberOfKeys
  ())
@@ -3747,41 +3747,8 @@
 %09%09%7B%0A
-%09%09%09%09%09%09usedIndices.add (thisKey);%0A
 %09%09%09%09
@@ -3913,15 +3913,8 @@
 ync 
-finish 
 %22 + 
@@ -3932,49 +3932,28 @@
 + %22 
-used: %22 + usedIndices.size () + %22 
+finished with n
 keys
- of
+:
  %22 +
@@ -3975,60 +3975,9 @@
 s ()
- + %22 look ahead:%22 + lookAhead);%0A%09%09return usedIndices
+)
 ;%0A%09%7D

Revert "report back used keys from scan"