package cz.fidentis.analyst.symmetry;

import cz.fidentis.analyst.canvas.Canvas;
import cz.fidentis.analyst.core.ControlPanelAction;
import cz.fidentis.analyst.visitors.mesh.CrossSectionZigZag;
import org.openide.filesystems.FileChooserBuilder;

import javax.swing.JTabbedPane;
import javax.swing.JToggleButton;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.vecmath.Point3d;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * Action listener for the manipulation with the cutting plane.
 *
 * @author Dominik Racek
 */
public class ProfilesAction extends ControlPanelAction {

    /*
     * GUI elements
     */
    private final ProfilesPanel controlPanel;
    private final JTabbedPane topControlPanel;

    /*
     * Calculated profiles
     */
    private List<Point3d> primaryPoints;
    private List<Point3d> secondaryPoints;
    private List<Point3d> primaryMirrorPoints;
    private List<Point3d> secondaryMirrorPoints;

    private double lastSliderValue = 0.5;

    /**
     * Constructor.
     *
     * @param canvas          OpenGL canvas
     * @param topControlPanel Top component for placing control panels
     */
    public ProfilesAction(Canvas canvas, JTabbedPane topControlPanel) {
        super(canvas, topControlPanel);
        this.topControlPanel = topControlPanel;

        recomputePrimaryProfile();

        if (getSecondaryDrawableFace() != null) {
            recomputeSecondaryProfile();

            controlPanel = new ProfilesPanel(this, this.primaryPoints, this.secondaryPoints);
            controlPanel.setSecondaryMirrorPoints(this.secondaryMirrorPoints);
        } else {
            controlPanel = new ProfilesPanel(this, this.primaryPoints);
        }
        controlPanel.setPrimaryMirrorPoints(this.primaryMirrorPoints);

        // Place control panel to the topControlPanel
        this.topControlPanel.addTab(controlPanel.getName(), controlPanel.getIcon(), controlPanel);
        this.topControlPanel.addChangeListener(e -> {
            // If the symmetry panel is focused...
            if (((JTabbedPane) e.getSource()).getSelectedComponent() instanceof ProfilesPanel) {
                getCanvas().getScene().setDefaultColors();

                if (controlPanel.isShowPlaneChecked()) {
                    getCanvas().getScene().showCuttingPlanes();
                }

                if (controlPanel.isMirrorCutsChecked()) {
                    getCanvas().getScene().showMirrorPlanes();
                }
            } else {
                getCanvas().getScene().hideCuttingPlanes();
                getCanvas().getScene().hideMirrorPlanes();
            }
        });
        getCanvas().getScene().hideCuttingPlanes();
        getCanvas().getScene().hideMirrorPlanes();
    }

    private void exportProfile(List<Point3d> points, String title) {
        File file = new FileChooserBuilder(ProfilesAction.class)
                .setTitle(title)
                .setDefaultWorkingDirectory(new File(System.getProperty("user.home")))
                .setFilesOnly(true)
                .setFileFilter(new FileNameExtensionFilter("csv files (*.csv)", "csv"))
                .setAcceptAllFileFilterUsed(true)
                .showSaveDialog();

        if (file == null) {
            return;
        }

        // If chosen file exists, use the exact file path
        // If chosen file does not exist and does not have an extension, add it
        if (!file.exists()) {
            if (!file.getAbsolutePath().endsWith(".csv")) {
                file = new File(file.getAbsolutePath() + ".csv");
            }
        }

        try {
            file.createNewFile();

            PrintWriter writer = new PrintWriter(file.getAbsoluteFile(), "UTF-8");
            writer.println("N,X-Coordinate,Z-Coordinate");
            for (int i = 0; i < points.size(); ++i) {
                writer.println((i+1) + "," + points.get(i).x + "," + points.get(i).z);
            }

            writer.close();
        } catch (IOException ex) {
            System.out.println("ERROR writing to a file: " + ex);
        }
    }

