/**
 * *************************************************************************
 * Copyright (C) 2009 by Paul Lutus * lutusp@arachnoid.com * * This program is
 * free software; you can redistribute it and/or modify * it under the terms of
 * the GNU General Public License as published by * the Free Software
 * Foundation; either version 2 of the License, or * (at your option) any later
 * version. * * This program is distributed in the hope that it will be useful,
 * * but WITHOUT ANY WARRANTY; without even the implied warranty of *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General
 * Public License for more details. * * You should have received a copy of the
 * GNU General Public License * along with this program; if not, write to the *
 * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA. *
 * *************************************************************************
 */
/*
 * This class is identical in TankCalc and TankCalcAndroid
 * Please keep it that way
 */
package tankcalc;

import java.util.ArrayList;

/**
 *
 * @author lutusp
 */
final public class ImageGenerator {

    TankProcessor tvi;
    int xsteps = 0;
    int ysteps = 0;

    boolean lowRes = false;

    double sensorBoxSize = 1;
    int sensorConeSize = 4;
    int sensorXSteps;
    int sensorYSteps;
    int cylinderXSteps;
    ImageArray leftCap, cylinder, rightCap, sensor;

    ArrayList<ImageArray> leftView, rightView;

    TankProperties sharedTP;

    public ImageGenerator(TankProcessor tproc, TankProperties stp, boolean lr) {
        tvi = tproc;
        sharedTP = stp;
        lowRes = lr;

    }

    void p(String s) {
        System.out.println(s);
    }

    String pn(double n) {
        return String.format("%.4f", n);
    }

    public void setDrawingScale() {
        // shall we reset the view rotations and translations?
        double tolerance = 2;
        double me = tvi.maxExtent;
        double rf = tvi.g_R * 2;
        me = (me < rf) ? rf : me;
        if (sharedTP.modelScaleFactor == 0
                || me > sharedTP.modelOldScaleFactor * tolerance
                || me < sharedTP.modelOldScaleFactor / tolerance) {
            sharedTP.modelScaleFactor = me;
            sharedTP.modelScale = 800.0 / sharedTP.modelScaleFactor;
            sharedTP.modelTranslateX = 0;
            sharedTP.modelTranslateY = 0;
            sharedTP.modelTranslateZ = 0;
            sharedTP.modelOldScaleFactor = sharedTP.modelScaleFactor;
        }
        sharedTP.graphicPerspectiveFactor = (sharedTP.modelScaleFactor * 3);
    }

    public void rebuildModel(TankProperties stp) {

        sharedTP = stp;
        setDrawingScale();

        boolean lowMode = (lowRes && sharedTP.polygonMode);

        xsteps = (lowMode) ? 4 : sharedTP.graphicXDrawSteps;
        ysteps = (lowMode) ? 32 : sharedTP.graphicYDrawSteps;
        cylinderXSteps = (sharedTP.polygonMode) ? (lowRes) ? 1 : 8 : xsteps;
        sensorBoxSize = sharedTP.modelScaleFactor * .01;
        sensorXSteps = (lowMode) ? 8 : 16;
        sensorYSteps = (lowMode) ? 8 : 16;

        leftCap = new ImageArray(sharedTP.graphicDrawLeftCap, xsteps, ysteps);
        cylinder = new ImageArray(sharedTP.graphicDrawCylinder, cylinderXSteps, ysteps);
        rightCap = new ImageArray(sharedTP.graphicDrawRightCap, xsteps, ysteps);
        sensor = new ImageArray(sharedTP.graphicDrawSensor, sensorXSteps, sensorYSteps);

        genEndCap(
                leftCap,
                -(tvi.halfCylinder + tvi.g_rL),
                -tvi.halfCylinder, sharedTP.leftEndCapMode,
                false,
                tvi.g_rL,
                tvi.g_rLSign,
                tvi.leftMajorSphereRadius,
                Constants.leftCapColor, 1);
        genCylinder(
                cylinder,
                -tvi.halfCylinder,
                tvi.halfCylinder,
                tvi.g_R,
                1);
        genEndCap(rightCap,
                tvi.halfCylinder,
                tvi.halfCylinder + tvi.g_rR,
                sharedTP.rightEndCapMode,
                true, tvi.g_rR,
                tvi.g_rRSign,
                tvi.rightMajorSphereRadius,
                Constants.rightCapColor, 1);
        genSensor(sensor, 1);

        rotateDataSet(leftCap);
        rotateDataSet(cylinder);
        rotateDataSet(rightCap);
        
        leftCap.createPolygons();
        cylinder.createPolygons();
        rightCap.createPolygons();
        sensor.createPolygons();

        leftView = new ArrayList<ImageArray>();
        rightView = new ArrayList<ImageArray>();
        // some hidden line removal

        leftView.add(sensor);
        rightView.add(sensor);
        rightView.add(rightCap);
        leftView.add(leftCap);
        rightView.add(cylinder);
        leftView.add(cylinder);

        leftView.add(rightCap);
        rightView.add(leftCap);

    }

    void rotateDataSet(ImageArray set) {
        if (set.isActive()) {
            double sa = Math.sin(sharedTP.angleRadians);
            double ca = Math.cos(sharedTP.angleRadians);
            for (CartesianPoint[] row : set.point) {
                // rotate tank by user value
                for (CartesianPoint p : row) {
                    if (p != null) {
                        double x = p.x;
                        double y = p.y;
                        p.y = x * sa + y * ca;
                        p.x = x * ca - y * sa;
                    }
                }
            }
        }
    }

