package ru.rsdn.patterns.mvc.model;

import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;

import ru.rsdn.patterns.behavioral.observer.Publisher;

/**
 * Модель
 * @param
 * <P>
 * свойство модели
 */
public class Model<P> extends Publisher<IModelSubscriber<P>> {
	private final AtomicReference<P> ref;

	private final ReentrantLock lock = new ReentrantLock();

	/**
	 * Конструктор
	 * @param property свойство модели
	 */
	public Model(P property) {
		if (property == null)
			throw new NullPointerException("Пустой параметр");
		ref = new AtomicReference<P>(property);
	}

	/**
	 * @return свойство модели
	 */
	public P getProperty() {
		return ref.get();
	}

	/**
	 * Установить свойство модели
	 * @param property свойство модели
	 */
	public void setProperty(P property) {
		if (property == null)
			throw new NullPointerException("Пустой параметр");
		if (ref.getAndSet(property) != property) {
			lock.lock();
			try {
				notifySubscribers();
			} finally {
				lock.unlock();
			}
		}
	}

	/**
	 * Оповестить всех подписчиков
	 */
	protected void notifySubscribers() {
		for (final IModelSubscriber<P> subscriber : getSubscribers())
			notifySubscriber(subscriber);
	}

	private void notifySubscriber(IModelSubscriber<P> subscriber) {
		try {
			subscriber.modelChanged(this);
		} catch (final RuntimeException e) {
			System.out
					.println("Обнаружен дефектный подписчик: " + subscriber);
			unsubscribe(subscriber);
		}
	}

	@Override
	public void subscribe(IModelSubscriber<P> subscriber) {
		super.subscribe(subscriber);
		notifySubscriber(subscriber);
	}

	@Override
	public String toString() {
		return ref.get().toString();
	}
}