/*
 * Created on 31 mai 2005 by richet
 */
package org.math.plot.render;

import org.math.plot.plotObjects.*;
import org.math.plot.utils.FastMath;

public abstract class Projection {

    protected int[][] baseScreenCoords;
    public static double DEFAULT_BORDER = 0.15;
    protected double borderCoeff = 1 - 2 * DEFAULT_BORDER;
    protected AWTDrawer draw;

    public Projection(AWTDrawer _draw) {
        draw = _draw;
    }

    public void initBaseCoordsProjection(boolean reset) {
        // System.out.println("Projection.initBaseCoordsProjection");
        if (baseScreenCoords == null) {
            baseScreenCoords = new int[draw.canvas.base.baseCoords.length][2];
        }
        if (reset) {
            totalScreenRatio[0] = 1;
            totalScreenRatio[1] = 1;
        }
        for (int i = 0; i < draw.canvas.base.dimension + 1; i++) {
            // Compute the basis extremity coordinates in the normed-centered screen (ie [-0.5,0.5]x[-0.5,0.5] screen)
            double[] ratio = baseCoordsScreenProjectionRatio(draw.canvas.base.baseCoords[i]);
            // Compute the basis extremity coordinates in the true screen (ie in px: [0,400]x[0,400])
            baseScreenCoords[i][0] = (int) (draw.canvas.getWidth() * (.5 + (borderCoeff * ratio[0] / totalScreenRatio[0])));
            baseScreenCoords[i][1] = (int) (draw.canvas.getHeight() * (.5 - (borderCoeff * ratio[1] / totalScreenRatio[1])));
        }
        //System.err.println("\n" + Array.toString(baseScreenCoords));
    }

    // ///////////////////////////////////////////
    // ////// move methods ///////////////////////
    // ///////////////////////////////////////////
    public void translate(int[] screenTranslation) {
        for (int i = 0; i < draw.canvas.base.dimension + 1; i++) {
            baseScreenCoords[i][0] = baseScreenCoords[i][0] + screenTranslation[0];
            baseScreenCoords[i][1] = baseScreenCoords[i][1] + screenTranslation[1];
        }
    }
    // This stores the whole zooming ratio along all dilate calls.
    public double[] totalScreenRatio = new double[]{1, 1};
    public double[] maxScreenRatio = new double[]{1, 1};
    public double[] minScreenRatio = new double[]{.01, .01};

    public void dilate(int[] screenOrigin, double[] screenRatio) {
        // System.out.println("screenOrigin = "+screenOrigin[0]+" ,
        // "+screenOrigin[1]);
        // System.out.println("screenRatio = "+screenRatio[0]+" ,
        // "+screenRatio[1]);

        // Update the zooming ratio history
        if (totalScreenRatio[0] * screenRatio[0] > maxScreenRatio[0]) screenRatio[0] = maxScreenRatio[0]/totalScreenRatio[0];
        if (totalScreenRatio[1] * screenRatio[1] > maxScreenRatio[1]) screenRatio[1] = maxScreenRatio[1]/totalScreenRatio[1];

        if (totalScreenRatio[0] * screenRatio[0] < minScreenRatio[0]) screenRatio[0] = minScreenRatio[0]/totalScreenRatio[0];
        if (totalScreenRatio[1] * screenRatio[1] < minScreenRatio[1]) screenRatio[1] = minScreenRatio[1]/totalScreenRatio[1];
        
        for (int i = 0; i < draw.canvas.base.dimension + 1; i++) {
            // System.out.println("baseScreenCoords["+i+"] =
            // "+baseScreenCoords[i][0]+" , "+baseScreenCoords[i][1]);
            baseScreenCoords[i][0] = (int) ((baseScreenCoords[i][0] - screenOrigin[0]) / screenRatio[0]);
            baseScreenCoords[i][1] = (int) ((baseScreenCoords[i][1] - screenOrigin[1]) / screenRatio[1]);
            // System.out.println(" -> baseScreenCoords["+i+"] =
            // "+baseScreenCoords[i][0]+" , "+baseScreenCoords[i][1]);
        }
        // Update the zooming ratio history
        totalScreenRatio[0] = totalScreenRatio[0] * screenRatio[0];
        totalScreenRatio[1] = totalScreenRatio[1] * screenRatio[1];
    }

    // ///////////////////////////////////////////
    // ////// projection method //////////////////
    // ///////////////////////////////////////////
    public int[] screenProjection(double... pC) {
        // System.out.println("Projection.screenProjection("+Array.toString(pC)+")");
        double[] sC = new double[2];
        sC[0] = baseScreenCoords[0][0];
        sC[1] = baseScreenCoords[0][1];
        for (int i = 0; i < draw.canvas.base.dimension; i++) {
            double normdist_pC_baseCoords = 0;
            if (draw.canvas.base.axesScales[i].equalsIgnoreCase(Base.LOGARITHM)) {
                normdist_pC_baseCoords = ((FastMath.log(pC[i]) - FastMath.log(draw.canvas.base.baseCoords[0][i])) / (FastMath.log(draw.canvas.base.baseCoords[i + 1][i]) - FastMath.log(draw.canvas.base.baseCoords[0][i])));
            } else if (draw.canvas.base.axesScales[i].equalsIgnoreCase(Base.LINEAR) || draw.canvas.base.axesScales[i].equalsIgnoreCase(Base.STRINGS)) {
                normdist_pC_baseCoords = ((pC[i] - draw.canvas.base.baseCoords[0][i]) / (draw.canvas.base.baseCoords[i + 1][i] - draw.canvas.base.baseCoords[0][i]));
            }
            sC[0] += normdist_pC_baseCoords * (baseScreenCoords[i + 1][0] - baseScreenCoords[0][0]);
            sC[1] += normdist_pC_baseCoords * (baseScreenCoords[i + 1][1] - baseScreenCoords[0][1]);
        }

        if (draw.base_offset != null) {
            for (int i = 0; i < draw.canvas.base.dimension; i++) {
                sC[0] += draw.base_offset[i] * (baseScreenCoords[i + 1][0] - baseScreenCoords[0][0]);
                sC[1] += draw.base_offset[i] * (baseScreenCoords[i + 1][1] - baseScreenCoords[0][1]);
            }
        }

        if (draw.screen_offset != null) {
            sC[0] += draw.screen_offset[0];
            sC[1] += draw.screen_offset[1];
        }

        return new int[]{(int) sC[0], (int) sC[1]};
    }

    public int[] screenProjectionBase(double... rC) {
        double[] sC = new double[2];
        sC[0] = baseScreenCoords[0][0];
        sC[1] = baseScreenCoords[0][1];
        for (int i = 0; i < draw.canvas.base.dimension; i++) {
            sC[0] += rC[i] * (baseScreenCoords[i + 1][0] - baseScreenCoords[0][0]);
            sC[1] += rC[i] * (baseScreenCoords[i + 1][1] - baseScreenCoords[0][1]);
        }

        if (draw.base_offset != null) {
            for (int i = 0; i < draw.canvas.base.dimension; i++) {
                sC[0] += draw.base_offset[i] * (baseScreenCoords[i + 1][0] - baseScreenCoords[0][0]);
                sC[1] += draw.base_offset[i] * (baseScreenCoords[i + 1][1] - baseScreenCoords[0][1]);
            }
        }

        if (draw.screen_offset != null) {
            sC[0] += draw.screen_offset[0];
            sC[1] += draw.screen_offset[1];
        }

        return new int[]{(int) sC[0], (int) sC[1]};
    }

    protected abstract double[] baseCoordsScreenProjectionRatio(double[] xyz);
}