package mior.model;

import java.util.Random;

public class CPUMiorModel extends SimpleMiorModel {

	public final int OFFER_INVALID = -1;
	public final int OFFER_UNINIT = -2;

	public CPUMiorModel(MiorWorld world) {
		super(world);
	}

	@Override
	public void doTopologyImpl() {
		int occupation = 0;
		int tailleMax = 0;

		for (int iMM = 0; iMM < mmList.length; iMM++) {
			int taille = 0;

			for (int iOM = 0; iOM < omList.length; iOM++) {

				final MiorMM mm = mmList[iMM];
				final MiorOM om = omList[iOM];
				final int offset = iMM * world.nbOM + iOM;

				final float dx = mm.x - om.x;
				final float dy = mm.y - om.y;

				if (Math.sqrt(dx * dx + dy * dy) < world.RA) {
					associations[offset] = OFFER_UNINIT;
					taille++;
					occupation++;
				} else {
					associations[offset] = OFFER_INVALID;
				}
			}

			tailleMax = Math.max(tailleMax, taille);
		}

		final int maxCapacity = nbOM * nbMM;
		final double fraction = (100.0 * occupation) / maxCapacity;

		//System.out.println("Occupation : " + occupation + "/" + maxCapacity
		//		+ "\t(" + fraction + "%)");
		//System.out.println("Taille max : " + tailleMax + "/" + nbOM);
	}

	@Override
	protected void doLiveImpl() {
		for (int iMM = 0; iMM < world.nbMM; iMM++) {
			final int indexes[] = getOMIndexes();

			breath(iMM, indexes);

			if (mmList[iMM].dormance == 0) {
				grow(iMM, indexes);
			}
		}

		clean();
	}

	@Override
	protected void releaseImpl() {
	}

	private void breath(int iMM, int[] indexes) {
		final MiorMM mm = mmList[iMM];
		final int totalNeed = (int) (mm.carbone * world.RR);
		int remainingNeed = totalNeed;

		// Check if ressources seems to be available

		for (final int iOM : indexes) {
			final int offset = iMM * world.nbOM + iOM;

			if (remainingNeed < 0) {
				break;
			}

			// System.out.println(associations[mmOffset + iOM]);
			if (associations[offset] != OFFER_INVALID) {
				final int offer = (int) (world.K * omList[iOM].carbone);
				// System.out.println("MM(" + iMM + "): OM(" + iOM + ") offers "
				// + offer + ", remaining need : " + remainingNeed);
				remainingNeed -= offer;
			}
		}

		if (remainingNeed > 0) {
			mm.dormance = 1;
		} else {
			mm.dormance = 0;
			remainingNeed = totalNeed;

			for (final int iOM : indexes) {
				if (remainingNeed < 0) {
					break;
				}

				final int offset = iMM * world.nbOM + iOM;

				if (associations[offset] != OFFER_INVALID) {
					final MiorOM om = omList[iOM];
					int offer, consum;

					synchronized (om) {
						offer = (int) (world.K * om.carbone);
						consum = Math.min(offer, remainingNeed);

						remainingNeed -= consum;
						om.carbone -= consum;
					}

					synchronized (world) {
						world.CO2 += consum;
					}

					associations[offset] = offer - consum;
				}
			}
		}

	}

	private int[] getOMIndexes() {
		final Random rng = new Random();
		final int[] indexes = new int[world.nbOM];

		for (int i = 0; i < indexes.length; i++) {
			indexes[i] = i;
		}

		shuffle(indexes, rng);
		return indexes;
	}

	private void grow(int iMM, int[] indexes) {
		final MiorMM mm = mmList[iMM];

		final int totalNeed = (int) (mm.carbone * world.GR);
		int remainingNeed = totalNeed;

		for (final int iOM : indexes) {
			if (remainingNeed < 0) {
				break;
			}

			final int omOffset = iMM * omList.length + iOM;
			int offer;

			if (associations[omOffset] != OFFER_INVALID) {
				final MiorOM om = omList[iOM];

				synchronized (om) {
					if (associations[omOffset] != OFFER_UNINIT) {
						offer = associations[omOffset];
					} else {
						offer = (int) (world.K * om.carbone);
					}

					final int consum = Math.min(offer, remainingNeed);

					remainingNeed -= consum;
					om.carbone -= consum;
					mm.carbone += consum;
				}

			}
		}
	}

	private void clean() {
		for (int i = 0; i < associations.length; i++) {
			if (associations[i] != OFFER_INVALID) {
				associations[i] = OFFER_UNINIT;
			}
		}
	}

	private void shuffle(int[] a, Random rng) {
		for (int i = a.length - 1; i >= 1; i--) {
			final int j = rng.nextInt(i + 1);
			final int t = a[i];
			a[i] = a[j];
			a[j] = t;
		}
	}

}
