package com.srbenoit.render;

import com.srbenoit.geom.Point3;
import com.srbenoit.geom.Vector3;

/**
 * A vertex in world coordinates that a scene is composed of.
 */
public class WorldVertex extends Point3 {

    /** the index of this vertex in the scene where it is contained */
    private int indexInScene;

    /** the number of faces this vertex is part of */
    private int numFaces;

    /** the list of faces that contain the vertex */
    private WorldFace[] faces;

    /** the index of this vertex in each face in the faces array */
    private int[] indexInFaces;

    /**
     * Constructs a new <code>WorldVertex</code>.
     */
    public WorldVertex() {

        super();

        this.numFaces = 0;
        this.faces = new WorldFace[5];
        this.indexInFaces = new int[5];
        this.indexInScene = -1;
    }

    /**
     * Constructs a new <code>WorldVertex</code>.
     *
     * @param  xCoord  the X coordinate
     * @param  yCoord  the Y coordinate
     * @param  zCoord  the Z coordinate
     */
    public WorldVertex(final double xCoord, final double yCoord, final double zCoord) {

        super(xCoord, yCoord, zCoord);

        this.numFaces = 0;
        this.faces = new WorldFace[5];
        this.indexInFaces = new int[5];
        this.indexInScene = -1;
    }

    /**
     * Sets the index of this vertex in the scene.
     *
     * @param  index  the index
     */
    public void setIndexInScene(final int index) {

        this.indexInScene = index;
    }

    /**
     * Gets the index of this vertex in the scene.
     *
     * @return  the index
     */
    public int getIndexInScene() {

        return this.indexInScene;
    }

    /**
     * Adds a face to the list of faces this vertex is part of.
     *
     * @param  face   the face
     * @param  index  the index of this vertex in the face
     */
    public void addFace(final WorldFace face, final int index) {

        WorldFace[] newArray;
        int[] newIndices;

        if (this.numFaces == this.faces.length) {
            newArray = new WorldFace[this.numFaces + 5];
            System.arraycopy(this.faces, 0, newArray, 0, this.numFaces);
            this.faces = newArray;

            newIndices = new int[this.numFaces + 5];
            System.arraycopy(this.indexInFaces, 0, newIndices, 0, this.numFaces);
            this.indexInFaces = newIndices;
        }

        this.faces[this.numFaces] = face;
        this.indexInFaces[this.numFaces] = index;
        this.numFaces++;
    }

    /**
     * Gets the number of faces this vertex takes part in.
     *
     * @return  the number of faces
     */
    public int getNumFaces() {

        return this.numFaces;
    }

    /**
     * Gets a face this vertex takes part in.
     *
     * @param   index  the index of the face
     * @return  the face
     */
    public WorldFace getFace(final int index) {

        return this.faces[index];
    }

    /**
     * Gets the index of this vertex in one of the faces that it is part of.
     *
     * @param   index  the index of the face
     * @return  the index of this vertex in the specified face
     */
    public int getIndexInFace(final int index) {

        return this.indexInFaces[index];
    }

    /**
     * Computes the average of the normal vectors of all attached faces and normalizes the result
     * to obtain the normal vector at the vertex.
     *
     * @param  normal  the vector in which to place the computed normal
     */
    public void buildNormal(final Vector3 normal) {

        double x;
        double y;
        double z;

        if (this.numFaces == 0) {
            normal.setVec(0, 0, 1);
        } else {
            x = 0;
            y = 0;
            z = 0;

            for (int i = 0; i < this.numFaces; i++) {
                x += this.faces[i].getVecX();
                y += this.faces[i].getVecY();
                z += this.faces[i].getVecZ();
            }

            normal.setVec(x, y, z);
            normal.normalize(); // This method already deals with the 0-length case
        }
    }
}
