package cz.fidentis.analyst.scene;

import cz.fidentis.analyst.face.HumanFace;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;

/**
 * Abstract class for ...
 * 
 * @author Radek Oslejsek
 */
public class Scene {
    
    private final List<HumanFace> faces = new ArrayList<>();
    private final List<DrawableFace> drawableFaces = new ArrayList<>();
    private final List<DrawableFeaturePoints> drawableFeaturePoints = new ArrayList<>();
    private final List<DrawablePlane> drawableSymmetryPlanes = new ArrayList<>();
    private final List<Drawable> otherDrawables = new ArrayList<>();
    
    /**
     * Constructor for single face analysis.
     * 
     * @param face Human face to be analyzed
     * @throws IllegalArgumentException if the {@code face} is {@code null}
     */
    public Scene(HumanFace face) {
        if (face == null) {
            throw new IllegalArgumentException("face");
        }

        faces.add(face);
        drawableFaces.add(new DrawableFace(face));
        if (face.getFeaturePoints() != null) {
            drawableFeaturePoints.add(new DrawableFeaturePoints(face.getFeaturePoints()));
        } else {
            drawableFeaturePoints.add(null);
        }
        drawableSymmetryPlanes.add(null);
        
        setDefaultColors();
    }
    
    /**
     * Constructor for one-to-one analysis.
     * 
     * @param primary Primary face to be analyzed
     * @param secondary Secondary face to be analyzed
     * @throws IllegalArgumentException if some face is {@code null}
     */
    public Scene(HumanFace primary, HumanFace secondary) {
        if (primary == null) {
            throw new IllegalArgumentException("primary");
        }
        if (secondary == null) {
            throw new IllegalArgumentException("secondary");
        }
        
        faces.add(primary);
        faces.add(secondary);
        
        drawableFaces.add(new DrawableFace(primary));
        drawableFaces.add(new DrawableFace(secondary));
        
        if (primary.getFeaturePoints() != null) {
            drawableFeaturePoints.add(new DrawableFeaturePoints(primary.getFeaturePoints()));
        } else {
            drawableFeaturePoints.add(null);
        }
        if (secondary.getFeaturePoints() != null) {
            drawableFeaturePoints.add(new DrawableFeaturePoints(secondary.getFeaturePoints()));
        } else {
            drawableFeaturePoints.add(null);
        }

        drawableSymmetryPlanes.add(null);
        drawableSymmetryPlanes.add(null);

        setDefaultColors();
    }
    
    /**
     * Sets default colors of faces and feature points
     */
    public final void setDefaultColors() {
        for (int i = 0; i < getNumFaces(); i++) {
            if (drawableFaces.get(i) != null) {
                drawableFaces.get(i).setColor((i == 0) ? DrawableFace.SKIN_COLOR_PRIMARY : DrawableFace.SKIN_COLOR_SECONDARY); 
            }
            if (drawableFeaturePoints.get(i) != null) {
                drawableFeaturePoints.get(i).setColor(getColorOfFeaturePoints((i == 0) ? DrawableFace.SKIN_COLOR_PRIMARY : DrawableFace.SKIN_COLOR_SECONDARY));
                drawableFeaturePoints.get(i).resetAllColorsToDefault();
            }
        }
    }
    
    /**
     * Returns number of faces in the scene.
     * @return number of faces in the scene
     */
    public int getNumFaces() {
        return faces.size();
    }
    
    /**
     * Returns human face.
     * 
     * @param index Index of the face
     * @return drawable face or {@code null}
     */
    public HumanFace getHumanFace(int index) {
        return (index >= 0 && index < getNumFaces()) ? faces.get(index) : null;
    }
    
    /**
     * Returns drawable face.
     * 
     * @param index Index of the face
     * @return drawable face or {@code null}
     */
    public DrawableFace getDrawableFace(int index) {
        return (index >= 0 && index < getNumFaces()) ? drawableFaces.get(index) : null;
    }
    
    /**
     * Returns drawable feature points.
     * 
     * @param index Index of the face
     * @return drawable face or {@code null}
     */
    public DrawableFeaturePoints getDrawableFeaturePoints(int index) {
        return (index >= 0 && index < getNumFaces()) ? drawableFeaturePoints.get(index) : null;
    }
    
    /**
     * Returns drawable symmetry plane.
     * 
     * @param index Index of the face
     * @return drawable face or {@code null}
     */
    public DrawablePlane getDrawableSymmetryPlane(int index) {
        return (index >= 0 && index < getNumFaces()) ? drawableSymmetryPlanes.get(index) : null;
    }
    
    /**
     * Sets the drawable symmetry plane.
     * 
     * @param index Index of the face
     * @param sPlane New symmetry plane
     */
    public void setDrawableSymmetryPlane(int index, DrawablePlane sPlane) {
        if (index >= 0 && index < getNumFaces() && sPlane != null) {
            this.drawableSymmetryPlanes.set(index, sPlane);
        }
    }
    
    /**
     * Adds new drawable object to the scene.
     * 
     * <p>
     * Note: This drawable object is ignored in the {@link setDefaultColors} method.
     * The caller must take care of the colors of the object themself.
     * </p>
     * 
     * @param otherDrawable Drawable object to be added to the scene
     */
    public void addOtherDrawable(Drawable otherDrawable) {
        otherDrawables.add(otherDrawable);
    }
    
    /**
     * Returns all drawable objects.
     * 
     * @return all drawable objects.
     */
    public List<Drawable> getAllDrawables() {
        List<Drawable> ret = new ArrayList<>();
        ret.addAll(this.drawableFaces);
        ret.addAll(this.drawableFeaturePoints);
        ret.addAll(this.drawableSymmetryPlanes);
        ret.addAll(this.otherDrawables);
        while (ret.remove(null)) {}
        return ret;
    }
    
    private Color getColorOfFeaturePoints(Color origColor) {
        return new Color(
                Math.min(255, origColor.getRed() + 40),
                Math.min(255, origColor.getGreen() + 40),
                Math.min(255, origColor.getBlue() + 40)
        );
    }
}
