package scalaSci.math.plot;

import java.awt.*;
import javax.swing.JFrame;
import javax.swing.JPanel;

import org.matheclipse.symja.OutputTextPane;
import org.scilab.forge.jlatexmath.TeXConstants;
import org.scilab.forge.jlatexmath.TeXFormula;
import org.scilab.forge.jlatexmath.TeXIcon;

// This class implements a lot of MATLAB like plotting facilities.
// In order to implement the grid like structure of Matlab subplots  a constructor
// is defined that builds these objects from an array of graphic panel objects of the same type.
// The FrameView class represents plotting frame objects similar to Matlab figures.
// They can contain a rectangular grid of subplot objects.

import scalaExec.Interpreter.GlobalValues;
import scalaSci.math.plot.plotObjects.Axis;
import scalaSci.math.plot.plotObjects.LatexImage;
import scalaSci.math.plot.plotObjects.texLabel;


public class plot {

    static public boolean scatterPlotOn = false;  // controls whether to connect adjacent points on the plot
    static int currentFigTableIndex = 0;   // the identifier of the working figure as its index at the Figure table
    static int currentPlotCnt2D = 0;  // holds the number of the existing 2D figure  objects
    static int currentPlotCnt3D = 0; // holds the number of the existing 3D figure objects
    static int currentPlotCnt = 0;  // a counter for the current number of figures
    static int maxNumberOfFigs = 20;  // maximum number of figure frames
    static double figTableIncreaseFactor = 1.5; // factor to increase dynamically the figure table size
    static PlotPanel[][][] allPlots = null;  // all the plot objects that belong to each MATLAB-like figure object, e.g. allPlots[f][] is the table with the references to the plots for figure f
    static FrameView[] allFrames = null; // holds the pointers to all the frames for all the figure objects

    static PlotPanel currentPlot = null;  // the handle where plot operations are directed

    static boolean new_figure = true;  // controls whether to create a new figure or to plot upon an existing one

    static boolean holdOnMode = true;  // controls holding previous plots, used to implement Matlab's hold("on"), hold("off")

    // plots all points, however this can be resource wasteful for large signals
    static public boolean fullPlotsOn() {
        boolean prevState = PlotGlobals.skipPointsOnPlot;
        PlotGlobals.skipPointsOnPlot = false;
        return prevState;
    }

    // fast plots are generated by skipping points for large plots in order to keep the number of points bounded
    static public boolean fastPlotsOn() {
        boolean prevState = PlotGlobals.skipPointsOnPlot;
        PlotGlobals.skipPointsOnPlot = true;
        return prevState;
    }

    static public int setMaximumPlotPoints(int newMaxPointsToPlot) {
        int prevMaxPointsToPlot = PlotGlobals.limitPlotPoints;
        PlotGlobals.limitPlotPoints = newMaxPointsToPlot;
        return prevMaxPointsToPlot;
    }

    // controls for 3-D plots the max points at the X-dimension
    static public int setMaximumPlotXPoints(int newMaxPointsXToPlot) {
        int prevMaxXPointsToPlot = PlotGlobals.limitPlotPointsX;
        PlotGlobals.limitPlotPointsX = newMaxPointsXToPlot;
        return prevMaxXPointsToPlot;
    }

    // controls for 3-D plots the max points at the Y-dimension
    static public int setMaximumPlotYPoints(int newMaxPointsYToPlot) {
        int prevMaxYPointsToPlot = PlotGlobals.limitPlotPointsY;
        PlotGlobals.limitPlotPointsY = newMaxPointsYToPlot;
        return prevMaxYPointsToPlot;
    }

    static public PlotPanel getPlot() {
        return currentPlot;
    }

    static public Axis getXAxis() {
        return currentPlot.getAxis(0);
    }

    static public Axis getYAxis() {
        return currentPlot.getAxis(1);
    }

    static public Axis getZAxis() {
        return currentPlot.getAxis(2);
    }

    // turn scatter plots on, in scatter plots points are not connected by a line
    static public boolean scatterPlotsOn() {
        boolean prevLineMode = scatterPlotOn;
        scatterPlotOn = true;
        return prevLineMode;
    }

    // turn line plots off, in line plots points are connected by a line
    static public boolean linePlotsOn() {
        boolean prevLineMode = scatterPlotOn;
        scatterPlotOn = false;
        return prevLineMode;
    }

    static public double[] arrToDouble(float[] x)  // convert float [] array to double []
    {
        int N = x.length;
        double[] dx = new double[N];
        for (int k = 0; k < N; k++)
            dx[k] = x[k];
        return dx;
    }

    static public double[][] arrToDoubleDouble(float[][] x)  // convert float [][] array to double [][]
    {
        int N = x.length;
        int M = x[0].length;
        double[][] dxy = new double[N][M];
        for (int r = 0; r < N; r++)
            for (int c = 0; c < M; c++)
                dxy[r][c] = x[r][c];
        return dxy;
    }


