/* Skuska z OOP, 4. jun 2008, otazka 18

Vhodne je pouzit vzor Observer. Stopky predstavuju sledovany predmet (subject)), a displeje su vlastne pozorovatelia (observers). Lahko mozno pridat dalsie zobrazenie odvodenim od rozhrania TimeDisplay alebo aj iny druh stopiek odvodenim od rozhrania StopWatch.

(Kazdy typ by mal byt vo vlastnom subore.)

*/

public interface StopWatch {
	void incrementTime();
	void notifyDisplays();
	void addDisplay(TimeDisplay d);
	int getTime();
}

public class SecStopWatch implements StopWatch {
	private int time; // cas v sekundach
	private List<TimeDisplay> displays = new ArrayList<TimeDisplay>(); // zoznam displejov

	public void start() { ... } // spustenie merania
	public void stop() { ... } //  zastavenie merania

	public void reset() { time = 0; } // vynulovanie casu
	
	public int getTime() { // ziskanie casu
		return time;
	}

	public void incrementTime() { // posun casu
		time++; // vzdy posun o sekundu
		notifyDisplays(); // zaslanie notifikacie displejom
	}

	public void notifyDisplays() { // zaslanie notifikacie displejom
		for (TimeDisplay e : displays) { // notifikacia pojde vsetkym registrovanym displejom
			e.refresh(); // displej sam rozhodne co ma robit
	}

	void addDisplay(TimeDisplay d) { // registracia displeja
		displays.add(d);
	}
}


// Spolocne rozhranie vsetkych displejov
// Mohlo obsahovat aj dalsie metody, ale metoda refresh() je nevyhnutna
public interface TimeDisplay {
	refresh(); // aktualizacia vnutorneho stavu displeja
}

public class SecTimeDisplay implements TimeDisplay {
	private StopWatch timeSource;
	private int time;
	private int shift;
	
	public SecTimeDisplay(StopWatch w, int t) {
		timeSource = w; // displej musi poznat svoje stopky
		shift = t; // a casovy posun
	}
	
	public void refresh() { // aktualizacia vnutorneho stavu displeja
		time = timeSource.getTime(); // 
	}
	
	public void setShift(int s) { // nastavenie posunu
		shift += s;
	}

	public void display() { // samotne zobrazenie - v sekundach
		System.out.println(time + shift);
	}
}


// DigTimeDisplay je takmer rovnaky ako SecTimeDisplay - az na zobrazenie
// Spolocna struktura mohla byt v triede od ktorej by dedili obidva displeje
public class DigTimeDisplay {
	private StopWatch timeSource;
	private int time;
	private int shift;
	
	public DigTimeDisplay(StopWatch w, int t) {
		timeSource = w; // displej musi poznat svoje stopky
		shift = t; // a casovy posun
	}

	public void refresh() {
		time = timeSource.getTime();
	}
	
	public void setShift(int s) {
		shift += s;
	}

	public void display() { // samotne zobrazenie - hh:mm:ss
		int h = (time + shift) / 3600;
		int m = ((time + shift) % 3600) / 60;
		int s = (time + shift) % 60;
				
		System.out.println( h / 10 + h % 10 + ":" +
							m / 10 + m % 10 + ":" +
							s / 10 + s % 10 );
	}
}


// Priklad pouzitia:

// Majme jedny stopky
SecStopWatch sw = new SecStopWatch();

// Tri rozne displeje budu pripojene na tieto stopky. Budu ulozene v poli pre lahsiu manipulaciu.
TimeDisplay[] d = new TimeDisplay[3];

d[0] = new SecTimeDisplay(sw, 0);
d[1] = new SecTimeDisplay(sw, 10);
d[2] = new DigTimeDisplay(sw, -255);

// Tu by sme zaradili metodu incrementTime() do planovaca na vykonanie kazdu sekundu.

// Zobrazenie na displejoch je jednoduche:
for (TimeDisplay e : d)
	display();