    void genSensor(ImageArray set, double alpha) {
        if (set.isActive()) {
            // sensor path orientstion
            double sr = 0;
            switch (sharedTP.sensorOrientation) {
                case Constants.SENSORSURFACE:
                    sr = 0;
                    break;
                case Constants.SENSORXAXIS:
                    sr = -sharedTP.angleRadians;
                    break;
                case Constants.SENSORYAXIS:
                    sr = -sharedTP.angleRadians + Math.PI / 2;
                    break;
            }
            double ssv = Math.sin(sr);
            double scv = Math.cos(sr);
            // sensor path extent
            double tankLen = tvi.g_L + tvi.g_rL + tvi.g_rR;
            double radius;
            double ystep = 2 * Math.PI / sensorYSteps;
            for (int x = 0; x <= sensorConeSize; x++) {
                CartesianPoint[] row = set.point[x];
                radius = x * sensorBoxSize / sensorConeSize;
                boolean start = true;
                for (int y = 0; y <= sensorYSteps; y++) {
                    double ar = y * ystep;
                    double sv = Math.sin(ar);
                    double cv = Math.cos(ar);
                    double xv = radius * cv;
                    double yv = radius;
                    row[y] = new CartesianPoint(
                            tvi.trueSensorPositionX + xv * scv + yv * ssv,
                            tvi.trueSensorPositionY + yv * scv - xv * ssv,
                            -(tvi.trueSensorPositionZ + radius * sv),
                            start,
                            Constants.sensorColor,
                            alpha);
                    start = false;

                }
            }

            int sensorPathSteps = sensorXSteps - (sensorConeSize + 1);
            radius = sensorBoxSize * ((sharedTP.polygonMode) ? .25 : 0.01);
            for (int x = 0; x <= sensorPathSteps; x++) {
                double sensorPos = (x / (double) (sensorPathSteps));
                CartesianPoint[] row = set.point[x + sensorConeSize + 1];
                boolean start = true;
                for (int y = 0; y <= sensorYSteps; y++) {
                    double ar = y * ystep;
                    double sv = Math.sin(ar);
                    double cv = Math.cos(ar);
                    double xv = radius * cv;
                    double yv = sensorBoxSize + (tankLen - sensorBoxSize) * sensorPos;
                    CartesianPoint p = new CartesianPoint(
                            tvi.trueSensorPositionX + xv * scv + yv * ssv,
                            tvi.trueSensorPositionY + yv * scv - xv * ssv,
                            -(tvi.trueSensorPositionZ + radius * sv),
                            start,
                            Constants.sensorColor,
                            alpha);
                    row[y] = p;
                    start = false;
                }
            }
        }

    }

    void genEndCap(ImageArray set, double ax, double bx, int endCapMode, boolean rev, double g_r, double g_rSign, double sphereRadius, IColor color, double alpha) {
        if (set.isActive()) {
            double radius;
            if (g_r == 0) {
                bx = ax + 1e-3;
                g_r = 1e-3;
                endCapMode = Constants.ELLIPSEMODE;
            }
            double xstep = g_r / xsteps;
            double ystep = 2 * Math.PI / ysteps;
            for (int x = 0; x <= xsteps; x++) {
                CartesianPoint[] row = set.point[x];
                double xcoord = ax + (x * xstep);
                radius = endCapRadius(xcoord, ax, bx, endCapMode, rev, g_r, sphereRadius);
                boolean start = true;
                for (int y = 0; y <= ysteps; y++) {
                    double ar = y * ystep;
                    CartesianPoint p = new CartesianPoint(xcoord, Math.cos(ar) * radius, Math.sin(ar) * radius * sharedTP.scalingFactor, start, color, alpha);
                    row[y] = p;
                    start = false;
                }
            }
        }
    }

    void genCylinder(ImageArray set, double ax, double bx, double radius, double alpha) {
        if (set.isActive()) {
            double xstep = (bx - ax) / cylinderXSteps;
            double ystep = 2 * Math.PI / ysteps;
            CartesianPoint[] row = null;
            for (int x = 0; x <= cylinderXSteps; x++) {
                try {
                    row = set.point[x];
                } catch (Exception e) {
                    break;
                }
                double xcoord = (x * xstep) + ax;
                boolean start = true;
                for (int y = 0; y <= ysteps; y++) {
                    double ar = y * ystep;
                    double car = Math.cos(ar) * radius;
                    double sar = Math.sin(ar) * radius * sharedTP.scalingFactor;
                    row[y] = new CartesianPoint(xcoord, car, sar, start,
                            Constants.cylinderColor,
                            alpha);
                    start = false;
                }
            }
        }
    }

    double endCapRadius(double x, double ax, double bx, int endCapMode, boolean rev, double g_r, double sphereRadius) {
        double delta;
        double radius = 0;
        double rt2 = g_r * 2;
        double invx, x2;
        double range = bx - ax;
        double ratio = tvi.g_R / g_r;
        x = (x > bx) ? bx : x;
        switch (endCapMode) {
            case Constants.ELLIPSEMODE: // elliptical end cap
                delta = ((rev) ? g_r : 0);
                x2 = (x - ax) + delta;
                invx = rt2 - x2;
                radius = Math.sqrt(invx * x2) * ratio;
                break;
            case Constants.CONICMODE: // conical end cap
                delta = (rev ? g_r : 0);
                x2 = (x - ax) + delta;
                invx = rt2 - x2;
                radius = ((invx < x2) ? invx : x2) * ratio;
                break;
            case Constants.SPHEREMODE: // spherical end cap
                delta = (rev ? sphereRadius - range : -sphereRadius);
                double srsq = sphereRadius * sphereRadius;
                x2 = (x - ax) + delta;
                radius = Math.sqrt(srsq - (x2 * x2));
                break;
        } // switch (endCapMode)
        radius = (Double.isNaN(radius)) ? 0 : radius;
        return radius;
    }
}
