package info.limpet.data.operations;

import info.limpet.ICollection;
import info.limpet.ICommand;
import info.limpet.IOperation;
import info.limpet.IQuantityCollection;
import info.limpet.IStore;
import info.limpet.data.commands.AbstractCommand;
import info.limpet.data.impl.QuantityCollection;
import info.limpet.data.math.SimpleMovingAverage;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.measure.Quantity;

public class SimpleMovingAverageOperation implements IOperation<ICollection>
{
	public static final String SERIES_NAME_TEMPLATE = "Simple Moving Average ({0})";

	CollectionComplianceTests aTests = new CollectionComplianceTests();

	final protected int windowSize;

	public SimpleMovingAverageOperation(int windowSize)
	{
		this.windowSize = windowSize;
	}

	public Collection<ICommand<ICollection>> actionsFor(
			List<ICollection> selection, IStore destination)
			{
		Collection<ICommand<ICollection>> res = new ArrayList<ICommand<ICollection>>();
		if (appliesTo(selection))
		{
			String name = MessageFormat.format(SERIES_NAME_TEMPLATE, windowSize);
			ICommand<ICollection> newC = new SimpleMovingAverageCommand(
					name, name, selection, destination);
			res.add(newC);
		}

		return res;
	}

	private boolean appliesTo(List<ICollection> selection)
	{
		boolean singleSeries = selection.size() == 1;
		boolean allQuantity = aTests.allQuantity(selection);
		return (singleSeries && allQuantity);
	}

	public class SimpleMovingAverageCommand extends
			AbstractCommand<ICollection>
	{

		public SimpleMovingAverageCommand(String operationName, String outputName,
				List<ICollection> selection, IStore store)
		{
			super(operationName, "Calculates a Simple Moving Average", outputName,
					store, false, false, selection);
		}

		@Override
		public void execute()
		{
			IQuantityCollection<?> input = (IQuantityCollection<?>) _inputs.get(0);

			List<ICollection> outputs = new ArrayList<ICollection>();

			// ok, generate the new series
			IQuantityCollection<?> target = new QuantityCollection<>(input.getName()
					+ getOutputName(), this, input.getUnits());

			outputs.add(target);

			// store the output
			super.addOutput(target);

			// start adding values.
			performCalc(outputs);

			// tell each series that we're a dependent
			Iterator<ICollection> iter = _inputs.iterator();
			while (iter.hasNext())
			{
				ICollection iCollection = iter.next();
				iCollection.addDependent(this);
			}

			// ok, done
			List<ICollection> res = new ArrayList<ICollection>();
			res.add(target);
			getStore().addAll(res);
		}

		@Override
		protected void recalculate()
		{
			// update the results
			performCalc(_outputs);
		}

		/**
		 * wrap the actual operation. We're doing this since we need to separate it
		 * from the core "execute" operation in order to support dynamic updates
		 * 
		 * @param unit
		 * @param outputs
		 */
		private void performCalc(List<ICollection> outputs)
		{
			IQuantityCollection<?> target = (IQuantityCollection<?>) outputs.iterator().next();

			// clear out the lists, first
			Iterator<ICollection> iter = _outputs.iterator();
			while (iter.hasNext())
			{
				IQuantityCollection<?> qC = (IQuantityCollection<?>) iter.next();
				qC.getValues().clear();
			}

			SimpleMovingAverage sma = new SimpleMovingAverage(windowSize);
			IQuantityCollection<?> input = (IQuantityCollection<?>) _inputs.get(0);

			for (Quantity<?> quantity : input.getValues())
			{
				sma.newNum(quantity.getValue().doubleValue());
				target.add(sma.getAvg());
			}
		}
	}

}

@@ -344,40 +344,8 @@
 e;%0A%0A
-import java.text.MessageFormat;%0A
 impo
@@ -636,14 +636,8 @@
 rage
- (%7B0%7D)
 %22;%0A%0A
@@ -723,24 +723,25 @@
 otected int 
+_
 windowSize;%0A
@@ -804,16 +804,17 @@
 %0A%09%09this.
+_
 windowSi
@@ -940,18 +940,16 @@
 nation)%0A
-%09%09
 %09%7B%0A%09%09Col
@@ -1061,81 +1061,8 @@
 %09%09%7B%0A
-%09%09%09String name = MessageFormat.format(SERIES_NAME_TEMPLATE, windowSize);%0A
 %09%09%09I
@@ -1130,18 +1130,50 @@
 %09%09%09%09
-name, name
+SERIES_NAME_TEMPLATE, SERIES_NAME_TEMPLATE
 , se
@@ -1188,24 +1188,42 @@
  destination
+,%0A%09%09%09%09%09_windowSize
 );%0A%09%09%09res.ad
@@ -1508,20 +1508,17 @@
  extends
-%0A%09%09%09
+ 
 Abstract
@@ -1542,16 +1542,40 @@
 on%3E%0A%09%7B%0A%0A
+%09%09private int winSize;%0A%0A
 %09%09public
@@ -1688,16 +1688,32 @@
 re store
+, int windowSize
 )%0A%09%09%7B%0A%09%09
@@ -1822,16 +1822,237 @@
 ction);%0A
+%09%09%09winSize = windowSize;%0A%09%09%7D%0A%0A%09%09public int getWindowSize()%0A%09%09%7B%0A%09%09%09return winSize;%0A%09%09%7D%0A%0A%09%09public void setWindowSize(int winSize)%0A%09%09%7B%0A%09%09%09this.winSize = winSize;%0A%0A%09%09%09// ok, we now need to update!%0A%09%09%09super.dataChanged(null);%0A
 %09%09%7D%0A%0A%09%09@
@@ -2342,16 +2342,23 @@
 )%0A%09%09%09%09%09+
+ %22 %22 + 
  getOutp
@@ -3293,24 +3293,30 @@
 %3C?%3E) outputs
+%0A%09%09%09%09%09
 .iterator().
@@ -3601,19 +3601,16 @@
 rage(win
-dow
 Size);%0A%09

make the window size accessible