package com.brackeen.javagamebook.graphics3D;

import java.awt.*;
import java.awt.image.*;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import com.brackeen.javagamebook.math3D.*;

/**
    Ta klasa SimpleTexturedPolygonRenderer demonstruje
    podstawy mapowania tekstur z zachowaniem perspektywy.
    Działa jednak bardzo powoli i mapuje na każdym wielokącie tę samą teksturę.
*/
public class SimpleTexturedPolygonRenderer extends PolygonRenderer
{
    protected Vector3D a = new Vector3D();
    protected Vector3D b = new Vector3D();
    protected Vector3D c = new Vector3D();
    protected Vector3D viewPos = new Vector3D();
    protected Rectangle3D textureBounds = new Rectangle3D();
    protected BufferedImage texture;

    public SimpleTexturedPolygonRenderer(Transform3D camera,
        ViewWindow viewWindow, String textureFile)
    {
        super(camera, viewWindow);
        texture = loadTexture(textureFile);
    }


    /**
        Ładuje obraz tekstury z pliku. Obraz ten będzie mapowany
        na wszystkich wielokątach.
    */
    public BufferedImage loadTexture(String filename) {
        try {
            return ImageIO.read(new File(filename));
        }
        catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }


    protected void drawCurrentPolygon(Graphics2D g) {

        // Wylicz granice tekstury.
        // Granice te powinny być już wyliczone i przechowywane 
        // wraz z wielokątem. Współrzędne wyliczamy jednak w celach
        // demonstracyjnych.
        Vector3D textureOrigin = textureBounds.getOrigin();
        Vector3D textureDirectionU = textureBounds.getDirectionU();
        Vector3D textureDirectionV = textureBounds.getDirectionV();

        textureOrigin.setTo(sourcePolygon.getVertex(0));

        textureDirectionU.setTo(sourcePolygon.getVertex(3));
        textureDirectionU.subtract(textureOrigin);
        textureDirectionU.normalize();

        textureDirectionV.setTo(sourcePolygon.getVertex(1));
        textureDirectionV.subtract(textureOrigin);
        textureDirectionV.normalize();

        // transformuj granice tekstury
        textureBounds.subtract(camera);

        // rozpocznij wyliczenia związane z mapowaniem tekstury 
        a.setToCrossProduct(textureBounds.getDirectionV(),
            textureBounds.getOrigin());
        b.setToCrossProduct(textureBounds.getOrigin(),
            textureBounds.getDirectionU());
        c.setToCrossProduct(textureBounds.getDirectionU(),
            textureBounds.getDirectionV());

        int y = scanConverter.getTopBoundary();
        viewPos.z = -viewWindow.getDistance();

        while (y<=scanConverter.getBottomBoundary()) {
            ScanConverter.Scan scan = scanConverter.getScan(y);

            if (scan.isValid()) {
                viewPos.y =
                    viewWindow.convertFromScreenYToViewY(y);
                for (int x=scan.left; x<=scan.right; x++) {
                    viewPos.x =
                        viewWindow.convertFromScreenXToViewX(x);

                    // wylicz lokalizację tekstury
                    int tx = (int)(a.getDotProduct(viewPos) /
                        c.getDotProduct(viewPos));
                    int ty = (int)(b.getDotProduct(viewPos) /
                        c.getDotProduct(viewPos));

                    // pobierz kolor rysowania
                    try {
                        int color = texture.getRGB(tx, ty);

                        g.setColor(new Color(color));
                    }
                    catch (ArrayIndexOutOfBoundsException ex) {
                        g.setColor(Color.red);
                    }

                    // narysuj piksel
                    g.drawLine(x,y,x,y);
                }
            }
            y++;
        }
    }

}