    // extracts the digits of a decimal number, in order to allow writing for example:
//   subplot(322), which is interpreted as subplot(3,2,2)
    public static void subplot(int p) {
        int r = p / 100;
        int rem = p % 100;
        int c = rem / 10;
        int id = rem % 10;
        subplot2d(r, c, id);
    }

    // extracts the digits of a decimal number, in order to allow writing for example:
//   subplot2d(322), which is interpreted as subplot2d(3,2,2)
    public static void subplot2d(int p) {
        int r = p / 100;
        int rem = p % 100;
        int c = p / 10;
        int id = rem % 10;
        subplot2d(r, c, id);
    }

    // extracts the digits of a decimal number, in order to allow writing for example:
//   subplot3d(322), which is interpreted as subplot3d(3,2,2)
    public static void subplot3d(int p) {
        int r = p / 100;
        int rem = p % 100;
        int c = p / 10;
        int id = rem % 10;
        subplot3d(r, c, id);
    }

    // increase the size of the global figure table when required
    public static void increaseFigTable() {
        int maxNumberOfFigsLarge = (int) (maxNumberOfFigs * figTableIncreaseFactor);  // update number of figures
        FrameView[] cpAllFrames = new FrameView[maxNumberOfFigsLarge];   // table that holds indices to the new figure frames
        System.arraycopy(allFrames, 0, cpAllFrames, 0, allFrames.length);
        PlotPanel[][][] cpAllPlots = new PlotPanel[maxNumberOfFigsLarge][][];
        System.arraycopy(allPlots, 0, cpAllPlots, 0, maxNumberOfFigs);
        for (int k = maxNumberOfFigs; k < maxNumberOfFigsLarge; k++) {  // initialize new entries to nulls
            cpAllPlots[k] = null;
            cpAllFrames[k] = null;
        }
        maxNumberOfFigs = maxNumberOfFigsLarge;  // update figure table size
        //   enlarged tables become the current
        allPlots = cpAllPlots;
        allFrames = cpAllFrames;
    }

    // increase the size of the tables used to keep figures, in order to cover the specified figure number
    // passed as a parameter, i.e. specifiedFigNo
    public static void increaseFigTableSpecifiedSize(int specifiedFigNo) {
        if (specifiedFigNo <= maxNumberOfFigs)
            return;  // specified size smaller than the current Figure table size
        int maxNumberOfFigsLarge = (int) (figTableIncreaseFactor * specifiedFigNo);  // update number of figs
        FrameView[] cpAllFrames = new FrameView[maxNumberOfFigsLarge];
        System.arraycopy(allFrames, 0, cpAllFrames, 0, allFrames.length);
        PlotPanel[][][] cpAllPlots = new PlotPanel[maxNumberOfFigsLarge][][];
        System.arraycopy(allPlots, 0, cpAllPlots, 0, maxNumberOfFigs);
        for (int k = maxNumberOfFigs; k < maxNumberOfFigsLarge; k++) {  // initialize new entries to nulls
            cpAllPlots[k] = null;
            cpAllFrames[k] = null;
        }
        maxNumberOfFigs = maxNumberOfFigsLarge;  // update figure table size
        //   enlarged tables become the current
        allPlots = cpAllPlots;
        allFrames = cpAllFrames;
    }


    // initializes the plotting system
    public static void initplots() {
        if (allFrames == null) {   // plotting system not initialized yet
            allPlots = new PlotPanel[maxNumberOfFigs][][];  // all the "subplot" objects
            for (int k = 0; k < maxNumberOfFigs; k++)
                allPlots[k] = null;
            allFrames = new FrameView[maxNumberOfFigs];   // all the "figure" objects
            for (int k = 0; k < maxNumberOfFigs; k++)
                allFrames[k] = null;
            currentPlotCnt2D = 0;
            currentPlotCnt3D = 0;
            currentPlotCnt = 0;

            currentFigTableIndex = 0;
            currentPlot = null;
            new_figure = true;
        }
    }

    // returns the current number of figure objects (e.g. FrameView objects), is the number of non-null entries of the allFrames[] table
    public static int getFigCount() {
        int figCnt = 0;
        for (int k = 0; k < maxNumberOfFigs; k++)
            if (allFrames[k] != null)
                figCnt++;

        return figCnt;
    }

    // create a new 2D figure object if we do not focus on an existing one
    public static void newPlot2D() {
        initplots();  // init plotting system if not yet initialized
        // if either we do not have a current 2D plotting panel or a new figure is explicitly requested we create a new figure
        if ((currentPlot == null) || (!(currentPlot instanceof Plot2DPanel) || new_figure)) {
            // assume a subplot2D(1,1,1), i.e.  the array of 2D subplots is initialized to a single subplot for the whole figure
            Plot2DPanel[][] subplots2D = new Plot2DPanel[1][1];
            Plot2DPanel newPanel = new Plot2DPanel();
            subplots2D[0][0] = newPanel;

            // construct the new 2D plot frame
            Plot2DPanel newplot = new Plot2DPanel(subplots2D, PlotPanel.NORTH);    // create a new 2D figure object

            currentFigTableIndex = getFigureId();  // request an available free figure identifier

            // initialize the frame window of the new figure
            String figTitle = "Fig. " + (currentFigTableIndex + 1);
            FrameView f = new FrameView(figTitle, newplot, currentFigTableIndex);
            if (GlobalValues.smallScalaImage != null)
                f.setIconImage(GlobalValues.smallScalaImage.getScaledInstance(400, 400, 0));

            allFrames[currentFigTableIndex] = f;   // keep the frame window of the new figure
            allPlots[currentFigTableIndex] = subplots2D; // keep the subplot structure

            currentPlotCnt++;
            currentPlotCnt2D++;   // a new 2D plot is created
            new_figure = false;
            currentPlot = subplots2D[0][0];  // focus on a component with a canvas
            currentPlot.setLegendOrientation("SOUTH");

        }

    }


