package com.brackeen.javagamebook.graphics3D;

import java.awt.Graphics2D;
import java.awt.Color;
import com.brackeen.javagamebook.math3D.*;

/**
    Klasa PolygonRenderer jest abstrakcyjną klasą odpowiedzialną
    za transformowanie wielokątów i rysowanie ich na ekranie.
*/
public abstract class PolygonRenderer {

    protected ScanConverter scanConverter;
    protected Transform3D camera;
    protected ViewWindow viewWindow;
    protected boolean clearViewEveryFrame;
    protected Polygon3D sourcePolygon;
    protected Polygon3D destPolygon;

    /**
        Tworzy nowy obiekt PolygonRenderer dla podanych obiektów
        Transform3D (kamery) i ViewWindow (okna obrazu). Obraz jest
        oczyszczany w momencie przywołania funkcji startFrame().
    */
    public PolygonRenderer(Transform3D camera,
        ViewWindow viewWindow)
    {
        this(camera, viewWindow, true);
    }


    /**
        Tworzy nowy obiekt PolygonRenderer dla podanych obiektów
        Transform3D (kamery) i ViewWindow (okna obrazu). Jeśli
        clearViewEveryFrame ma wartość true, obraz będzie oczyszczany
        w momencie przywołania funkcji startFrame().
    */
    public PolygonRenderer(Transform3D camera,
        ViewWindow viewWindow, boolean clearViewEveryFrame)
    {
        this.camera = camera;
        this.viewWindow = viewWindow;
        this.clearViewEveryFrame = clearViewEveryFrame;
        init();
    }


    /**
        Tworzy konwerter skanujący i docelowy wielokąt.
    */
    protected void init() {
        destPolygon = new Polygon3D();
        scanConverter = new ScanConverter(viewWindow);
    }


    /**
        Pobiera kamerę wykorzystywaną przez ten obiekt PolygonRenderer.
    */
    public Transform3D getCamera() {
        return camera;
    }


    /**
        Wskazuje początek renderowania klatki obrazu. Metoda ta
        powinna być przywoływana dla każdej klatki, zanim jeszcze 
        zostanie narysowany jakikolwiek wielokąt.
    */
    public void startFrame(Graphics2D g) {
        if (clearViewEveryFrame) {
            g.setColor(Color.black);
            g.fillRect(viewWindow.getLeftOffset(),
                viewWindow.getTopOffset(),
                viewWindow.getWidth(), viewWindow.getHeight());
        }
    }


    /**
        Informuje o końcu renderowania klatki obrazu. Metoda ta
        powinna być przywoływana dla każdej klatki już po narysowaniu
        wszystkich wielokątów.
    */
    public void endFrame(Graphics2D g) {
        // tymczasem nie rób nic.
    }


    /**
        Transformuje i rysuje wielokąt.
    */
    public boolean draw(Graphics2D g, Polygon3D poly) {
        if (poly.isFacing(camera.getLocation())) {
            sourcePolygon = poly;
            destPolygon.setTo(poly);
            destPolygon.subtract(camera);
            boolean visible = destPolygon.clip(-1);
            if (visible) {
                destPolygon.project(viewWindow);
                visible = scanConverter.convert(destPolygon);
                if (visible) {
                    drawCurrentPolygon(g);
                    return true;
                }
            }
        }
        return false;
    }


    /**
        Rysuje bieżący wielokąt. W tym momencie bieżący
        wielokąt jest transformowany, przycinany, rzutowany,
        konwertowany na linie i rysowany.
    */
    protected abstract void drawCurrentPolygon(Graphics2D g);
}