
package external;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import org.das2.datum.DatumRange;
import org.das2.graph.DasColorBar;
import org.das2.graph.DefaultPlotSymbol;
import org.das2.graph.PlotSymbol;
import org.das2.graph.PsymConnector;
import org.das2.util.ClassMap;
import org.python.core.Py;
import org.python.core.PyInteger;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.autoplot.RenderType;
import org.autoplot.ScriptContext2023;
import org.autoplot.datasource.DataSourceUtil;
import org.autoplot.dom.Application;
import org.autoplot.dom.CanvasUtil;
import org.autoplot.dom.Column;
import org.autoplot.dom.DataSourceFilter;
import org.autoplot.dom.DomNode;
import org.autoplot.dom.DomUtil;
import org.autoplot.dom.Plot;
import org.autoplot.dom.PlotElement;
import org.autoplot.dom.Row;
import org.das2.qds.QDataSet;
import org.autoplot.jythonsupport.JythonOps;
import org.autoplot.jythonsupport.PyQDataSetAdapter;
import org.das2.datum.DatumRangeUtil;
import org.das2.graph.FillStyle;
import org.das2.graph.Renderer;
import org.das2.graph.SeriesRenderer;
import org.das2.qds.DataSetUtil;
import org.das2.qds.ops.Ops;
import org.das2.util.monitor.NullProgressMonitor;
import org.python.core.PyJavaInstance;
import org.python.core.PyList;
import org.python.core.PyMethod;

/**
 * new implementation of the plot command allows for keywords in the
 * Jython environment.
 *<blockquote><pre><small>{@code
 * plot( 0, ripples(20) )
 * plot( 1, ripples(20), color=Color.BLUE )
 * plot( 2, ripples(20), renderType='series>color=blue' )
 *}</small></pre></blockquote>
 * @see https://autoplot.org/help.plotCommand
 * @author jbf
 */
public class PlotCommand extends PyObject {

    private Application dom;
    private ScriptContext2023.State state;
    
    public PlotCommand( Application dom ) {
        this.dom= dom;
        this.state= dom.getController().getScriptContextState();
    }
    
    private static final Logger logger= org.das2.util.LoggerManager.getLogger("autoplot");
    
    public static final PyString __doc__ =
        new PyString("<html><H2>plot([index],x,y,z,[named parameters])</H2>"
            + "plot (or plotx) plots the data or URI for data on the canvas.\n"
            + "See https://autoplot.org/help.plotCommand<br>\n"
            + "<br><b>named parameters:</b>\n"
            + "<table>"
            + "<tr><td>xlog ylog zlog </td><td>explicitly set this axis to log (or linear when set equal to 0.).</td></tr>\n"
            + " <tr><td> [xyz]title </td><td>set the label for the axis.</td></tr>\n"
            + " <tr><td> index       </td><td>plot index\n</td></tr>"
            + " <tr><td> title       </td><td>title for the plot\n</td></tr>"
            + " <tr><td> renderType  </td><td> explcitly set the render type, to scatter, series, nnSpectrogram, digital, etc\n</td></tr>"
            + " <tr><td> color      </td><td> the line colors.\n</td></tr>"
            + " <tr><td> fillColor   </td><td>the color when filling volumes.\n</td></tr>"
            + " <tr><td> colorTable  </td><td>the color table to use, like white_blue_black or black_red.\n</td></tr>"
            + " <tr><td> symbolSize     </td><td>set the point (pixel) size\n</td></tr>"
            + " <tr><td> symbolFill     </td><td>none, outline, or solid (solid is default)\n</td></tr>"
            + " <tr><td> lineWidth   </td><td>deprecated--the line thickness in points (pixels)\n</td></tr>"
            + " <tr><td> lineThick   </td><td>the line thickness in points (pixels)\n</td></tr>"
            + " <tr><td> lineStyle   </td><td>the line style, one of solid,none,dotfine,dashfine</td></tr>"
            + " <tr><td> symbol      </td><td>the symbol, e.g. dots triangles cross\n</td></tr>"
            + " <tr><td> isotropic   </td><td>constrain the ratio between the x and y axes.\n</td></tr>"
            + " <tr><td> legendLabel </td><td>add label to the legend</td></tr>"
            + " <tr><td> title   </td><td>title for the plot\n</td></tr>"
            + " <tr><td> xpos    </td><td>override horizontal position of plot, eg. '50%+1em,100%-2em'\n</td>"
            + " <tr><td> ypos    </td><td>override vertical position of plot, eg. '0%+1em,25%-2em', 0 is top\n</td>"
            + " <tr><td> [xy]drawTickLabels</td><td>False turns off the x or y tick labels for the plot\n</td>"
            + " <tr><td> [xy]tickValues</td><td>explicitly control the tick locations.</td>"
            + " <tr><td> [xyz]autoRangeHints</td><td>hints to the autorange, see https://autoplot.org/AxisAutoRangeHints\n</td>"
            + " <tr><td> renderer</td><td>add custom renderer, a class extending org.das2.graph.Renderer, see https://autoplot.org/CustomRenderers</td>"
            + " <tr><td> rightAxisOf</td><td>specify a plot where a new plot with a new yaxis.</td>"
            + " <tr><td> topAxisOf</td><td>specify a plot where a new plot with a new xaxis above.</td>"
            + " <tr><td> overplotOf</td><td>a plot or plot element with which this should share axes.  Note something should reset the plot!</td>"
            + " <tr><td> reset=F</td><td>suppress autoranging, default is True"
            + "</table></html>");