    // create  a new 3D figure object if we do not focus on an existing one
    public static void newPlot3D() {
        initplots();  // init plotting system if not yet initialized
        // if either we do not have a current 3D ploting panel or a new figure is explicitly requested we create a new figure
        if ((currentPlot == null) || (!(currentPlot instanceof Plot3DPanel) || new_figure)) {
            // assume a subplot3D(1,1,1)
            Plot3DPanel[][] subplots3D = new Plot3DPanel[1][1];  // the array of 3D-subplots
            subplots3D[0][0] = new Plot3DPanel();
            // construct the new 3D plot frame
            Plot3DPanel newPlot = new Plot3DPanel(subplots3D, PlotPanel.NORTH);    // create a new 3D figure object
            String figTitle = "Fig. " + (currentFigTableIndex + 1);
            FrameView f = new FrameView(figTitle, newPlot, currentFigTableIndex);
            if (GlobalValues.smallScalaImage != null)
                f.setIconImage(GlobalValues.smallScalaImage);

            currentFigTableIndex = getFigureId();  // get a free figure id

            allFrames[currentFigTableIndex] = f; // keep the frame window of the new figure
            allPlots[currentFigTableIndex] = subplots3D;   // keep the subplot structure
            currentPlotCnt3D++;   // a new 3D plot is created

            currentPlotCnt++;
            new_figure = false;
            currentPlot = subplots3D[0][0];  // focus on a component with a canvas
            currentPlot.setLegendOrientation("SOUTH");

        }

    }

    // closes all the available figure objects
    public static void closeAll() {
        if (allFrames != null)
            for (int figId = 0; figId < maxNumberOfFigs; figId++)
                if (allFrames[figId] != null)
                    close(figId + 1);
        allFrames = null;
    }


    public static void close(String all) {
        if (all.equalsIgnoreCase("all"))
            closeAll();
    }


    // returns an Id of an unused slot for figure
    public static int getFigureId() {
        boolean figIdsRemain = false;   // remain empty slots for new figures
        for (int k = 0; k < maxNumberOfFigs; k++) {
            if (allFrames[k] == null) {
                currentFigTableIndex = k;
                figIdsRemain = true;
                currentFigTableIndex = k;
                break;
            }
        }
        if (figIdsRemain == false) { // increase the size of figure tables
            int prevFigCnt = maxNumberOfFigs;  // previous count of Figures
            increaseFigTable();  // increase the size of figure table
            currentFigTableIndex = prevFigCnt;  // return the first id from the enlarged region
        }
        return currentFigTableIndex;
    }

    // constructs a 2d figure object. Returns the figure id
    static public int figure() {
        return figure2d();
    }

    // constructs a new figure 2D object with a single subplot panel, i.e. subplot(1,1,1). Returns the figure id
    static public int figure2d() {   // initialize flags for creating a new figure at the next ploting operation
        initplots();
        new_figure = true;
        int currentFigTableIndex = getFigureId();  // returns the id of the available figure
        createSubplot2D(1, 1, 1, currentFigTableIndex);
        return currentFigTableIndex + 1;
    }

    // constructs a new figure 3D object with a single subplot panel, i.e. subplot(1,1,1). Returns the figure id
    static public int figure3d() {   // initialize flags for creating a new 3D figure at the next ploting operation
        initplots();
        new_figure = true;
        int currentFigTableIndex = getFigureId();  // returns the id of the available figure
        createSubplot3D(1, 1, 1, currentFigTableIndex);
        return currentFigTableIndex + 1;
    }

    // focus on the figure with the identifier figId
    static public PlotPanel figure(int figId) {
        initplots();
        if (figId < 1)  // assume the smallest figId when a zero or negative figId is requested
            figId = 1;
        if (figId > maxNumberOfFigs)   // increase Figure table size
            increaseFigTableSpecifiedSize(figId);

        int figMinus1 = figId - 1;   // indexes start at 0, figures numbered from 1 according to Matlab conventions
        if (allPlots[figMinus1] != null) {  // figure id exists
            currentPlot = allPlots[figMinus1][0][0];  // the requested figure panel
// if the figure id exists and is Plot3DPanel then it should be closed explicitly in order to use the figure id for 3D plots
            if (currentPlot instanceof Plot3DPanel)
                return null;
            return currentPlot;
        } else {  // we do not have the requested figure object, create it explicitly
            currentFigTableIndex = figMinus1;
            return createSubplot2D(1, 1, 1, currentFigTableIndex);
        }
    }

