/*******************************************************************************
*   Copyright 2013 Analog Devices, Inc.
*
*   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.analog.lyric.dimple.solvers.core.multithreading.phasealgorithm;

import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.eclipse.jdt.annotation.Nullable;

import com.analog.lyric.dimple.schedulers.scheduleEntry.IScheduleEntry;
import com.analog.lyric.dimple.solvers.interfaces.ISolverFactorGraph;

/*
 * Responsible for picking of a chunk of schedule entries,
 * updating the entries, and then stealing work until there's nothing left.
 * 
 * Package protected
 */
class WorkerWithStealing implements Callable<Object>
{
	private final ISolverFactorGraph _solverGraph;
	private final ConcurrentLinkedQueue<IScheduleEntry> [] _deques;
	private final int _which;
	private final ArrayList<IScheduleEntry> _nodes;
	private final boolean _stealing;
	
	WorkerWithStealing(
		ISolverFactorGraph solverGraph,
		ArrayList<IScheduleEntry> nodes,
		int which, ConcurrentLinkedQueue<IScheduleEntry> [] deques,
		boolean stealing)
	{
		_solverGraph = solverGraph;
		_which = which;
		_deques = deques;
		_nodes= nodes;
		_stealing = stealing;
	}
	
	
	@Override
	public @Nullable Object call() throws Exception
	{
		//Which thread am I?
		int which = _which;
		
		//Figure out how many nodes I shold pick off.
		int nodesPerThread = _nodes.size() / _deques.length;
		int first = _which*nodesPerThread;
		int last = first + nodesPerThread - 1;
		
		//If I'm the last thread, I'm responsible for the last guy.
		if (which == _deques.length - 1)
			last = _nodes.size()-1;
		
		//Add the schedule entries to my queue.
		for (int i = first; i <= last; i++)
		{
			IScheduleEntry tmp = _nodes.get(i);
			_deques[which].add(tmp);
		}
		
		//Pick off the first guy
		IScheduleEntry n = _deques[which].poll();
	
		//Until there's nothing left.
		while (n != null)
		{
			//update the schedule entry.
			_solverGraph.runScheduleEntry(n);
						
			//get the next guy.
			n = _deques[which].poll();
			
			if (n == null && _stealing)
			{
				//If I'm out of work, see if I can steal work from any of the other guys.
				for (int i = 0; i < _deques.length; i++)
				{
					//Start after my entry so not everyone looks from the beginning.
					n = _deques[(_which + i) % _deques.length].poll();
					
					//If I still find no work, we're done.
					if (n != null)
						break;
				}
			}
			
		}
		return null;
	}
}