    public static final PyString __completions__;
    
    static {
        String text = new BufferedReader(
            new InputStreamReader( PlotCommand.class.getResourceAsStream("PlotCommand.json"), StandardCharsets.UTF_8) )
            .lines().collect(Collectors.joining("\n"));
        __completions__= new PyString( text );
    }
        
    private static QDataSet coerceIt( PyObject arg0 ) {
        Object o = arg0.__tojava__(QDataSet.class);
        if (o == null || o == Py.NoConversion) {
            return JythonOps.dataset(arg0);
        } else {
            QDataSet ds = (QDataSet) o;
            if (ds.rank() == 0) {
                // QDataSet library handles coerce logic.
                return ds;
            } else {
                return ds;
            }
        }
    }
    
    private static boolean booleanValue( PyObject arg0 ) {
        if ( arg0.isNumberType() ) {
            return arg0.__nonzero__();
        } else {
            String s= String.valueOf(arg0);
            return s.equals("True") || s.equals("T") || s.equals("1");
        }
    }
    
    /**
     * implement the python call.
     * @param args the "rightmost" elements are the keyword values.
     * @param keywords the names for the keywords.
     * @return Py.None
     */
    @Override
    public PyObject __call__(PyObject[] args, String[] keywords) {

        PyObject False= Py.newBoolean(false);
        PyObject True= Py.newBoolean(true);

        FunctionSupport fs= new FunctionSupport( "plot", 
            new String[] { "pos", "x", "y", "z",
            "xtitle", "xrange",
            "ytitle", "yrange",
            "ztitle", "zrange",
            "xscale", "yscale", 
            "xlog", "ylog", "zlog",
            "title",
            "renderType",
            "color", "fillColor", "colorTable",
            "symbolSize","lineWidth","lineThick","lineStyle",
            "symsize","linewidth","linethick","linestyle",
            "legendLabel",
            "symbol", "symbolFill",
            "isotropic", "xpos", "ypos",
            "xdrawTickLabels", "ydrawTickLabels",
            "xautoRangeHints", "yautoRangeHints", "zautoRangeHints",
            "xtickValues", "ytickValues", "ztickValues",
            "renderer", "rightAxisOf", "topAxisOf", "overplotOf",
            "index", "reset"
        },
        new PyObject[] { Py.None, Py.None, Py.None, Py.None,
            Py.None, Py.None,
            Py.None, Py.None,
            Py.None, Py.None,
            Py.None, Py.None,
            False, False, False,
            Py.None,
            Py.None,
            Py.None,Py.None,Py.None,
            Py.None,Py.None,Py.None,Py.None,
            Py.None,Py.None,Py.None,Py.None,
            Py.None,
            Py.None, Py.None, 
            Py.None, Py.None, Py.None,
            Py.None, Py.None,
            Py.None, Py.None, Py.None,
            Py.None, Py.None, Py.None,
            Py.None, Py.None, Py.None, Py.None,
            Py.None, True,
        } );
        
        fs.args( args, keywords );
        
        int nparm= args.length - keywords.length;

        if ( nparm==0 ) {
            logger.warning("args.length=0");
            return Py.None;
        }

        int iplot=0;
        int nargs= nparm;

        boolean reset=true; // reset axis settings
        
        // If the first (zeroth) argument is an int, than this is the data source where the value should be inserted.  Additional
        // data sources and plots will be added until there are enough.
        // this is an alias for the index argument.
        PyObject po0= args[0];
        if ( po0 instanceof PyInteger ) {
            iplot= ((PyInteger)po0).getValue();
            PyObject[] newArgs= new PyObject[args.length-1];
            for ( int i=0; i<args.length-1; i++ ) {
                newArgs[i]= args[i+1];
            }
            args= newArgs;
            nargs= nargs-1;
            nparm= args.length - keywords.length;
            po0= args[0];
        }
        
        Row row=null; // these will be set when xpos and ypos are used.
        Column column=null;
        Plot plot=null; // use this plot
        
        Application dom;
        ScriptContext2023 scriptContext= this.dom.getController().getScriptContext();
        
        dom= scriptContext.getDocumentModel();
        String renderType=null;
        for ( int i=0; i<keywords.length; i++  ) {
            if ( keywords[i].equals("renderType" ) ) {
                renderType= args[i+nparm].toString();
            } else if ( keywords[i].equals("column") || keywords[i].equals("xpos")) {
                String spec= args[i+nparm].toString();
                if ( Ops.isSafeName(spec) ) {
                    DomNode n= DomUtil.getElementById( dom, spec );
                    if ( n instanceof Column ) {
                        column= (Column)n;
                    } else {
                        throw new IllegalArgumentException("column named parameter is not the name of a column");
                    }
                } else if ( args[i+nparm] instanceof PyString ) {
                    column= dom.getCanvases(0).getController().maybeAddColumn( spec );
                } else {
                    try {
                        column= (Column)args[i+nparm].__tojava__(Column.class);
                    } catch (Exception e ) {
                        String columnId=((Plot)args[i+nparm].__tojava__(Plot.class)).getColumnId();
                        DomNode n= DomUtil.getElementById( dom, columnId );
                        column= (Column)n;
                    }
                }
                if ( row==null ) row=dom.getCanvases(0).getMarginRow();
            } else if ( keywords[i].equals("row") || keywords[i].equals("ypos")) {
                String spec= args[i+nparm].toString();
                if ( Ops.isSafeName(spec) ) {
                    DomNode n= DomUtil.getElementById( dom, spec );
                    if ( n instanceof Row ) {
                        row= (Row)n;                        
                    } else {
                        throw new IllegalArgumentException("row named parameter is not the name of a row");
                    }
                } else if ( args[i+nparm] instanceof PyString ) {
                    row= dom.getCanvases(0).getController().maybeAddRow( spec );                 
                } else {
                    try {
                        row= (Row)args[i+nparm].__tojava__(Row.class);
                    } catch (Exception e ) {
                        String rowId=((Plot)args[i+nparm].__tojava__(Plot.class)).getRowId();
                        DomNode n= DomUtil.getElementById( dom, rowId );
                        row= (Row)n;
                    }
                }
                if ( column==null ) column=dom.getCanvases(0).getMarginColumn();
            } else if ( keywords[i].equals("rightAxisOf") || keywords[i].equals("topAxisOf") || keywords[i].equals("overplotOf") ) {
                String spec= args[i+nparm].toString();
                Plot p=null;
                if ( Ops.isSafeName(spec) ) {
                    DomNode n= DomUtil.getElementById( dom, spec );
                    if ( n instanceof PlotElement ) {
                        n= DomUtil.getElementById( dom, ((PlotElement)n).getPlotId() );
                    }
                    p= (Plot)n;
                } else {
                    try {
                        p = (Plot)args[i+nparm].__tojava__(Plot.class);
                    } catch ( Exception e ) {
                        PlotElement pe= ((PlotElement)args[i+nparm].__tojava__(PlotElement.class));
                        if ( pe!=null ) {
                            p= (Plot) DomUtil.getElementById( dom, ((PlotElement)pe).getPlotId() );
                        }                        
                    }
                }
                if ( p==null ) {
                    throw new IllegalArgumentException("unable to identify plot");
                }
                Plot underPlot=null;
                row= (Row)DomUtil.getElementById(dom,p.getRowId());
                column= (Column)DomUtil.getElementById(dom,p.getColumnId());
                if ( keywords[i].equals("overplotOf") ) {
                    plot= p;
                    iplot= dom.getDataSourceFilters().length;
                } else {
                    for ( Plot p1: dom.getPlots() ) {
                        if ( p1.getRowId().equals(row.getId()) && p1.getColumnId().equals(column.getId()) ) {
                            if ( p1.getYaxis().isOpposite() ) {
                                plot= p1;
                            } else {
                                underPlot= p1;
                            }
                        }
                    }
                }
                
                if ( plot==null ) {
                    plot= dom.getController().addPlot( row, column );
                    if ( keywords[i].equals("rightAxisOf") ) {
                        plot.getYaxis().setOpposite(true);
                        dom.getController().bind( underPlot.getXaxis(), "range", plot.getXaxis(), "range"  );
                        plot.getXaxis().setVisible(false);
                    } else if ( keywords[i].equals("topAxisOf") ) {
                        plot.getXaxis().setOpposite(true);
                        dom.getController().bind( underPlot.getYaxis(), "range", plot.getYaxis(), "range"  );
                        plot.getYaxis().setVisible(false);
                    }
                }
                
            } else if ( keywords[i].equals("index") ) {
                int sindex= Integer.parseInt( args[i+nparm].toString() );
                iplot= sindex;
            } else if ( keywords[i].equals("reset") ) {
                reset= args[i+nparm].equals(True);
            } else if ( keywords[i].equals("renderer") ) {
                renderType="internal";
            }
        }
        
        if ( row!=null ) {
            assert column!=null;
            Plot p= plot;
            if ( p==null ) {
                for ( Plot p1: dom.getPlots() ) {
                    if ( p1.getRowId().equals(row.getId()) && p1.getColumnId().equals(column.getId()) ) {
                        p=p1;
                    }
                }
            }
            if ( p==null ) p= dom.getController().addPlot( row, column );
            while ( dom.getDataSourceFilters().length <= iplot ) {
                dom.getController().addDataSourceFilter();
            }
            PlotElement pe= dom.getController().addPlotElement( p, dom.getDataSourceFilters(iplot) );
            List<PlotElement> pes= dom.getController().getPlotElementsFor( dom.getDataSourceFilters(iplot) );
            pes.remove(pe);
            for ( PlotElement rm: pes ) {
                Plot prm= dom.getController().getPlotFor(rm);
                dom.getController().deletePlotElement(rm);
                if ( dom.getController().getPlotElementsFor(prm).isEmpty() ) {
                    dom.getController().deletePlot(prm);
                }
            }
        }

        QDataSet[] qargs= new QDataSet[nargs];

        if ( nargs==1 && po0 instanceof PyString ) {
            scriptContext.plot( iplot, ((PyString) po0).toString());
        } else if ( nargs==2 && po0 instanceof PyString && args[1] instanceof PyString ) {
            DatumRange drtr= DatumRangeUtil.parseTimeRangeValid(((PyString)args[1]).toString() );
            try{
                String uri= DataSourceUtil.setTimeRange( ((PyString) po0).toString(), drtr, new NullProgressMonitor() );
                scriptContext.plot( iplot, uri );          
            } catch ( IOException | URISyntaxException | ParseException ex ) {
                throw new RuntimeException(ex);
            }
            
        } else {
            for ( int i=0; i<nargs; i++ ) {
                QDataSet ds= coerceIt(args[i]);
                qargs[i]= ds;
            }

            if ( nargs==1 ) {  // x
                scriptContext.plot( iplot, null, null, qargs[0], renderType, reset );
            } else if ( nargs==2 ) {  // x, y
                scriptContext.plot( iplot, null, qargs[0], qargs[1], renderType, reset );
            } else if ( nargs==3 ) {  // x, y, z
                scriptContext.plot( iplot, null, qargs[0], qargs[1], qargs[2], renderType, reset );
            }

        }
        // we're done plotting, now for the arguments 

        // we can't use this up above because ScriptContext.plot hangs because we've locked the application and it can't wait until idle.
        // NOTE THERE'S A BUG HERE, for a moment the application is idle and a waiting process could proceed.
        dom.getController().registerPendingChange( this, this );  
        dom.getController().performingChange(this,this);

        final Plot fplot;
        final PlotElement fplotElement;
        
        try {
            int chNum= iplot;

            while ( dom.getDataSourceFilters().length <= chNum ) {
                Plot p= CanvasUtil.getMostBottomPlot(dom.getController().getCanvas());
                dom.getController().setPlot(p);
                dom.getController().addPlotElement( null, null  );
            }
            DataSourceFilter dsf= dom.getDataSourceFilters(chNum);
            List<PlotElement> elements= dom.getController().getPlotElementsFor( dsf );
            if ( elements.isEmpty() ) {
                logger.log(Level.WARNING, "no elements found for data at index={0}", iplot);
                return Py.None;
            }
            PlotElement element= elements.get(0);
            plot= dom.getController().getPlotFor(element);
            plot.setIsotropic(false);

            for ( int i=nparm; i<args.length; i++ ) { //HERE nargs
                String kw= keywords[i-nparm];
                PyObject val= args[i];

                String sval= (String) val.__str__().__tojava__(String.class);
                if ( kw.equals("ytitle") ) {
                    plot.getYaxis().setLabel( sval);
                } else if ( kw.equals("yrange") ) {
                    DatumRange newRange= JythonOps.datumRange(val,plot.getYaxis().getRange().getUnits());
                    if ( plot.getYaxis().isLog() && newRange.min().doubleValue(newRange.getUnits())<0 ) {
                        plot.getYaxis().setLog(false);
                    }
                    plot.getYaxis().setRange( newRange );
                } else if ( kw.equals("ylog") ) {
                    plot.getYaxis().setLog( booleanValue( val ) );
                } else if ( kw.equals("yscale") ) {
                    plot.getYaxis().setScale(JythonOps.datum(val));
                } else if ( kw.equals("xtitle") ) {
                    plot.getXaxis().setLabel( sval);
                } else if ( kw.equals("xrange") ) {
                    DatumRange newRange= JythonOps.datumRange( val,plot.getXaxis().getRange().getUnits() );
                    if ( plot.getXaxis().isLog() && newRange.min().doubleValue(newRange.getUnits())<0 ) {
                        plot.getXaxis().setLog(false);
                    }
                    plot.getXaxis().setRange( newRange );
                } else if ( kw.equals("xlog") ) {
                    plot.getXaxis().setLog( booleanValue(val) );
                } else if ( kw.equals("xscale") ) {
                    plot.getXaxis().setScale(JythonOps.datum(val));
                } else if ( kw.equals("ztitle") ) {
                    plot.getZaxis().setLabel( sval);
                } else if ( kw.equals("zrange") ) {
                    DatumRange newRange= JythonOps.datumRange(val,plot.getZaxis().getRange().getUnits());
                    if ( plot.getZaxis().isLog() && newRange.min().doubleValue(newRange.getUnits())<0 ) {
                        plot.getZaxis().setLog(false);
                    }
                    plot.getZaxis().setRange( newRange );
                } else if ( kw.equals("zlog") ) {
                    plot.getZaxis().setLog( booleanValue(val) );
                } else if ( kw.equals("color" ) ) {
                    Color c= JythonOps.color(val);
                    element.getStyle().setColor( c );
                } else if ( kw.equals("fillColor" ) ) { // because you can specify renderType=stairSteps, we need fillColor.
                    Color c;
                    c= JythonOps.color(val);
                    element.getStyle().setFillColor( c );
                } else if ( kw.equals("colorTable" ) ) { 
                    if ( val.__tojava__(DasColorBar.Type.class) == Py.NoConversion) {
                        DasColorBar.Type t= org.das2.graph.DasColorBar.Type.parse(sval);
                        element.getStyle().setColortable(t);
                    } else {
                        DasColorBar.Type t = (DasColorBar.Type) val.__tojava__(DasColorBar.Type.class);
                        element.getStyle().setColortable( t );
                    }
                } else if ( kw.equals("title") ) {
                    plot.setTitle(sval);
                } else if ( kw.equals("symsize") || kw.equals("symbolSize") ) {
                    element.getStyle().setSymbolSize( Double.valueOf(sval) );
                } else if ( kw.equals("symbolFill") ) {
                    if ( element.getController().getRenderer() instanceof SeriesRenderer ) {
                        FillStyle sfs= (FillStyle) ClassMap.getEnumElement( FillStyle.class, sval ) ;
                        ((SeriesRenderer) element.getController().getRenderer() ).setFillStyle(sfs);
                    }
                } else if ( kw.equals("linewidth" ) || kw.equals("lineWidth") ) {
                    element.getStyle().setLineWidth( Double.valueOf(sval) );
                } else if ( kw.equals("linethick" ) || kw.equals("lineThick") ) {
                    element.getStyle().setLineWidth( Double.valueOf(sval) );
                } else if ( kw.equals("linestyle") || kw.equals("lineStyle") ) {
                    PsymConnector p= (PsymConnector) ClassMap.getEnumElement( PsymConnector.class, sval );
                    element.getStyle().setSymbolConnector( p );
                } else if ( kw.equals("symbol") ) {
                    PlotSymbol p= (PlotSymbol) ClassMap.getEnumElement( DefaultPlotSymbol.class, sval );
                    if ( p!=null ) {
                        element.getStyle().setPlotSymbol( p );
                    } else {
                        throw new IllegalArgumentException("unable to identify symbol: "+sval);
                    }
                } else if ( kw.equals("renderType") ) {
                    String srenderType= sval;
                    String renderControl;
                    if ( srenderType!=null && srenderType.trim().length()>0 ) {
                        int ii= srenderType.indexOf('>');
                        if ( ii==-1 ) {
                            renderControl= "";
                        } else {
                            renderControl= srenderType.substring(ii+1);
                            srenderType= srenderType.substring(0,ii);
                        }                    
                        RenderType rt= RenderType.valueOf(srenderType);
                        element.setRenderType(rt);
                        element.setRenderControl(renderControl);
                    }
                } else if ( kw.equals("renderer") ) {
                    Renderer r;
                    if (val.__tojava__(Renderer.class) != Py.NoConversion) {
                        Renderer oldRenderer= element.getController().getRenderer();
                        String control= oldRenderer.getControl();
                        r = (Renderer) val.__tojava__(Renderer.class);
                        QDataSet ds1=null;
                        switch (nargs) {
                            case 1:
                                ds1= qargs[0];
                                break;
                            case 2:
                                ds1= Ops.link( qargs[0], qargs[1] );
                                break;
                            case 3:
                                ds1= Ops.link( qargs[0], qargs[1], qargs[2] );
                                break;
                            default:
                                break;
                        }
                        PyObject doAuto= val.__findattr__( "doAutorange" );
                        if ( doAuto==null ) {
                            doAuto= val.__findattr__( "autorange" );
                        }
                        if ( doAuto!=null && doAuto!=Py.None && ds1!=null ) {
                            PyObject range= ((PyMethod)doAuto).__call__(new PyQDataSetAdapter().adapt(ds1));
                            QDataSet rangeds= (QDataSet) range.__tojava__(QDataSet.class);
                            plot.getXaxis().setRange( DataSetUtil.asDatumRange(rangeds.slice(0) ) );
                            if ( rangeds.length()>1 ) plot.getYaxis().setRange( DataSetUtil.asDatumRange(rangeds.slice(1) ) );
                            if ( rangeds.length()>2 ) plot.getZaxis().setRange( DataSetUtil.asDatumRange(rangeds.slice(2) ) );
                        }
                        plot.getController().getDasPlot().removeRenderer(oldRenderer);
                        plot.getController().getDasPlot().addRenderer(r);
                        r.setDataSet(ds1);
                        r.setColorBar((DasColorBar) plot.getZaxis().getController().getDasAxis());
                        element.getController().setRenderer(r);
                        element.setRenderType(RenderType.internal);
                        r.setControl(control);
                    } else {
                        logger.warning("no conversion for renderer");
                    }
                } else if ( kw.equals("legendLabel" ) ) {
                    if ( !sval.equals("") ) {
                        element.setLegendLabel(sval);
                        element.setDisplayLegend(true);
                    }
                } else if ( kw.equals("isotropic" ) ) {
                    plot.setIsotropic(true);
                } else if ( kw.equals("xdrawTickLabels") ) {
                    plot.getXaxis().setDrawTickLabels( booleanValue(val) );
                } else if ( kw.equals("ydrawTickLabels") ) {
                    plot.getYaxis().setDrawTickLabels( booleanValue(val) );
                } else if ( kw.equals("xtickValues") ) {
                    plot.getXaxis().setTickValues(sval);
                } else if ( kw.equals("ytickValues") ) {
                    plot.getYaxis().setTickValues(sval);
                } else if ( kw.equals("ztickValues") ) {
                    plot.getZaxis().setTickValues(sval);
                } else if ( kw.equals("xautoRangeHints") ) {
                    plot.getXaxis().setAutoRangeHints( sval );
                } else if ( kw.equals("yautoRangeHints") ) {
                    plot.getYaxis().setAutoRangeHints( sval );
                } else if ( kw.equals("zautoRangeHints") ) {
                    plot.getZaxis().setAutoRangeHints( sval );
                }
            }
            
            fplot= plot;
            fplotElement= element;

        } finally {
            dom.getController().changePerformed(this,this);
        }

        return new PyList( new PyObject[] { new PyJavaInstance(fplot), new PyJavaInstance(fplotElement)  } );
    }

}