    static public int figure2d(int figId) {
        return figure2d();
    }

    // focus on the figure with the identifier figId
    static public PlotPanel figure3d(int figId) {
        initplots();
        if (figId < 1)  // assume the smallest figId when a zero or negative figId is requested
            figId = 1;
        int figMinus1 = figId - 1;   // indexes start at 0, figures numbered from 1 according to Matlab conventions
        if (allPlots[figMinus1] != null) {  // figure id exists
            currentPlot = allPlots[figMinus1][0][0];  // the requested figure panel
// if the figure id exists and is Plot2DPanel then it should be closed explicitly in order to use the figure id for 3D plots
            if (currentPlot instanceof Plot2DPanel)
                return null;

            return currentPlot;
        } else {  // we do not have the requested figure object, create it explicitly
            currentFigTableIndex = figMinus1;
            return createSubplot3D(1, 1, 1, currentFigTableIndex);
        }
    }

    /*
     var N=pow(2, 15).toInt;   var t=linspace(0, 10, N);
     var (noise, sig) = (vrand(N), sin(2.3*t))
     var (fftNoise, fftSig) = (fft(noise), fft(sig))
     figure(1); subplot(2,1,1); plot(t, sig, Color.BLUE, "signal"); hold("on"); plot(t, noise, Color.RED, "noise");
     subplot(2,1,2); plot(fftSig, Color.BLUE, "FFT of signal"); hold("on"); plot(fftNoise,  Color.RED, "FFT of noise")

     */
    static public boolean hold(boolean newHoldState) {
        boolean oldHoldState = holdOnMode;
        holdOnMode = newHoldState;
        return oldHoldState;
    }

    static public String hold(String newHoldState) {
        boolean oldHoldState = holdOnMode;
        boolean newHoldModeToSet = false;
        if (newHoldState.equalsIgnoreCase("on") || newHoldState.equalsIgnoreCase("1"))
            newHoldModeToSet = true;
        holdOnMode = newHoldModeToSet;
        return oldHoldState == true ? "on" : "off";
    }


    static public void title(String titleStr) {
        int plotCnt = currentFigTableIndex;
        if (plotCnt < 0)
            plotCnt = 0;
        FrameView currentView = allFrames[plotCnt];
        if (currentView != null)
            currentView.setTitle(titleStr);
    }

    // sets the title of the figure frame with identofoer figId
    static public void title(int figId, String titleStr) {
        FrameView currentView = allFrames[figId - 1];
        if (currentView != null)
            currentView.setTitle(titleStr);
    }

    /*
     t = inc(0, 0.01, 10); x = sin(0.12*t);  plot(x);
     */
// clears all the plots from the figure with figId
    static public void clf(int figId) {
        FrameView figFrame = allFrames[figId - 1];   // the frame of the figure
        if (figFrame == null) return;   // no such figure
        PlotPanel[][] plots = allPlots[figId - 1];
        int numRows = plots.length;
        int numCols = plots[0].length;
        for (int row = 0; row < numRows; row++)
            for (int col = 0; col < numCols; col++) {  // for all subplots
                PlotPanel currentPanel = plots[row][col];
                currentPanel.removeAllPlots();
            }
    }

    // clears all the plots from the reference plotPanel
    static public void clf(PlotPanel plotPanel) {
        plotPanel.removeAllPlots();
    }


    // clears all the plot with identifier plotId, from the figure with identifier figId, at subplot id: [xId, yId]
// the numbering of plot ids starts at 1 (as the fig ids)
    static public void clf(int figId, int xId, int yId, int plotId) {
        FrameView figFrame = allFrames[figId - 1];   // the frame of the figure
        if (figFrame == null) return;   // no such figure
        PlotPanel[][] plots = allPlots[figId - 1];
        int numRows = plots.length;
        int numCols = plots[0].length;
        if (numRows > yId || numCols > xId)   // requested subplot not exists
            return;
        PlotPanel currentPanel = plots[xId][yId];
        currentPanel.remove(plotId - 1);
    }


    // clears the plot with identifier plotId, from the figure with identifier figId
// the numbering of plot ids starts at 1 (as the fig ids)
// Example:
// var x=linspace(0, 2, 1000);   var y = sin(3.4*x); plot(x, y); hold("on")
// plot(x, 3*sin(8*y), Color.BLUE)
//  clf(1,1)  // clears the first plot
    static public void clf(int figId, int plotId) {
        FrameView figFrame = allFrames[figId - 1];   // the frame of the figure
        if (figFrame == null) return;   // no such figure
        PlotPanel[][] plots = allPlots[figId - 1];
        int numRows = plots.length;
        int numCols = plots[0].length;
        for (int row = 0; row < numRows; row++)
            for (int col = 0; col < numCols; col++) {  // for all subplots
                PlotPanel currentPanel = plots[row][col];
                currentPanel.removePlot(plotId - 1);
            }

    }

