/*******************************************************************************
*   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.singlequeuealgorithm;

import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

import com.analog.lyric.dimple.exceptions.DimpleException;
import com.analog.lyric.dimple.schedulers.dependencyGraph.StaticDependencyGraph;
import com.analog.lyric.dimple.schedulers.dependencyGraph.StaticDependencyGraphNode;
import com.analog.lyric.dimple.solvers.core.multithreading.MultiThreadingManager;
import com.analog.lyric.dimple.solvers.core.multithreading.MultithreadingAlgorithm;

/*
 * This algorithm uses a single work queue.  It uses the dependency graph code that
 * does not unroll loops.
 */
public class SingleQueueMutlithreadingAlgorithm extends MultithreadingAlgorithm
{

	public SingleQueueMutlithreadingAlgorithm(MultiThreadingManager manager)
	{
		super(manager);
	}

	/*
	 * Sets up the worker threads and kicks them off.
	 */
	@Override
	public void iterate(int numIters)
	{
		//get the dependency graph.
 		StaticDependencyGraph dg = getManager().getDependencyGraph();
 		 		
 		//do each iteration.
 		for (int j = 0; j < numIters; j++)
 		{
	 		//create the work qeuue
	 		LinkedBlockingQueue<StaticDependencyGraphNode> workQueue = new LinkedBlockingQueue<StaticDependencyGraphNode>();

	 		//Add all entries with no dependencies.
	 		for (StaticDependencyGraphNode dgn : dg.getInitialEntries())
	 		{
	 			workQueue.add(dgn);
	 		}

	 		//Keep track of how many nodes are left to update.
			AtomicInteger nodesLeft = new AtomicInteger(dg.getNumNodes());

	 		
			//Instantiate the Callable objects.
	 		int numThreads = getManager().getNumWorkers();
			ArrayList<Callable<Object>> workers = new ArrayList<Callable<Object>>();
	 		for (int i = 0; i < numThreads; i++)
	 			workers.add(new SingleQueueWorker(getManager().getSolverGraph(), workQueue, dg.getNumNodes(), nodesLeft));
	 		
	 		//Kick off the work
	 		try {
				getManager().getService().invokeAll(workers);
			} catch (InterruptedException e) {
				throw new DimpleException(e);
			}
 		}
	}

}
