//implement push and pull model in observer pattern
//author: Weiheng
//for: 776 design pattern
//using the running time calculating the cost of the two models.
#include <iostream>
#include <stdio.h>
#include <vector>
#include<cstdlib>
#include<ctime>

/*define the interface of observer*/
class IObserver {
public:
	virtual void update(std::vector<int> sprice) = 0;
	virtual void modify() = 0;
	virtual void showprice() = 0;
};

/*define the interface of subject*/
class ISubject {
public:
	virtual const std::vector<int> prices() = 0;
	virtual void add(IObserver *obs) = 0;
	virtual void remove(IObserver *obs) = 0;
	virtual void publish() = 0;
};




/*define company*/
class Company : public ISubject {
private:
	std::string model_;
	std::vector<int> price;
	std::vector<IObserver*> observers;
public:
	Company(std::string model) {
		model_ = model;
		srand((unsigned)time(NULL));
		for (int i = 0; i < 10; i++) {
			price.push_back(rand());
			std::cout << price[i] << " ";
		}
		

	}//push model, x pull model.
    const std::vector<int> prices() {
		return price;
	}
	void add(IObserver *obs) {
		observers.push_back(obs);
		std::cout << observers.size() << std::endl;
	}

	void remove(IObserver *obs) {
		int i = 0;
		while (i < observers.size()) {
			if (observers[i] == obs) break;
			i++;
		}
		std::swap(observers[i], observers[observers.size()-1]);
		observers.pop_back();
	}

	void publish() {
		srand((unsigned)time(NULL));
		for (int i = 0; i < 10; i++) {
			price[i] = rand();
		}

		if (model_=="push") {
			for (auto obs : observers) {
				obs->update(price);
			}
		}
		else {
			for (auto obs : observers) {
				obs->modify();
			}
		}
	}

};

/*define users*/
class User : public IObserver {
private:
	std::vector<int> price;
	bool needupdate = false;
	ISubject *Sub_;
public:
	User(ISubject *sub) {
		
		Sub_ = sub;
		for (int i = 0; i < 10; i++) {
			price.push_back((*Sub_).prices()[i]);
			std::cout << price[i];
		}

	}
	void update(std::vector<int> sprice) {
		for (int i = 0; i < sprice.size(); i++) {
			price[i] = sprice[i];
		}
	}
	void modify() {
		needupdate = true;
	}

	void showprice() {
		if (needupdate) {
			for (int i = 0; i < price.size(); i++) {
				price[i] = (*Sub_).prices()[i];
			}
			needupdate = false;
		}
		for (int i = 0; i < price.size(); i++) {
			std::cout << price[i] << " ";
		}
		std::cout << std::endl;

	}


};

int main() {
	clock_t start_time = clock();
	ISubject *sub = new Company("pull");//using the push model
	std::cout << std::endl;
	std::cout << (*sub).prices()[3];
	IObserver *ob1 = new User(sub);
	IObserver *ob2 = new User(sub);
	sub->add(ob1);
	sub->add(ob2);
	for (int i = 0; i < 10000; i++) {//change here, publish several times and user only show it once. 
		sub->publish();
		ob1->showprice();
	}
	
	clock_t end_time = clock();
	std::cout << "Running time is: " << static_cast<double>(end_time - start_time) / CLOCKS_PER_SEC * 1000 << "ms" << std::endl;//print run time;

	getchar();
	getchar();
	return 0;

}