    // clears the plot with identifier plotId, from the figure with identifier figId, at subplot id: [xId, yId]
// the numbering of plot ids starts at 1 (as the fig ids)
    static public void clf(int figId, int xId, int yId) {
        FrameView figFrame = allFrames[figId - 1];   // the frame of the figure
        if (figFrame == null) return;   // no such figure
        PlotPanel[][] plots = allPlots[figId - 1];
        int numRows = plots.length;
        int numCols = plots[0].length;
        if (yId > numRows || xId > numCols)   // requested subplot not exists
            return;
        PlotPanel currentPanel = plots[yId - 1][xId - 1];
        currentPanel.removeAllPlots();
    }


    // creates a Matlab-like placement of multiple subplots in a figure with id figureId
    // the function overwrites (i.e. disposes) any plot object that was residing on slots allFrames[figureId], allPlots[figureId]
    // sets the currentPlot member variable to the focused subplot object
    // a figure identifier to use is explicitly requested
    static public PlotPanel createSubplot2D(int rows, int cols, int focusSubPlot, int figureId) {
        initplots();
        // create new figure
        Plot2DPanel[][] newPlot2DPanel = new Plot2DPanel[rows][cols];
        for (int ni = 0; ni < rows; ni++)    // initialize the new plot panel
            for (int mi = 0; mi < cols; mi++)
                newPlot2DPanel[ni][mi] = new Plot2DPanel();


        currentFigTableIndex = figureId;  // the requested figure id to create
        String figTitle = "Fig. " + (currentFigTableIndex + 1);
        // construct the new 2D plot frame
        Plot2DPanel newplot = new Plot2DPanel(newPlot2DPanel, PlotPanel.NORTH);    // create a new 2D figure object

        FrameView fprev = allFrames[currentFigTableIndex];  // any existing frame at that slot
        if (fprev == null) {
            currentPlotCnt2D++;
            currentPlotCnt++;
        } else {  // dispose the previous frameView
            fprev.dispose();
        }

        FrameView f = new FrameView(figTitle, newplot, currentFigTableIndex);
        f.setBackground(Color.WHITE);

        allFrames[currentFigTableIndex] = f;  // keep the currently constructed figure frame
        allPlots[currentFigTableIndex] = newPlot2DPanel;  // keep the current 2D figure array of plot panels

        new_figure = false;  // avoid creating new figure panel for subsequent calls

        // adjust the currentPlot parameter to point to the focused subplot requested by the focusSubPlot parameter
        int rowNo = (int) (focusSubPlot / cols);
        if (rowNo * cols == focusSubPlot) rowNo--;

        int colNo = (int) (focusSubPlot - rowNo * cols) - 1;
        currentPlot = newPlot2DPanel[rowNo][colNo];
        currentPlot.setLegendOrientation("SOUTH");

        return (PlotPanel) newplot;
    }


    // creates a Matlab-like placement of multiple subplots in a figure with id figureId
    // the function overwrites any plot object that was residing on slots allFrames[currentFigTableIndex], allPlots[currentFigTableIndex]
    // sets the currentPlot member variable to the focused subplot object
    // a figure identifierto use  is explicitly requested
    static public PlotPanel createSubplot3D(int rows, int cols, int focusSubPlot, int figureId) {
        initplots();
        // create new figure
        Plot3DPanel[][] newPlot3DPanel = new Plot3DPanel[rows][cols];
        for (int ni = 0; ni < rows; ni++)    // initialize the new plot panel
            for (int mi = 0; mi < cols; mi++)
                newPlot3DPanel[ni][mi] = new Plot3DPanel();


        currentFigTableIndex = figureId;  // the requested figure id to create
        String figTitle = "Fig. " + (currentFigTableIndex + 1);
        // construct the new 3D plot frame
        Plot3DPanel newplot = new Plot3DPanel(newPlot3DPanel, PlotPanel.NORTH);    // create a new 3D figure object

        FrameView fprev = allFrames[currentFigTableIndex];   // any existing frame at that slot
        if (fprev == null) {
            currentPlotCnt3D++;
            currentPlotCnt++;
        } else {  // dispose the previous frameView
            fprev.dispose();
        }

        FrameView f = new FrameView(figTitle, newplot, currentFigTableIndex);

        allFrames[currentFigTableIndex] = f;  // keep the currently constructed figure frame
        allPlots[currentFigTableIndex] = newPlot3DPanel;  // keep the current 3D figure array of plot panels

        new_figure = false;  // avoid creating new figure panel for subsequent calls

        // adjust the currentPlot parameter to point to the focused subplot requested by the focusSubPlot parameter
        int rowNo = (int) (focusSubPlot / cols);
        if (rowNo * cols == focusSubPlot) rowNo--;

        int colNo = (int) (focusSubPlot - rowNo * cols) - 1;
        currentPlot = newPlot3DPanel[rowNo][colNo];
        currentPlot.setLegendOrientation("SOUTH");

        return (PlotPanel) newplot;
    }


