package draughts;

import grid.Grid;
import position.Direction;
import position.Position;

/**
 * A draughts csomagban hozzuk létre az absztrakt Piece osztályt!
 * A dámajátékban kétféle bábu van: egyszerű és dáma. Az alábbiakban a két
 * fajtához egy-egy osztályt készítünk (Man és King), kiemelve a közös vonásokat
 * egy szülőosztályba (Piece). A bábukkal léphetünk a játéktáblán. Lépés előtt
 * megvizsgáljuk, hogy szabályos-e a lépés. Ha igen, áthelyezzük a bábut,
 * ellenkező esetben kivételt váltunk ki.
 */
public abstract class Piece {

	/**
	 * Az osztályon belül definiáljuk a nyilvános, osztályszintű Color
	 * felsorolási típust WHITE és BLACK értékekkel!
	 */
	public static enum Color {

		WHITE, BLACK
	}
	/**
	 * Vegyük fel a következő adattagokat. Mindegyik hozzáférhető a leszármazott
	 * számára (de nem nyilvános).
	 * Position objektum, mely a bábu pozíciója a játéktáblán.
	 */
	protected Position p;

	/**
	 * Color objektum, mely a bábu színe. Ez az adattag nem módosítható (final).
	 */
	protected final Color c;

	/**
	 * Grid objektum Piece típussal paraméterezve, mely a játéktábla. Ez az
	 * adattag sem módosítható (final).
	 */
	protected final Grid g;

	/**
	 * Egy konstruktor, mely inicializálja az adattagokat. A paramétereket nem
	 * kell klónozni.
	 * @param p paraméterül várja a bábu pozícióját
	 * @param c színét
	 * @param g és a játéktáblát
	 */
	public Piece(Position p, Color c, Grid g) {
		this.p = p;
		this.c = c;
		this.g = g;
	}

	/**
	 * canStepTo, melyet nem tudjuk itt megadni, ezért legyen absztrakt.
	 * @param p paraméterül vár egy pozíciót
	 * @return csak akkor ad vissza logikai igaz értéket, ha a bábu léphet a
	 * megadott pozícióra
	 */
	public abstract boolean canStepTo(Position p);

	/**
	 * stepTo: Ha nem sikerült a megadott pozícióra lépni, egy
	 * InvalidStepException kivételt vált ki. Ehhez vizsgáljuk meg, hogy
	 * tudunk-e a bábuval az adott helyre lépni (canStepTo). Ha nem, dobjunk
	 * InvalidStepException kivételt, átadva az eredeti és a paraméter pozíciót.
	 * Ellenkező esetben végezzük az alábbi lépéseket:
	 * @param q paraméterül vár egy pozíciót
	 * @throws draughts.InvalidStepException
	 * @noreturn nincs visszatérési értéke
	 */
	public void stepTo(Position q) throws InvalidStepException {
		if (canStepTo(q)) {
			/**
			 * Számítsuk ki a bábu eredeti helye és a paraméter közötti
			 * távolságot (distance).
			 * A távolság alapján kérdezzük le, melyik irányba kell lépni a
			 * bábuval (fromDistance).
			 */
			Direction d = Direction.fromDistance(p.distance(q));

			/**
			 * Az irány alapján hozzuk létre a bábu aktuális pozíciójával
			 * szomszédos pozíciót (next).
			 */
			Position n = p.next(d);

			/**
			 * Ha a szomszédos pozíció még nem az, ahova kell lépni (equals),
			 * akkor ez azt jelenti, hogy van egy bábu, melyet át kell ugornunk,
			 * azaz le kell ütnünk. A leütés azt jelenti, hogy az adott helyen a
			 * játéktáblán null lesz (set).
			 */
			if (!n.equals(q)) {
				g.set(n, null);
			}
			/**
			 * Helyezzük át a bábut eredeti helyéről az újra. Ehhez legyen az
			 * eredeti helyen null, és az új helyre állítsuk be az aktuális
			 * bábut. Ne felejtsük el átállítani a bábu helyét jelölő adattagot
			 * sem!
			 */
			g.set(p, null);
			g.set(q, this);
			p = n;
		} else {
			throw new InvalidStepException(p, q);
		}
	}

	/**
	 * Tegyük absztrakttá a toString metódust! Ezzel azt érjük el, hogy a
	 * leszármazottakban kötelező legyen ezt is megadni.
	 * @return String
	 */
	@Override
	public abstract String toString();

}
