/*
Objektovo-orientovane programovanie 2006/2007
Valentino Vranić
Skúška 22.6.2007 - opravný termín

Toto je kód jedného z možných riešení pre otázku 18. Využíva idióm double dispatch.

Každá trieda by mala byť v samostatnom súbore s názvom v tvare NázovTriedy.java. Pre úplnosť by na začiatku každého takého súboru mal byť uvedený balík do ktorého triedy patria a import balíkov java.util a java.io:

	package mymodel;
	import java.util.*;
	import java.io.*;
	
*/

import java.util.*;
import java.io.*;


// Dokumenty

interface PrintDocument {
	void print(Printer p); // požiadavka na zaradenie dokumentu do tlače na príslušnej tlačiarni
	void printNow(Printer p); // vytlačenie dokumentu na príslušnej tlačiarni
}

class PDFDocument implements PrintDocument {
	public byte[] doc;

	public void printNow(Printer p) {
		p.process(this); // skutočné vytlačenie dokumentu na príslušnej tlačiarni
	}

	public void print(Printer p) {
		p.insert(this); // vloženie dokumentu do radu
		p.startPrinting(); // spustenie tlače
	}
	public PDFDocument(DataInputStream f) { /* . . . */ }
}

class PSDocument implements PrintDocument {
	public byte[] doc;

	public void printNow(Printer p) {
		p.process(this);
	}

	public void print(Printer p) {
		p.insert(this);
		p.startPrinting();
	}
	public PSDocument(DataInputStream f) { /* . . . */ }
}

class BMPDocument implements PrintDocument {
	public byte[] doc;

	public void printNow(Printer p) {
		p.process(this);
	}

	public void print(Printer p) {
		p.insert(this);
		p.startPrinting();
	}
	public BMPDocument(DataInputStream f) { /* . . . */ }
}


// Ovladáče

interface Printer {
	void process(PDFDocument doc); // každý typ dokumentu sa spracúva inak
	void process(PSDocument doc);
	void process(BMPDocument doc);
	void insert(PrintDocument doc); // vloženie dokumentu do radu dokumentov na tlač
	void startPrinting(); // spustenie tlače radu dokumentov
}

class XPrinter implements Printer {
	private boolean printing; // príznak, či práve prebieha tlač
	private List<PrintDocument> queue = new ArrayList<PrintDocument>();
	
	public synchronized void insert(PrintDocument doc) {
		queue.add(doc);
	}

	private synchronized void remove(int i) {
		queue.remove(i);
	}

	private synchronized void get(int i) {
		queue.get(i);
	}
	
	public void startPrinting() {
		final Printer thisPrinter = this; // referencia na tento Printer je potrebná vo vnútornej triede
		
		if (!printing) {
			new Thread() { // tlač sa spustí v samostatnej niti
				public void run() {
					while (!queue.isEmpty()) {
						(queue.get(0)).printNow(thisPrinter);
						remove(0);
					}
				}
			}.start();
		}
	}

	public void process(PDFDocument doc) { /* . . . */ }
	public void process(PSDocument doc) { /* . . . */ }
	public void process(BMPDocument doc) { /* . . . */ }
}

class XPrinter1 extends XPrinter {
	public void process(PDFDocument doc) { /* . . . */ }
	public void process(PSDocument doc) { /* . . . */ }
	public void process(BMPDocument doc) { /* . . . */ }
}

class XPrinter2 extends XPrinter {
	public void process(PDFDocument doc) { /* . . . */ }
	public void process(PSDocument doc) { /* . . . */ }
	public void process(BMPDocument doc) { /* . . . */ }
}



class M {
	public static void main(String[] args) {
		DataInputStream f = ...;

		PrintDocument myDoc = new PDFDocument(f);
		Printer myPrinter = new XPrinter2();

		myDoc.print(myPrinter);
	}
}