    // creates a Matlab-like placement of multiple subplots in the current Figure with index currentFigTableIndex
// returns the id of the figure object
    static public int Subplot2D(int rows, int cols, int focusSubPlot) {
        initplots();
        if (allFrames[currentFigTableIndex] == null) {  // create new 2D-figure panel
            // create new figure
            Plot2DPanel[][] newPlot2DPanel = new Plot2DPanel[rows][cols];
            for (int ni = 0; ni < rows; ni++)    // initialize the new plot panel
                for (int mi = 0; mi < cols; mi++)
                    newPlot2DPanel[ni][mi] = new Plot2DPanel();


            // construct the new 2D plot frame
            currentPlotCnt2D++;
            currentPlotCnt++;

            Plot2DPanel newplot = new Plot2DPanel(newPlot2DPanel, PlotPanel.NORTH);    // create a new 2D figure object
            String figTitle = "Fig. " + (currentFigTableIndex + 1);
            FrameView f = new FrameView(figTitle, newplot, currentFigTableIndex);

            allFrames[currentFigTableIndex] = f;
            allPlots[currentFigTableIndex] = newPlot2DPanel;  // keep the current 2D figure frame

            new_figure = false;  // avoid creating new figure panel for subsequent calls
            // create the objects of the current plot. These are reused at subsequent calls of subplot2D()

            int rowNo = (int) (focusSubPlot / cols);
            if (rowNo * cols == focusSubPlot) rowNo--;

            int colNo = (int) (focusSubPlot - rowNo * cols) - 1;
            currentPlot = newPlot2DPanel[rowNo][colNo];
            currentPlot.setLegendOrientation("SOUTH");

        } else {  // either focus on the requested plot if the subplot grid structure matches,
            // or otherwise create a new subplot structure
            Plot2DPanel[][] currentPlot2DPanel = (Plot2DPanel[][]) allPlots[currentFigTableIndex];
            Plot2DPanel[][] newPlot2DPanel = currentPlot2DPanel;

            int yGrid = currentPlot2DPanel.length;
            int xGrid = currentPlot2DPanel[0].length;
            if (yGrid != rows || xGrid != cols) {    // a grid of different size is requested
                newPlot2DPanel = new Plot2DPanel[rows][cols];

                // dispose the previous frameView object
                FrameView pf = allFrames[currentFigTableIndex];
                pf.dispose();

                // create new figure
                for (int ni = 0; ni < rows; ni++)    // initialize the new plot panel
                    for (int mi = 0; mi < cols; mi++)
                        newPlot2DPanel[ni][mi] = new Plot2DPanel();

                Plot2DPanel newplot = new Plot2DPanel(newPlot2DPanel, PlotPanel.NORTH);    // create a new 2D figure object
                String figTitle = "Fig. " + (currentFigTableIndex + 1);
                FrameView f = new FrameView(figTitle, newplot, currentFigTableIndex);
                f.setBackground(Color.WHITE);

                allFrames[currentFigTableIndex] = f;
                allPlots[currentFigTableIndex] = newPlot2DPanel;  // keep the current 2D figure frame
                new_figure = false;

            }
            int rowNo = (int) (focusSubPlot / cols);
            if (rowNo * cols == focusSubPlot) rowNo--;

            int colNo = (int) (focusSubPlot - rowNo * cols) - 1;
            currentPlot = newPlot2DPanel[rowNo][colNo];
            currentPlot.setLegendOrientation("SOUTH");
        }
        return currentFigTableIndex;
    }


    // creates a Matlab-like placement of multiple subplots in a figure.
    // returns the id of the figure object
    static public int Subplot3D(int rows, int cols, int focusSubPlot) {
        initplots();
        if (allFrames[currentFigTableIndex] == null) {  // create new 3D-figure panel
            // create new figure
            Plot3DPanel[][] newPlot3DPanel = new Plot3DPanel[rows][cols];
            for (int ni = 0; ni < rows; ni++)    // initialize the new plot panel
                for (int mi = 0; mi < cols; mi++)
                    newPlot3DPanel[ni][mi] = new Plot3DPanel();


            // construct the new 3D plot frame
            currentPlotCnt3D++;
            currentPlotCnt++;

            Plot3DPanel newplot = new Plot3DPanel(newPlot3DPanel, PlotPanel.NORTH);    // create a new 3D figure object
            String figTitle = "Fig. " + (currentFigTableIndex + 1);
            FrameView f = new FrameView(figTitle, newplot, currentFigTableIndex);
            f.setBackground(Color.WHITE);

            allFrames[currentFigTableIndex] = f;
            allPlots[currentFigTableIndex] = newPlot3DPanel;  // keep the current 3D figure frame

            new_figure = false;  // avoid creating new figure panel for subsequent calls
            // create the objects of the current plot. These are reused at subsequent calls of subplot3D()

            int rowNo = (int) (focusSubPlot / cols);
            if (rowNo * cols == focusSubPlot) rowNo--;

            int colNo = (int) (focusSubPlot - rowNo * cols) - 1;
            currentPlot = newPlot3DPanel[rowNo][colNo];
            currentPlot.setLegendOrientation("SOUTH");

        } else {  // either focus on the requested plot if the subplot grid structure matches,
            // or otherwise create a new subplot structure
            Plot3DPanel[][] currentPlot3DPanel = (Plot3DPanel[][]) allPlots[currentFigTableIndex];
            Plot3DPanel[][] newPlot3DPanel = currentPlot3DPanel;

            int yGrid = currentPlot3DPanel.length;
            int xGrid = currentPlot3DPanel[0].length;
            if (yGrid != rows || xGrid != cols) {    // a grid of different size is requested
                newPlot3DPanel = new Plot3DPanel[rows][cols];

                // dispose the previous frameView object
                FrameView pf = allFrames[currentFigTableIndex];
                pf.dispose();

                // create new figure
                for (int ni = 0; ni < rows; ni++)    // initialize the new plot panel
                    for (int mi = 0; mi < cols; mi++)
                        newPlot3DPanel[ni][mi] = new Plot3DPanel();

                Plot3DPanel newplot = new Plot3DPanel(newPlot3DPanel, PlotPanel.NORTH);    // create a new 3D figure object
                String figTitle = "Fig. " + (currentFigTableIndex + 1);
                FrameView f = new FrameView(figTitle, newplot, currentFigTableIndex);

                allFrames[currentFigTableIndex] = f;
                allPlots[currentFigTableIndex] = newPlot3DPanel;  // keep the current 3D figure frame
                new_figure = false;

            }
            int rowNo = (int) (focusSubPlot / cols);
            if (rowNo * cols == focusSubPlot) rowNo--;

            int colNo = (int) (focusSubPlot - rowNo * cols) - 1;
            currentPlot = newPlot3DPanel[rowNo][colNo];
            currentPlot.setLegendOrientation("SOUTH");
        }
        return currentFigTableIndex;
    }