    private void recomputePrimaryProfile() {
        //Main profile
        CrossSectionZigZag cs = new CrossSectionZigZag(getScene().getDrawableCuttingPlane(0).getPlane());
        getPrimaryDrawableFace().getModel().compute(cs);
        this.primaryPoints = cs.getPoints();

        //Mirror profile
        CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableMirrorPlane(0).getPlane());
        getPrimaryDrawableFace().getModel().compute(mcs);
        this.primaryMirrorPoints = mcs.getPoints();
    }

    private void recomputeSecondaryProfile() {
        //Main profile
        CrossSectionZigZag cs = new CrossSectionZigZag(getScene().getDrawableCuttingPlane(1).getPlane());
        getSecondaryDrawableFace().getModel().compute(cs);
        this.secondaryPoints = cs.getPoints();

        //Mirror profile
        CrossSectionZigZag mcs = new CrossSectionZigZag(getScene().getDrawableMirrorPlane(1).getPlane());
        getSecondaryDrawableFace().getModel().compute(mcs);
        this.secondaryMirrorPoints = mcs.getPoints();
    }

    private void recomputeProfiles() {
        recomputePrimaryProfile();
        controlPanel.setPrimaryPoints(this.primaryPoints);
        controlPanel.setPrimaryMirrorPoints(this.primaryMirrorPoints);

        if (getSecondaryDrawableFace() != null) {
            recomputeSecondaryProfile();
            controlPanel.setSecondaryPoints(this.secondaryPoints);
            controlPanel.setSecondaryMirrorPoints(this.secondaryMirrorPoints);
        }
    }

    private void setCuttingPlaneOffset(int index, double value) {
        //Move cutting planes left and mirror planes right
        //If normal is negative, need to negate value
        if (getScene().getDrawableCuttingPlane(index).getPlane().getNormal().x < 0) {
            getScene().getDrawableCuttingPlane(index).translatePlane(-value);
        } else {
            getScene().getDrawableCuttingPlane(index).translatePlane(value);
        }

        if (getScene().getDrawableMirrorPlane(index).getPlane().getNormal().x < 0) {
            getScene().getDrawableMirrorPlane(index).translatePlane(value);
        } else {
            getScene().getDrawableMirrorPlane(index).translatePlane(-value);
        }
    }

    @Override
    public void actionPerformed(ActionEvent ae) {
        String action = ae.getActionCommand();

        switch (action) {
            case ProfilesPanel.ACTION_COMMAND_SHOW_HIDE_PLANE:
                if (((JToggleButton) ae.getSource()).isSelected()) {
                    getScene().showCuttingPlanes();
                    getScene().showMirrorPlanes();
                } else {
                    getScene().hideCuttingPlanes();
                    getScene().hideMirrorPlanes();
                }
                break;
            case ProfilesPanel.ACTION_COMMAND_EXPORT:
                exportProfile(this.primaryPoints, "Export primary face profile to file");
                if (controlPanel.isMirrorCutsChecked()) {
                    exportProfile(this.primaryMirrorPoints, "Export primary face mirror profile to file");
                }

                if (getSecondaryDrawableFace() != null) {
                    exportProfile(this.secondaryPoints, "Export secondary face profile to file");
                    if (controlPanel.isMirrorCutsChecked()) {
                        exportProfile(this.secondaryMirrorPoints, "Export secondary face mirror profile to file");
                    }
                }
                break;
            case ProfilesPanel.ACTION_OFFSET_CUTTING_PLANE:
                double value = controlPanel.getOffsetValue() - lastSliderValue;
                double multiplier = -150;

                setCuttingPlaneOffset(0, multiplier * value);

                if (getSecondaryDrawableFace() != null) {
                    setCuttingPlaneOffset(1, multiplier * value);
                }
                recomputeProfiles();
                lastSliderValue = controlPanel.getOffsetValue();
                break;
            case ProfilesPanel.ACTION_COMMAND_RECOMPUTE:
                recomputeProfiles();
                break;
            case ProfilesPanel.ACTION_ALIGN_PROFILES:
                controlPanel.setAlignProfiles();
                break;
            case ProfilesPanel.ACTION_MIRROR_CUTS:
                controlPanel.setMirrorCuts();

                if (controlPanel.isMirrorCutsChecked()) {
                    getCanvas().getScene().showMirrorPlanes();
                } else {
                    getCanvas().getScene().hideMirrorPlanes();
                }
                break;
            default:
                // do nothing
        }
        renderScene();
    }

}