    // overloaded call
    static public void subplot(int rows, int cols, int focusSubPlot) {
        Subplot2D(rows, cols, focusSubPlot);
    }

    // overloaded call
    static public void subplot2D(int rows, int cols, int focusSubPlot) {
        Subplot2D(rows, cols, focusSubPlot);
    }

    // overloaded call
    static public void subplot2d(int rows, int cols, int focusSubPlot) {
        Subplot2D(rows, cols, focusSubPlot);
    }


    // overloaded call
    static public void subplot3D(int rows, int cols, int focusSubPlot) {
        Subplot3D(rows, cols, focusSubPlot);
    }

    // overloaded call
    static public void subplot3d(int rows, int cols, int focusSubPlot) {
        Subplot3D(rows, cols, focusSubPlot);
    }


    // closes the current  figure
    static public int close() {
        // find next usable plot
        int currentFigSlotExamined = currentPlotCnt - 1;

        FrameView closingFrameView = allFrames[currentFigTableIndex];  // the frame view of the current figure
        //  we have a current view,  close it, and refocus on another figure
        if (closingFrameView != null) {
            closingFrameView.dispose();
            if (allPlots[currentFigTableIndex][0][0] instanceof Plot2DPanel)
                currentPlotCnt2D--;
            else
                currentPlotCnt3D--;

            allPlots[currentFigTableIndex] = null;
            allFrames[currentFigTableIndex] = null;
            currentPlotCnt--;   // one less plot

            boolean atLeastOnefigureRemains = false;
            for (int k = 0; k < maxNumberOfFigs; k++) {  // scan all possible slots
                if (allFrames[currentFigSlotExamined] != null) {   // we have found an unused slot for focusing figure
                    atLeastOnefigureRemains = true;
                    break;
                }
                if (currentFigSlotExamined < 0)    // cycle from the highest position
                    currentFigSlotExamined = maxNumberOfFigs - 1;
            }   // scan all possible slots

            if (atLeastOnefigureRemains == true) {   // focus on the detected "survived" figure object
                PlotPanel[][] currentPlotPanel = (PlotPanel[][]) allPlots[currentFigSlotExamined];
                currentPlot = currentPlotPanel[0][0];    // make the first subplot of the figure object as the current one
                currentPlot.setLegendOrientation("SOUTH");
            }
        }         //  we have a current view,  close it, and refocus on another figure

        currentFigTableIndex = currentFigSlotExamined;
        return currentFigTableIndex + 1;
    }

    // close an explicitly requested figure id
    // it focuses automatically on the previous figure object which it returns
    static public int close(int figId) {
        int currentFigSlotExamined = currentPlotCnt - 1;

        if (allFrames != null) {   // plot system inited
            int figMinusOne = figId - 1;
            FrameView closingFrameView = allFrames[figMinusOne];
            // find next usable plot

            int len1 = allPlots[figMinusOne][0].length;
            int len2 = allPlots[figMinusOne].length;
            if (closingFrameView != null) {   // figure frame exists
                closingFrameView.dispose();
                for (int i = 0; i < len2; i++)
                    for (int j = 0; j < len1; j++)
                        allPlots[figMinusOne][i][j] = null;

                allPlots[figMinusOne] = null;  // TODOSterg: handle closing properly
                allFrames[figMinusOne] = null;
                currentPlotCnt--;   // one less plot

                boolean atLeastOnefigureRemains = false;
                for (int k = 0; k < maxNumberOfFigs; k++) {  // scan all possible slots
                    if (allFrames[currentFigSlotExamined] != null) {   // we have found an unused slot for focusing figure
                        atLeastOnefigureRemains = true;
                        break;
                    }
                    currentFigSlotExamined--;  // check lowest figure id
                    currentFigTableIndex = currentFigSlotExamined;
                    if (currentFigSlotExamined < 0)    // cycle from the highest position
                    {
                        currentFigTableIndex = currentFigSlotExamined = maxNumberOfFigs - 1;
                    }
                }   // scan all possible slots

                if (atLeastOnefigureRemains == true) {   // focus on the detected "survived" figure object
                    PlotPanel[][] currentPlotPanel = (PlotPanel[][]) allPlots[currentFigSlotExamined];
                    currentPlot = currentPlotPanel[0][0];    // make the first subplot of the figure object as the current one
                    currentPlot.setLegendOrientation("SOUTH");
                } else
                    new_figure = true;   // if figures do not remain create one on next plot
            }   // close currentFigTableIndex
        }
        return currentFigSlotExamined + 1;   // return index of the closed figure
    }

    static public void setColor(int red, int green, int blue) {
        Color newColor = new Color(red, green, blue);
        currentPlot.setForeground(newColor);
        currentPlot.setBackground(newColor);
        currentPlot.repaint();
    }


    // var x = vrand(500); plot(x);  markX(100, 200, new Font("Arial", Font.BOLD, 30));  markX(10.0, 0.3)
    static public void xlabel(String xLabelStr) {
        currentPlot.setAxisLabel(scalaSci.math.plot.PlotPanel.xAxisId, xLabelStr);
    }

    static public void ylabel(String yLabelStr) {
        currentPlot.setAxisLabel(scalaSci.math.plot.PlotPanel.yAxisId, yLabelStr);
    }

    static public void zlabel(String zLabelStr) {
        if (currentPlot instanceof Plot3DPanel)
            currentPlot.setAxisLabel(scalaSci.math.plot.PlotPanel.zAxisId, zLabelStr);
    }


    // set the color for LaTex text and return the previous setting
    public static Color setLatexColor(Color col) {
        Color prevColor = PlotGlobals.latexColor;
        PlotGlobals.latexColor = col;
        return prevColor;
    }

    public static void latexLabel(String latex) {
        texLabel txl = new scalaSci.math.plot.plotObjects.texLabel(latex, 1, 1);
        currentPlot.addPlotable(txl);
    }

    public static void latexLabel(String latex, int coordx, int coordy) {
        texLabel txl = new scalaSci.math.plot.plotObjects.texLabel(latex, coordx, coordy);
        currentPlot.addPlotable(txl);
    }

    public static void latexLabel(String latex, int size, int coordx, int coordy) {
        texLabel txl = new scalaSci.math.plot.plotObjects.texLabel(latex, size, coordx, coordy);
        currentPlot.addPlotable(txl);
    }

    public static void latexLabel(String latex, double coordx, double coordy) {
        texLabel txl = new scalaSci.math.plot.plotObjects.texLabel(latex, coordx, coordy);
        currentPlot.addPlotable(txl);
    }

    public static void latexLabel(String latex, int size, double coordx, double coordy) {
        texLabel txl = new scalaSci.math.plot.plotObjects.texLabel(latex, size, coordx, coordy);
        currentPlot.addPlotable(txl);
    }


    public static void latexLabel3d(String latex, double nw1, double nw2, double nw3, double se1, double se2, double se3, double sw1, double sw2, double sw3) {
        LatexImage tximg = new scalaSci.math.plot.plotObjects.LatexImage(latex, nw1, nw2, nw3, se1, se2, se3, sw1, sw2, sw3);
        currentPlot.addPlotable(tximg);
    }

    public static void latexLabel3d(String latex, int xcoord, int ycoord) {
        LatexImage tximg = new scalaSci.math.plot.plotObjects.LatexImage(latex, xcoord, ycoord);
        currentPlot.addPlotable(tximg);
    }

    public static void latexRender(String latex) {
        TeXFormula tf = new TeXFormula(latex);
        TeXIcon icontf = tf.createTeXIcon(TeXConstants.STYLE_DISPLAY, 20);
        icontf.setInsets(new Insets(5, 5, 5, 5));

        OutputTextPane otp = new OutputTextPane();
        otp.addIcon(icontf, 0, true);
        JPanel jp = new JPanel();
        jp.add(otp);

        JFrame tstF = new JFrame("test");
        tstF.add(jp);
        tstF.setSize(200, 200);
        tstF.setVisible(true);


    }    
        
        /*
         var t=inc(0, 0.01, 10); var x = sin(3.4*t); var y = sin(3.4*t);  plot(t,x, y)
         */
        
/*
         var t=inc(0, 0.01, 10); var x = sin(3.4*t); plot(t,x)
         var latex = "\\int_{i=1}^{N} k_i"
         latexLabel(latex, 50, 100, 100)
         */
}


