/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package eric.JSprogram;

import eric.*;
import eric.GUI.palette.PaletteManager;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import rene.gui.Global;
import rene.util.FileName;
import rene.zirkel.ZirkelCanvas;
import rene.zirkel.construction.Construction;
import rene.zirkel.expression.Expression;
import rene.zirkel.objects.AreaObject;
import rene.zirkel.objects.ConstructionObject;
import rene.zirkel.objects.ExpressionObject;
import rene.zirkel.objects.FunctionObject;
import rene.zirkel.objects.PointObject;
import rene.zirkel.objects.SegmentObject;

/**
 *
 * @author erichake
 */
public class JSRun {

    static Context cx = null;
    static ScriptableObject scope = null;
    static Construction C = null;
    static ZirkelCanvas ZC = null;
    static String CTOOL = "";
    static String CBACKUP = "";
    static String FILENAME = "";
    static ArrayList<String> BACKUPS = new ArrayList<String>();
//    static String[] cmdnames={"P","M"};
    static String[] allnames = {"cm", "Input", "Prompt", "Print", "Println", "Shownames", "Hidenames", "Hide", "Show",
        "Point", "Intersection", "Intersection2", "MidPoint", "Symmetry", "Line", "Segment", "Circle", "Parallel",
        "Perpendicular", "FixedCircle", "Move", "X", "Y", "GetExpressionValue", "Ray", "Angle", "Polygon", "Quadric",
        "CartesianFunction", "ParametricFunction", "SetColor", "SetThickness", "SetFixed", "Vector", "SetRGBColor",
        "SetShowName", "SetShowValue", "SetFilled", "SetPartial", "Expression", "SetAlias", "SetMagneticObjects",
        "AddMagneticObject", "SetMagneticRay", "SetPointType", "InteractiveInput", "FixedSegment", "SetHide", "Pause",
        "Delete", "SetExpressionValue", "Reflection", "Translation", "PerpendicularBisector", "AngleBisector", "Circle3pts",
        "Arc3pts", "FixedAngle", "Circle3", "ExecuteMacro", "Alert", "Conditional", "Layer"};
    static JSOuputConsole console = null;
    static public boolean busy = false;
    static public Thread JST = null;
    static JSConsole JSC = null;
//    static public ConstructionObject O=null;
//    static public Thread t=null;

    static public String[] getKeywords() {
        return allnames;
    }

    static public void deselect() {
        if (ZC != null) {
            ZC.clearSelected();
            PaletteManager.ClicOn(CTOOL);
        }
    }

    static public void init() {
        ZC = JZirkelCanvas.getCurrentZC();
    }

    static public void InitBeforeRun() {
        cx = Context.enter();
//        cx.setOptimizationLevel(-1);
//        cx.setLanguageVersion(Context.VERSION_1_7);
        scope = cx.initStandardObjects();
        scope.defineFunctionProperties(allnames, JSRun.class, ScriptableObject.DONTENUM);

//Object wrappedOut = Context.javaToJS(System.out, scope);
//ScriptableObject.putProperty(scope, "out", wrappedOut);

// Création d'une classe accessible en javascript, qui correspond
// à celle de la classe java :
        Const bb = new Const();
        Object wrappedOut2 = Context.javaToJS(bb, scope);
        ScriptableObject.putProperty(scope, "Const", wrappedOut2);


        CTOOL = PaletteManager.geomSelectedIcon();
        ZC = JZirkelCanvas.getCurrentZC();
        C = ZC.getConstruction();

        // try to backup the whole xml construction :
//        backup();

        try {
            console = new JSOuputConsole();
        } catch (Exception ex) {
        }

    }

    static public void clear() {
        deselect();
        cx = null;
        scope = null;
        C = null;
        ZC = null;
        CTOOL = "";
        console = null;
        JST = null;
//        CBACKUP="";
        BACKUPS.clear();
        busy = false;
        Global.setJSquit(false);
        Global.setJSO(null);
    }

    static public void cancelThread() {
        if (JST != null) {
            JST.interrupt();
        }
        JST = null;
    }

    static public void backup() {
        try {
            BACKUPS.add(FileTools.getCurrentFileSource());
        } catch (Exception ex) {
        }
    }

    static public boolean isBackup() {
        return (BACKUPS.size() > 0);
    }

    static public void restore() {
        cancelThread();
        if (BACKUPS.size() > 0) {
            C.clear();
            try {
                FileTools.setCurrentFileSource(BACKUPS.get(BACKUPS.size() - 1));
            } catch (final Exception e) {
            }
            BACKUPS.remove(BACKUPS.size() - 1);
            if (JSC != null) {
                JSC.setBackBtnEnabled(BACKUPS.size() > 0);
            }
        }
    }

    static public void openEditor() {
        if (JSC == null) {
            JSC = new JSConsole();
        } else {
            JSC.toFront();
        }
    }

    static public void closeEditor() {
        if (JSC != null) {
            if (JSC.cancelSaveChanges()) {
                
                return;
            }
            JSRun.clear();
            JSC.dispose();
        }
    }

//    static public void closeEditor() {
//        if (JSC==null) {
//            JSC=new JSConsole();
//        } else {
//            JSC.toFront();
//        }
//    }
    static public void newScriptInConstruction() {
        openEditor();
        JSC.selectScriptInfileChk(true);
        saveScriptInConstruction();
    }

    static public void saveScript(final String script, final boolean forcedlog) {
        Writer output = null;
        File file = null;
        String myFileName = null;

        if ((JSC.isFileOpened()) && (!forcedlog)) {
            myFileName = JSC.getFileName();
        } else {
            myFileName = FileTools.getSaveFile();
        }

        if (myFileName == null) {
            return;
        }

        myFileName += (myFileName.toLowerCase().endsWith(".js")) ? "" : ".js";
        file = new File(myFileName);

        try {
            output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF8"));
            output.write(script);
            output.close();
            JSC.setTextAreaChanged(false);
            JGeneralMenuBar.s_InitJSMenu();
            JSC.setWindowTitle(myFileName);
            JSC.setFileName(myFileName);
        } catch (Exception ex) {
            Logger.getLogger(JSRun.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    static public void runScript(final String script) {
        JST = new Thread() {

            public void run() {
                InitBeforeRun();
                backup();
                if (JSC != null) {
                    JSC.setBackBtnEnabled(true);
                }
                Global.setJSquit(false);
                busy = true;
                try {
                    cx.evaluateString(scope, script, "<cmd>", 0, null);
                } catch (RhinoException e) {
//                    System.out.println("syntaxic bug :");
                    if (JSC != null) {
                        JSC.Error(e.getMessage());
                    } else {

                        openEditor();
                        JSC.setScriptArea(script);
                        JSC.setWindowTitle(FILENAME);
                        JSC.setTextAreaChanged(false);
                        JSC.Error(e.getMessage());
                    }
                    restore();
                } finally {
                    if (cx != null) {
                        cx.exit();
                    }

                }
                if (JSC != null) {
                    JSC.setAlwaysOnTop(true);
                }
                deselect();
                C.dovalidate();
                ZC.paint(ZC.getGraphics());
                busy = false;
                if (JSC != null) {
                    SwingUtilities.invokeLater(new Runnable() {

                        public void run() {
                            JSC.setAlwaysOnTop(false);
                        }
                    });

                }
            }
        };
        JST.setPriority(Thread.MIN_PRIORITY);
        JST.start();
    }

    public static String getScriptsFolder() {
        String name = "";
        final JFileChooser jfc = new JFileChooser(Global.getOpenSaveDirectory());
        jfc.addChoosableFileFilter(new FileFilter() {

            @Override
            public boolean accept(File f) {
                if (f.isDirectory()) {
                    return true;
                }
                if (f.getAbsolutePath().toLowerCase().endsWith(".js")) {
                    return true;
                }
                return false;
            }

            @Override
            public String getDescription() {
                return "";
            }
        });
        jfc.setDialogType(javax.swing.JFileChooser.OPEN_DIALOG);
        jfc.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY);
        jfc.setApproveButtonText(Global.Loc("JSeditor.opendfavfolder"));
        int rep = jfc.showOpenDialog(null);

        if (rep == JFileChooser.APPROVE_OPTION) {
            name = jfc.getSelectedFile().getAbsolutePath();
            Global.setOpenSaveDirectory(name);
            Global.setParameter("scriptfolder", name);
        } else {
            name = "";
        }
        return name;
    }

    static public void removeScriptFromConstruction() {
        if (!JSC.getScriptName().equals("")) {
            ZirkelCanvas zc = JZirkelCanvas.getCurrentZC();
            if (zc != null) {
                zc.removeScript(JSC.getScriptName());
            }
            JSC.setScriptName("");
            if (JSC.getFileName().equals("")) {
                JSC.setWindowTitle(Global.Loc("JSeditor.title"));
            }
        }
    }

    static public void saveScriptInConstruction() {
        if (JSC.getScriptName().equals("")) {
            if (JSC.getFileName().equals("")) {
                String s = (String) JOptionPane.showInputDialog(
                        JSC,
                        Global.Loc("JSeditor.saveinfig.question"),
                        Global.Loc("JSeditor.saveinfig.title"),
                        JOptionPane.PLAIN_MESSAGE,
                        null,
                        null,
                        "");
                if ((s != null) && (s.length() > 0)) {
                    s = uniqueScriptName(s);
                    JSC.setWindowTitle(Global.Loc("JSeditor.infig") + s);
                    JSC.setScriptName(s);

                }
            } else {
                String s = FileName.filename(JSC.getFileName());
                s = s.replaceAll("\\Q.js\\E$", "");
                s = s.replaceAll("\\Q.JS\\E$", "");
                JSC.setScriptName(s);
            }
        }
        ZirkelCanvas zc = JZirkelCanvas.getCurrentZC();
        if (zc != null) {
            zc.saveScript(JSC.getScriptName(), JSC.getScript());
        }
    }

    public static boolean unique(String s, Vector<ScriptItem> V) {
        boolean unique = true;
        for (int i = 0; i < V.size(); i++) {
            if (s.equals(V.get(i).getScriptName())) {
                unique = false;
            }
        }
        return unique;
    }

    public static String uniqueScriptName(String base) {
        ZirkelCanvas zc = JZirkelCanvas.getCurrentZC();
        if (zc == null) {
            return base;
        }
        Vector<ScriptItem> V = zc.getScripts();
        if (!unique(base, V)) {
            int num = 0;
            do {
                num++;
                base = base.replaceAll("[\\s0-9]+$", "") + " " + num;
            } while (!unique(base, V));
        }
        return base;

    }

    static public void openEmbeddedScript(String name, String source) {
        openEditor();
        JSC.setScriptArea(source);
        JSC.setWindowTitle(Global.Loc("JSeditor.infig") + name);
        JSC.setScriptName(name);
        JSC.selectScriptInfileChk(true);
        JSC.setTextAreaChanged(false);

    }

    static public void openScriptFile(final String Filename, final boolean forceeditor) {
        closeEditor();
        String myscript = "";
        if (Filename.equals("")) {
            myscript = FileTools.getOpenFile();
        } else {
            myscript = Filename;
        }
        String str = "";
        String mystr = "";
        try {
            InputStream input = new FileInputStream(myscript);
            BufferedReader in = new BufferedReader(new InputStreamReader(input, "UTF-8"));
            while ((str = in.readLine()) != null) {
                str = str.trim();
                mystr += str + "\n";
            }
        } catch (Exception ex) {
//            System.out.println("bug !");
            FILENAME = "";
            return;
        }
        FILENAME = myscript;
        if ((!forceeditor) && (Global.getParameter("jsdumb", false))) {
            init();
            runScript(mystr);
        } else {
            
            openEditor();
            JSC.setScriptArea(mystr);
            JSC.setWindowTitle(FILENAME);
            JSC.setFileName(FILENAME);
            JSC.setTextAreaChanged(false);
            JSC.selectScriptInfileChk(false);
        }
    }

    private static void refreshZC() {
        if (C != null) {
            C.dovalidate();
            ZC.paint(ZC.getGraphics());
        }
    }

    private static String parseVariables(String s) {
        String origin = "";
        String endtxt = s.replaceAll("([0-9]+)e([0-9]+)", "$1E$2");
        do {
            origin = endtxt;
            StringBuffer sb1 = new StringBuffer();
            Pattern pxy = Pattern.compile("(x|y)_(\\w*)", Pattern.CASE_INSENSITIVE);
            Matcher m = pxy.matcher(origin);
            while (m.find()) {
                Object x = scope.get(m.group(2), scope);
                if (x != Scriptable.NOT_FOUND) {
                    m.appendReplacement(sb1, m.group(1) + "(" + Context.toString(x) + ")");
                }
            }
            m.appendTail(sb1);
            m.reset();
            StringBuffer sb2 = new StringBuffer();
            Pattern pexp = Pattern.compile("_(\\w*)", Pattern.CASE_INSENSITIVE);
            m = pexp.matcher(sb1.toString());
            while (m.find()) {
                Object x = scope.get(m.group(1), scope);
                if (x != Scriptable.NOT_FOUND) {
                    m.appendReplacement(sb2, Context.toString(x));
                }
            }
            m.appendTail(sb2);
            m.reset();
            endtxt = sb2.toString();
        } while (!(origin.equals(endtxt)));
        return endtxt;
    }

    static public String Loc(final String s) {
        return Global.Loc("JSerror." + s);
    }

    static public String LastObjectName() {
        if (C.last() != null) {
            return C.last().getName();
        } else {
            return "";
        }
    }

    static public void NormalizeLast() {
        ConstructionObject o = C.last();
        if (o != null) {
            o.setColorType(ConstructionObject.NORMAL);
            o.setShowName(false);
            o.setShowValue(false);
            o.setFilled(false);
            o.setPartial(false);
        }
    }

    /**
     * Pause in milliseconds
     * @param pause time, in milliseconds
     */
    static public void Pause(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Print a string in a console. Print calls won't generate carriage return
     * at the end of the output. If the console is not visible, it will be
     * bring to front automatically.
     * @param a String to be displayed
     */
    static public void Print(String a) {
        if (!console.isVisible()) {
            console.setVisible(true);
        }
        console.print(parseVariables(a));
    }

    /**
     * Print a string in a console. Println calls will generate carriage return
     * at the end of the output. If the console is not visible, it will be
     * bring to front automatically.
     * @param a String to be displayed
     */
    static public void Println(String a) {
        if (!console.isVisible()) {
            console.setVisible(true);
        }
        console.println(parseVariables(a));
    }

    /**
     * Ask and wait for a data input. This will bring a modal dialog to front
     * and the script will be interrupted until the user presses the ok button.
     * @param msg Question to ask the user
     * @return String which was typed in the box (typically : answer to the question)
     */
    static public Object Input(String msg) {
        Object s = JOptionPane.showInputDialog(
                JZirkelCanvas.getCurrentZC(),
                msg,
                "Input dialog",
                JOptionPane.QUESTION_MESSAGE,
                null,
                null, // c'est ouvert !!!
                ""); // valeur initiale
        return s;
    }

    /**
     * This will stop the script execution until the user shows an object of
     * a given type in the current CaRMetal window. The message msg will be
     * displayed in the status bar (bottom of the window) and the name of the
     * object you clicked on will be return. If the user selects a tool in the
     * palette, the script execution will ends.
     * @param msg Message to be displayed in the status bar
     * @param type Type of the object. This can be : Point, Line,
     * Segment, Circle and Expression
     * @return Name of the selected object
     */
    static public Object InteractiveInput(String msg, String type) throws Exception {
        Global.setJSO(null);
        Global.setJSquit(false);
        ZC.setJSTool(msg, type);
        while (Global.getJSO() == null) {
            if (Global.getJSquit()) {
//                cx.exit();
//                clear();
//                return null;
                throw new Exception(Loc("interactiveinput"));
            }
        }
        return Global.getJSO().getName();
    }

    /**
     * Shows an alert message. This will stop the
     * script execution until the user presses on the ok button.
     * @param msg Message to be displayed
     */
    static public void Prompt(String msg) {
        JOptionPane.showMessageDialog(
                JZirkelCanvas.getCurrentZC(),
                parseVariables(msg),
                "Prompt dialog",
                JOptionPane.WARNING_MESSAGE);
    }

    /**
     * Shows an alert message. This will stop the
     * script execution until the user presses on the ok button.
     * @param msg Message to be displayed
     */
    static public void Alert(String msg) {
        JOptionPane.showMessageDialog(
                JZirkelCanvas.getCurrentZC(),
                parseVariables(msg),
                "Prompt dialog",
                JOptionPane.WARNING_MESSAGE);
    }

    static public void c(String a) {
        ZC.JSsend(a);
    }

    static public String cm(String a) {
        ZC.JSsend(a);
        return LastObjectName();
    }

    /**
     * Moves a given object to an (x;y) position in the current CaRMetal window.
     * @param name Name of the object you want to move.
     * @param i x-coordinate
     * @param j y-coordinate
     */
    static public void Move(String name, String i, String j) throws Exception {
        ConstructionObject o = C.find(name);
        if (o == null) {
            throw new Exception(Loc("notfound"));
        }
        if (!(o instanceof PointObject)) {
            throw new Exception(Loc("notgoodtype"));
        }
        try {
            double x = Math.round(Double.valueOf(parseVariables(i)) * 1E13) / 1E13;
            double y = Math.round(Double.valueOf(parseVariables(j)) * 1E13) / 1E13;
            o.move(x, y);
        } catch (Exception e) {
            Expression expX = new Expression(parseVariables(i), o.getConstruction(), o);
            Expression expY = new Expression(parseVariables(j), o.getConstruction(), o);
            o.move(expX.getValue(), expY.getValue());
        }


        C.dovalidate();
        ZC.paint(ZC.getGraphics());
    }

    /**
     * Delete a given object in the current CaRMetal window.
     * @param name Name of the object you want to delete.
     */
    static public void Delete(String name) throws Exception {
        ConstructionObject o = C.find(name);
        if (o == null) {
            throw new Exception(Loc("notfound"));
        }
        ZC.delete(o);
    }

//  *******************************************************
//    Méthode retournant des informations sur les objets :
//  *******************************************************
    /**
     * Returns the value of a given expression which is in the construction.
     * Remember that "controls" are expressions too, so you can capture a
     * value of a control by this way.
     * @param name Name of an existing expression
     * @return Expression's numeric value.
     */
    static public double GetExpressionValue(String name) throws Exception {
        ConstructionObject o = C.find(name);
        if (o == null) {
            throw new Exception(Loc("notfound"));
        }
        if (!(o instanceof ExpressionObject)) {
            throw new Exception(Loc("notgoodtype"));
        }
        ExpressionObject oc = (ExpressionObject) o;
        double res = Double.NaN;
        try {
            res = oc.getValue();
        } catch (Exception ex) {
        }
        return res;
    }

    /**
     * Set the value of a given expression which is in the construction.
     * @param name Name of an existing expression
     * @param value Value you want to give to the expression
     */
    static public void SetExpressionValue(String name, String value) throws Exception {
        ConstructionObject o = C.find(name);
        if (o == null) {
            throw new Exception(Loc("notfound"));
        }
        if (!(o instanceof ExpressionObject)) {
            throw new Exception(Loc("notgoodtype"));
        }
        ExpressionObject oc = (ExpressionObject) o;
        o.setFixed(parseVariables(value));
        refreshZC();
    }

    /**
     *Return the x-coordinate of an object. If no object is found, then it returns NaN.
     * @param name the name of the object
     * @return the numeric value of the x-coordinate
     */
    static public double X(String name) throws Exception {
        ConstructionObject o = C.find(name);
        if (o != null) {
            return o.getX();
        } else {
            throw new Exception(Loc("notfound"));
        }
    }

    /**
     *Return the y-coordinate of an object. If no object is found, then it returns NaN.
     * @param name the name of the object
     * @return the numeric value of the y-coordinate
     */
    static public double Y(String name) throws Exception {
        ConstructionObject o = C.find(name);
        if (o != null) {
            return o.getY();
        } else {
            throw new Exception(Loc("notfound"));
        }
    }

//  **********************************************
//    Modification des attributs des objets
//  **********************************************
    /**
     * Set the color of an object using one of the 6 predifined colors
     * of CaRMetal.
     * <br><b>Note</b> : It's possible to change this property in one step for
     * multiple object.
     * <br><b> Example</b> : <i><b>SetColor("A,B,c1,E1","cyan")</b></i> will give the "cyan" color
     * to the objects A, B, c1 and E1
     * @param name Name of the object you want to change color
     * @param col Name of the color. It can be "green", "blue", "brown", "cyan", "red" or "black".
     */
    static public void SetColor(String name, String col) {
        String[] names = name.split(",");
        for (int i = 0; i < names.length; i++) {
            c("color(" + col + "," + names[i] + ")");
        }
    }

    /**
     *Set the shape of a point. It has no effect if the object is not a point.
     * <br><b>Note</b> : It's possible to change this property in one step for
     * multiple points.
     * <br><b> Example</b> : <i><b>SetPointType("A,B,E,F","square")</b></i> will give the "square" shape
     * to the points A, B, E and F
     * @param name Name of the point you want to change shape
     * @param type Name of the shape. It can be "square", "circle", "diamond", "point", "cross", "dcross".
     */
    static public void SetPointType(String name, String type) {
        String[] names = name.split(",");
        for (int i = 0; i < names.length; i++) {
            c("type(" + type + "," + names[i] + ")");
        }
    }

    /**
     * Set the RGB color of an object using 3 integer numbers in [0..255].
     * <br><b>Note</b> : It's possible to change this property in one step for
     * multiple object.
     * <br><b> Example</b> : <i><b>SetColor("A,B,c1",120,40,245)</b></i> will give the same color
     * to the objects A, B and c1. This color is defined by r=120, g=40 and b=245.
     * @param name Name of the object you want to change color
     * @param r Red value (integer in [0..255]
     * @param g Green value (integer in [0..255]
     * @param b Blue value (integer in [0..255]
     */
    static public void SetRGBColor(String name, int r, int g, int b) {
        Color mycolor = new Color(r, g, b);
        String[] names = name.split(",");
        for (int i = 0; i < names.length; i++) {
            ConstructionObject o = C.find(names[i]);
            if (o != null) {
                o.setColor(0, mycolor);
            }
        }
    }

    /**
     * Set the thickness of an object.
     * <br><b>Note</b> : It's possible to change this property in one step for
     * multiple objects.
     * <br><b> Example</b> : <i><b>SetThickness("A,B,c1,l1","thick")</b></i> will give the "thick" aspect
     * to the objects A, B, c1 and l1
     * @param name Name of the point you want to change shape
     * @param thc Name of the thickness. It can be "thick", "normal" and "thin".
     */
    static public void SetThickness(String name, String thc) {
        String[] names = name.split(",");
        for (int i = 0; i < names.length; i++) {
            c("thickness(" + thc + "," + names[i] + ")");
        }
    }

    /**
     * Set the alias name of an object.
     * <br><b>Note</b> : It's possible to change this property in one step for
     * multiple objects.
     * <br><b>Example</b> : <i><b>SetAlias("A,B,c1,l1","My beautiful object")</b></i>
     * will give the "My beautiful object" alias name to the objects A, B, c1 and l1
     * @param name Name of the point you want to change alias name.
     * @param alias Alias name.
     */
    static public void SetAlias(String name, String alias) {
        String[] names = name.split(",");
        for (int i = 0; i < names.length; i++) {
            ConstructionObject o = C.find(names[i]);
            if (o != null) {
                o.setAlias(alias);
            }
        }
    }

    /**
     * Set the standard magnetic attraction lenght of a point
     * @param name Name of a point
     * @param ray Attraction lenght, in pixels (number or expression)
     * @see #SetMagneticObjects
     * @see #AddMagneticObject
     */
    static public void SetMagneticRay(String name, String ray) {
        ConstructionObject o = C.find(name);
        if ((o == null) || (!(o instanceof PointObject))) {
            return;
        }
        PointObject pt = (PointObject) o;
        pt.setMagnetRayExp(ray);
        C.dovalidate();
        ZC.paint(ZC.getGraphics());
    }

    /**
     * Set the objects that will magnetize a given point
     * <br><b>Example</b> : <i><b>SetMagneticObjects("P","c1,d1,A")</b></i> will
     * make the point P attracted by the objects c1, d1 and A.
     * <br> It's possible to define exceptions using the ":" separator.
     * If, for instance, objectlist contains "A,E,c1:20,l1" and the attraction
     * field is defined by 50 pixels, objects "A", "E" and "l1" have an attraction
     * radius of 50 pixels, but the object c1 will have an attraction radius of 20 pixels.
     * @param name Name of a point
     * @param objectlist List of objects
     * @see #AddMagneticObject
     * @see #SetMagneticRay
     */
    static public void SetMagneticObjects(String name, String objectlist) {
        ConstructionObject o = C.find(name);
        if ((o == null) || (!(o instanceof PointObject))) {
            return;
        }
        PointObject pt = (PointObject) o;
        pt.setMagnetObjects(parseVariables(objectlist));
    }

    /**
     * Add an object to the list of magnetic objects of a given point.
     * @param name Name of the point
     * @param object Object to add to the magnetic point list
     * @see #SetMagneticObjects
     * @see #SetMagneticRay
     */
    static public void AddMagneticObject(String name, String object) {
        ConstructionObject o = C.find(name);
        if ((o == null) || (!(o instanceof PointObject))) {
            return;
        }
        PointObject pt = (PointObject) o;
        pt.addMagnetObject(object);
    }

    /**
     * Fix or unfix an object in the CaRMetal current window. A fixed object
     * can't be moved with the mouse.
     * <br><b>Note</b> : It's possible to change this property in one step for
     * multiple objects.
     * <br><b>Example</b> : <i><b>SetFixed("A,B,c1,l1",true)</b></i>
     * will fix the objects A, B, c1 and l1 in the CaRMetal current window.
     * @param name Name of the object
     * @param bool "true" value to fix the object, "false" to unfix it.
     */
    static public void SetFixed(String name, boolean bool) {
        String[] names = parseVariables(name).split(",");
        for (int i = 0; i < names.length; i++) {
            ConstructionObject o = C.find(names[i]);
            if (o != null) {
                o.setFixed(bool);
            }
        }
    }

    /**
     * Shows or hide the name of an object (or multiple objects).
     * <br><b>Example</b> : <i><b>SetShowName("A,B,c1,l1",true)</b></i>
     * will shows the names of objects A, B, c1 and l1.
     * @param name Name(s) of object.
     * @param bool "true" to show name, and "false" to hide it.
     */
    static public void SetShowName(String name, boolean bool) {
        String[] names = parseVariables(name).split(",");
        for (int i = 0; i < names.length; i++) {
            ConstructionObject o = C.find(names[i]);
            if (o != null) {
                o.setShowName(bool);
            }
        }
    }

    /**
     * Shows or hide the value of an object (or multiple objects).
     * <br><b>Example</b> : <i><b>SetShowValue("A,B,c1,l1",true)</b></i>
     * will shows the value of objects A, B, c1 and l1.
     * @param name Name(s) of object.
     * @param bool "true" to show value, and "false" to hide it.
     */
    static public void SetShowValue(String name, boolean bool) {
        String[] names = parseVariables(name).split(",");
        for (int i = 0; i < names.length; i++) {
            ConstructionObject o = C.find(names[i]);
            if (o != null) {
                o.setShowValue(bool);
            }
        }
    }

    /**
     * Some objects can be filled (circles, polygons, etc...). This will
     * fill or unfill an object (or multiple objects).
     * <br><b>Example</b> : <i><b>SetFilled("A,B,c1,l1",true)</b></i>
     * will fill objects A, B, c1 and l1.
     * @param name Name(s) of object.
     * @param bool "true" to fill objects, and "false" to unfill it.
     */
    static public void SetFilled(String name, boolean bool) {
        String[] names = parseVariables(name).split(",");
        for (int i = 0; i < names.length; i++) {
            ConstructionObject o = C.find(names[i]);
            if (o != null) {
                o.setFilled(bool);
            }
        }
    }

    /**
     * Some objects can be drawn partially (circles, lines). This will
     * draw partially or not an object (or multiple objects).
     * <br><b>Example</b> : <i><b>SetPartial("A,B,c1,l1",true)</b></i>
     * will draw partially objects A, B, c1 and l1.
     * @param name Name(s) of object.
     * @param bool "true" to draw partially, and "false" to draw completly.
     */
    static public void SetPartial(String name, boolean bool) {

        String[] names = parseVariables(name).split(",");
        for (int i = 0; i < names.length; i++) {
            ConstructionObject o = C.find(names[i]);
            if (o != null) {
                o.setPartial(bool);
                C.updateCircleDep();
            }
        }
    }

    /**
     *After calling this command, names of new objects will be displayed.
     */
    static public void Shownames() {
        cm("shownames");
    }

    /**
     *After calling this command, names of new objects will not be displayed.
     */
    static public void Hidenames() {
        cm("hidenames");
    }

    /**
     * Hides an object (or multiple objects).
     * <br><b>Example</b> : <i><b>Hide("A,B,c1,l1",true)</b></i>
     * will hide objects A, B, c1 and l1.
     * @param name Name(s) of object(s).
     */
    static public void Hide(String name) {
        String[] names = parseVariables(name).split(",");
        for (int i = 0; i < names.length; i++) {
            c("hide(true," + names[i] + ")");
        }
        ZC.paint(ZC.getGraphics());
    }

    static public void SetHide(String name, boolean b) {
        String[] names = parseVariables(name).split(",");
        for (int i = 0; i < names.length; i++) {
            c("hide(" + b + "," + names[i] + ")");
        }
        ZC.paint(ZC.getGraphics());
    }

    /**
     * Shows an object (or multiple objects).
     * <br><b>Example</b> : <i><b>Show("A,B,c1,l1",true)</b></i>
     * will show objects A, B, c1 and l1.
     * @param name Name(s) of object(s).
     */
    static public void Show(String name) {
        String[] names = parseVariables(name).split(",");
        for (int i = 0; i < names.length; i++) {
            c("hide(false," + names[i] + ")");
        }
        ZC.paint(ZC.getGraphics());
    }

//    static public void Delete(String name) {
//        String[] names=parseVariables(name).split(",");
//        for (int i=0; i<names.length; i++) {
//            ConstructionObject c=C.find(names[i]);
//            if (c!=)
//
//            c("hide(false,"+names[i]+")");
//        }
//        ZC.paint(ZC.getGraphics());
//    }
//  **********************************************
//    Création des objets :
//  **********************************************
    static public void Layer(String name, String exp) throws Exception {
        Conditional(name, "z", exp);
    }

    /**
     * Create a condition for an object or a list of objects
     * @param name Name(s) of object(s)
     * @param TAG type of condition this can be equal to :
     * solid, hidden, normal, thick, thin, black, green, blue,
     * cyan, red, brown, showvalue, showname, background, and superhidden
     * @param expTXT contitional expression to be apply
     * @return Name of the created point
     */
    static public void Conditional(String name, String TAG, String expTXT) throws Exception {
        try {
            String[] names = parseVariables(name).split(",");
            for (int i = 0; i < names.length; i++) {
                ConstructionObject O = C.find(names[i]);
                if (O != null) {
                    O.clearConditional(TAG);
                    Expression e = new Expression(parseVariables(expTXT), C, O);
                    if (!e.isValid()) {
                        throw new Exception(Loc("condition"));
                    }
                    O.addConditional(TAG, e);
                }
            }
            ZC.recompute();
            ZC.validate();
            ZC.repaint();
        } catch (Exception ex) {
            throw new Exception(Loc("condition"));
        }
    }

    /**
     * Create a point at given coordinates. coordinates can be defined by numerical
     * values (e.g. Point("A",-1,5.2)) or using expressions (e.g. Point("C","x(B)+1","y(B)+1").
     * If the name is not given (e.g. Point("",2,-2.5)), the point will be created as well.
     * <br>From javascript, you can also do something like this :
     * <ol>
     * <li>a=Point("",1,2)</li>
     * <li>b=Point("",-2,3)</li>
     * <li>m=4</li>
     * <li>c=Point("","(x_a+x_b)/_m","(y_a+y_b)/_m")</li>
     * </ol>
     * The "_" symbol means that javascript must use the content of variables.
     * For example The string "_m" will be replaced by "4" (line 3).<br>Another
     * "shortcut" : if, for example, a contains "P1" (the real name of the point
     * created by step 1), the string x_a is equivalent to the string "x(P1)".
     * @param name Name of the point (suggestion)
     * @param x x-coordinate (number or expression)
     * @param y y-coordinate (number or expression)
     * @return Name of the created point
     */
    static public String Point(String name, String x, String y) throws Exception {
        if (name.equals("undefined")) {
            double xx = C.getX() + 2 * Math.random() * C.getW() - C.getW();
            double yy = C.getY() - Math.random() * C.getH() + C.getH() / 2;
            x = "" + xx;
            y = "" + yy;
            name = "";
        } else if (y.equals("undefined")) {
            y = x;
            x = name;
            name = "";
        }

        PointObject pt = null;
        try {
            pt = new PointObject(C, Math.round(Double.valueOf(x) * 1E13) / 1E13, Math.round(Double.valueOf(y) * 1E13) / 1E13);
        } catch (Exception e) {
            try {
                pt = new PointObject(C, 0, 0);
                pt.setFixed(parseVariables(x), parseVariables(y));
                pt.validCoordinates();


            } catch (Exception ex) {

                throw new Exception(Loc("pointcoords"));
            }
        }
        pt.setDefaults();
        if (!name.equals("")) {
            pt.setName(name);
        }
        pt.setColorType(ConstructionObject.NORMAL);
        pt.setShowName(false);
        pt.setShowValue(false);
        pt.validate();
        C.add(pt);
        return pt.getName();
    }

    /**
     * Create the midpoint of two existing points
     * @param name Name of the midpoint
     * @param a Name of first point
     * @param b Name of second point
     * @return the midpoint name
     */
    static public String MidPoint(String name, String a, String b) {
        if (b.equals("undefined")) {
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("M(" + a + "," + b + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=" + "M(" + a + "," + b + ")");
        }
        return LastObjectName();
    }

    /**
     * Reflect the point p with line d
     * @param name Name of the Symmetric
     * @param d Reflection line
     * @param p Point to relect
     * @return the reflection point name
     */
    static public String Reflection(String name, String d, String p) throws Exception {
        if (p.equals("undefined")) {
            p = d;
            d = name;
            name = "";
        }
        if (name.equals("")) {
            c("@builtin@/syma(" + d + "," + p + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=@builtin@/syma(" + d + "," + p + ")");
        }
        return LastObjectName();
    }

    /**
     * Translate the point p with vector ab
     * @param name Name of the Translated point
     * @param a first point of the vector
     * @param b second point of the vector
     * @param p Point to translate
     * @return the translated point name
     */
    static public String Translation(String name, String a, String b, String p) throws Exception {
        if (p.equals("undefined")) {
            p = b;
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("@builtin@/trans(" + a + "," + b + "," + p + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=@builtin@/trans(" + a + "," + b + "," + p + ")");
        }
        return LastObjectName();
    }

    /**
     * Reflect the point b with center a
     * @param name Name of the Reflect point
     * @param a Symmetry center
     * @param b Point to relect
     * @return the symmetric point name
     */
    static public String Symmetry(String name, String a, String b) {
        if (b.equals("undefined")) {
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("P(2*x(" + a + ")-x(" + b + "),2*y(" + a + ")-y(" + b + "))");
            C.last().setShowName(false);
        } else {
            c(name + "=" + "P(2*x(" + a + ")-x(" + b + "),2*y(" + a + ")-y(" + b + "))");
        }
        return LastObjectName();
    }

    /**
     * Create the perpendicular bisector between two points a and b
     * @param name Name of the perpendicular bisector
     * @param a first point
     * @param b second point
     * @return the perpendicular bisector name
     */
    static public String PerpendicularBisector(String name, String a, String b) {

        if (b.equals("undefined")) {
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("@builtin@/med(" + a + "," + b + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=@builtin@/med(" + a + "," + b + ")");
        }
        return LastObjectName();
    }

    /**
     * Create the angle bisector between 3 points a, b and c
     * @param name Name of the perpendicular bisector
     * @param a first point
     * @param b second point
     * @param c third point
     * @return the angle bisector name
     */
    static public String AngleBisector(String name, String a, String b, String c) throws Exception {
        if (c.equals("undefined")) {
            c = b;
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("@builtin@/biss(" + a + "," + b + "," + c + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=@builtin@/biss(" + a + "," + b + "," + c + ")");
        }
        return LastObjectName();
    }

    /**
     * Create the circumcircular circle between 3 points a, b and c
     * @param name Name of the perpendicular bisector
     * @param a first point
     * @param b second point
     * @param c third point
     * @return the circumcircular circle name
     */
    static public String Circle3pts(String name, String a, String b, String c) throws Exception {
        if (c.equals("undefined")) {
            c = b;
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("@builtin@/circ(" + a + "," + b + "," + c + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=@builtin@/circ(" + a + "," + b + "," + c + ")");
        }
        return LastObjectName();
    }

    /**
     * Create the circle around a with radius bc
     * @param name Name of the circle
     * @param a center point
     * @param b first point
     * @param c second point
     * @return the circle name
     */
    static public String Circle3(String name, String a, String b, String c) throws Exception {
        if (c.equals("undefined")) {
            c = b;
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("c(" + a + "," + b + "," + c + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=c(" + a + "," + b + "," + c + ")");
        }
        return LastObjectName();
    }

    /**
     * Create the circumcircular arc between 3 points a, b and c
     * @param name Name of the perpendicular bisector
     * @param a first point
     * @param b second point
     * @param c third point
     * @return the circumcircular arc name
     */
    static public String Arc3pts(String name, String a, String b, String c) throws Exception {
        if (c.equals("undefined")) {
            c = b;
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("@builtin@/arc(" + a + "," + b + "," + c + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=@builtin@/arc(" + a + "," + b + "," + c + ")");
        }
        return LastObjectName();
    }

    /**
     * Creates the intersection between two objects. If you provide a name
     * for the intersection, only one intersection point will be created.
     * If you give the empty string "" as <i>name</i> parameter, two intersection
     * points will be created, if the <i>a</i> and <i>b</i> objects are two circles
     * or line/circle.
     * @param name Name of the intersection point
     * @param a Name of first object
     * @param b Name of second object
     * @return Name of intersection point
     * @see #Intersection2
     */
    static public String Intersection(String name, String a, String b) {
        if (b.equals("undefined")) {
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
//            System.out.println("int");
            c("I(" + a + "," + b + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=" + "I(" + a + "," + b + ")");
        }
        return LastObjectName();
    }

    /**
     * When two objects have two intersection points (two circles or one line/one circle)
     * you may use this command to create them both.
     * @param name1 Name of first intersection point
     * @param name2 Name of second intersection point
     * @param a Name of first object
     * @param b Name of second object
     * @return Name of intersection point
     * @see #Intersection
     */
    static public String Intersection2(String name1, String name2, String a, String b) {
        if (b.equals("undefined")) {
            b = a;
            a = name2;
            name2 = name1;
            name1 = "";
            if (b.equals("undefined")) {
                b = a;
                a = name2;
                name2 = "";
            }
        }
        if (name1.equals("")) {
            c("I1,I2=I(" + a + "," + b + ")");
            C.last().setShowName(false);
        } else {
            c(name1 + "," + name2 + "=" + "I(" + a + "," + b + ")");
        }
        return LastObjectName();
    }

    /**
     * Creates a line between two points
     * @param name Name of the line (suggestion)
     * @param a Name of first point
     * @param b Name of second point
     * @return Name of the created line
     */
    static public String Line(String name, String a, String b) {
        if (b.equals("undefined")) {
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("l(" + a + "," + b + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=" + "l(" + a + "," + b + ")");
        }
        NormalizeLast();
        return LastObjectName();
    }

    /**
     * Creates a ray from point a to point b
     * @param name Name of the ray (suggestion)
     * @param a Name of first point
     * @param b Name of second point
     * @return Name of the created ray
     */
    static public String Ray(String name, String a, String b) {
        if (b.equals("undefined")) {
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("r(" + a + "," + b + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=r(" + a + "," + b + ")");
        }
        NormalizeLast();
        return LastObjectName();
    }

    /**
     * Creates an angle defined by 3 points a,b and c (b is the vertex).
     * @param name Name of the angle (suggestion)
     * @param a Name of first point
     * @param b Name of the vertex
     * @param c Name of the third point
     * @return Name of the created angle
     */
    static public String Angle(String name, String a, String b, String c) {
        if (c.equals("undefined")) {
            c = b;
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("a(" + a + "," + b + "," + c + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=a(" + a + "," + b + "," + c + ")");
        }
        return LastObjectName();
    }

    /**
     * Creates an angle defined by 2 points a,b and an expression c (b is the vertex).
     * @param name Name of the angle (suggestion)
     * @param a Name of first point
     * @param b Name of the vertex
     * @param c angle in degree
     * @return Name of the created angle
     */
    static public String FixedAngle(String name, String a, String b, String c) {
        if (c.equals("undefined")) {
            c = b;
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("a(" + a + "," + b + "," + c + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=a(" + a + "," + b + "," + c + ")");
        }
        return LastObjectName();
    }

    /**
     * Execute a macro-construction m with parameters params
     * @param lastObjName Name of last created object
     * @param macroname Name of macro
     * @param params list of objects (e.g. "A,E,F,G")
     * @return Name of the last created object
     */
    static public String ExecuteMacro(String lastObjName, String macroname, String params) {
        if (params.equals("undefined")) {
            params = macroname;
            macroname = lastObjName;
            lastObjName = "";
        }
        if (lastObjName.equals("")) {
            c(macroname + "(" + parseVariables(params) + ")");
            C.last().setShowName(false);
        } else {
            c(lastObjName + "=" + macroname + "(" + parseVariables(params) + ")");
        }
        return LastObjectName();
    }

    /**
     * Creates a polygon define by a list of points. e.g. Polygon("","A,B,C")
     * will create the triangle ABC.
     * @param name Name of the polygon (suggestion)
     * @param params list of objects
     * @return Name of the created polygon
     */
    static public String Polygon(String name, String params) {
        if (params.equals("undefined")) {
            params = name;
            name = "";
        }
        String[] c = parseVariables(params).split(",");
        Vector V = new Vector();
        for (int i = 0; i < c.length; i++) {
            ConstructionObject o = C.find(c[i]);
            if (o != null) {
                V.add(o);
            }
        }
        AreaObject poly = new AreaObject(C, V);
        poly.setDefaults();
        if (!name.equals("")) {
            poly.setName(name);
        }
        poly.validate();
        C.add(poly);
        return LastObjectName();
    }

    /**
     * Creates a quadric defined by five points
     * @param name Name of the quadric (suggestion)
     * @param a Name of first point
     * @param b Name of second point
     * @param c Name of third point
     * @param d Name of fourth point
     * @param e Name of fifth point
     * @return Name of the created quadric
     */
    static public String Quadric(String name, String a, String b, String c, String d, String e) {
        if (e.equals("undefined")) {
            e = d;
            d = c;
            c = b;
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("quadric(" + a + "," + b + "," + c + "," + d + "," + e + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=quadric(" + a + "," + b + "," + c + "," + d + "," + e + ")");
        }
        NormalizeLast();
        return LastObjectName();
    }

    /**
     * Creates the graph of a cartesian function with x variable in [a,b]
     * @param name Name of the function (suggestion)
     * @param a Minimum (number or expression)
     * @param b Maximum (number or expression)
     * @param fx expression of the function (variable x)
     * @return Name of the function
     */
    static public String CartesianFunction(String name, String a, String b, String fx) {
        if (fx.equals("undefined")) {
            fx = b;
            b = a;
            a = name;
            name = "";
        }
        final FunctionObject f = new FunctionObject(C);
        f.setDefaults();
        if (!name.equals("")) {
            f.setName(name);
        }
        f.setExpressions("x", "", fx);
        if ((!a.equals("")) && (!b.equals(""))) {
            f.setRange(a, b, "0");
        }
        C.add(f);
        C.dovalidate();
        ZC.paint(ZC.getGraphics());
        NormalizeLast();
        return f.getName();
    }

    /**
     * Creates the graph of a parmetric function with t variable in [a,b]
     * @param name Name of the function (suggestion)
     * @param a Minimum (number or expression)
     * @param b Maximum (number or expression)
     * @param xt expression of x(t) (variable t)
     * @param yt expression of y(t) (variable t)
     * @return Name of the function
     */
    static public String ParametricFunction(String name, String a, String b, String xt, String yt) {
        if (yt.equals("undefined")) {
            yt = xt;
            xt = b;
            b = a;
            a = name;
            name = "";
        }
        final FunctionObject f = new FunctionObject(C);
        f.setDefaults();
        if (!name.equals("")) {
            f.setName(name);
        }
        f.setExpressions("t", xt, yt);
        if ((!a.equals("")) && (!b.equals(""))) {
            f.setRange(a, b, "0");
        }
        C.add(f);
        C.dovalidate();
        ZC.paint(ZC.getGraphics());
        NormalizeLast();
        return f.getName();
    }

    /**
     * Creates a segment with center a and length r.
     * @param name Name of the segment (suggestion)
     * @param a Center point
     * @param r Length (number or expression)
     * @return Name of the segment
     */
    static public String FixedSegment(String name, String a, String r) {
        if (r.equals("undefined")) {
            r = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("s(" + a + "," + r + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=" + "s(" + a + "," + r + ")");
        }
        NormalizeLast();
        return LastObjectName();
    }

    /**
     * Creates a segment between two points.
     * @param name Name of the segment (suggestion)
     * @param a Name of first point
     * @param b Name of second point
     * @return Name of the created segment
     */
    static public String Segment(String name, String a, String b) {
        if (b.equals("undefined")) {
            b = a;
            a = name;
            name = "";
        }
        SegmentObject so = null;
        try {
            so = new SegmentObject(C, (PointObject) C.find(a), (PointObject) C.find(b));
        } catch (Exception e) {
            return "";
        }
        so.setDefaults();
        if (!name.equals("")) {
            so.setName(name);
        }
        so.setArrow(false);
        so.setColorType(ConstructionObject.NORMAL);
        so.setShowName(false);
        so.setShowValue(false);
        so.validate();
        C.add(so);
        return so.getName();
    }

    /**
     * Creates a vector between two points.
     * @param name Name of the vector (suggestion)
     * @param a Name of origin point
     * @param b Name of second point
     * @return Name of the created vector
     */
    static public String Vector(String name, String a, String b) {
        if (b.equals("undefined")) {
            b = a;
            a = name;
            name = "";
        }
        SegmentObject so = null;
        try {
            so = new SegmentObject(C, (PointObject) C.find(a), (PointObject) C.find(b));
        } catch (Exception e) {
            return "";
        }
        so.setDefaults();
        if (!name.equals("")) {
            so.setName(name);
        }
        so.setArrow(true);
        so.setColorType(ConstructionObject.NORMAL);
        so.setShowName(false);
        so.setShowValue(false);
        C.add(so);
        return so.getName();
    }

    /**
     * Creates a circle with center a to point b.
     * @param name Name of the circle (suggestion)
     * @param a center point
     * @param b point of the circle
     * @return Name of the circle
     */
    static public String Circle(String name, String a, String b) {
        if (b.equals("undefined")) {
            b = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("c(" + a + "," + b + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=" + "c(" + a + "," + b + ")");
        }
        NormalizeLast();
        return LastObjectName();
    }

    /**
     * Creates a circle with center a and radius r.
     * @param name Name of the circle (suggestion)
     * @param a Center point
     * @param r Radius (number or expression)
     * @return Name of the circle
     */
    static public String FixedCircle(String name, String a, String r) {
        if (r.equals("undefined")) {
            r = a;
            a = name;
            name = "";
        }
        if (name.equals("")) {
            c("c(" + a + "," + r + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=" + "c(" + a + "," + r + ")");
        }
        NormalizeLast();
        return LastObjectName();
    }

    /**
     * Creates a parallel line through point pt to line lne.
     * @param name Name of the parallel line (suggestion)
     * @param lne Name of the line
     * @param pt Name of the point
     * @return Name of the parallel line
     */
    static public String Parallel(String name, String lne, String pt) {
        if (pt.equals("undefined")) {
            pt = lne;
            lne = name;
            name = "";
        }
        if (name.equals("")) {
            c("par(" + lne + "," + pt + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=" + "par(" + lne + "," + pt + ")");
        }
        NormalizeLast();
        return LastObjectName();
    }

    /**
     * Creates a perpendicular line through point pt to line lne.
     * @param name Name of the parallel line (suggestion)
     * @param lne Name of the line
     * @param pt Name of the point
     * @return Name of the perpendicular line
     */
    static public String Perpendicular(String name, String lne, String pt) {
        if (pt.equals("undefined")) {
            pt = lne;
            lne = name;
            name = "";
        }
        if (name.equals("")) {
            c("perp(" + lne + "," + pt + ")");
            C.last().setShowName(false);
        } else {
            c(name + "=" + "perp(" + lne + "," + pt + ")");
        }
        NormalizeLast();
        return LastObjectName();
    }

    /**
     * Creates an expression.
     * @param name Name of expression (suggestion)
     * @param exp Expression
     * @param x x-coordinate of expression (number or expression)
     * @param y y-coordinate of expression (number or expression)
     * @return Name of expression
     */
    static public String Expression(String name, String exp, String x, String y) {
        if (y.equals("undefined")) {
            y = x;
            x = exp;
            exp = name;
            name = "";
        }
        exp = parseVariables(exp);
        final ExpressionObject p = new ExpressionObject(C, 0, 0);
        p.setDefaults();
        if (!name.equals("")) {
            p.setName(name);
        }
        try {
            p.setExpression(exp, C);
        } catch (final Exception e) {
            return "";
        }
        try {
            double xx = Math.round(Double.valueOf(x) * 1E13) / 1E13;
            double yy = Math.round(Double.valueOf(y) * 1E13) / 1E13;
            p.move(xx, yy);
        } catch (Exception e) {
            x = parseVariables(x);
            y = parseVariables(y);
            p.setFixed(x, y);
        }
        C.add(p);
        NormalizeLast();
        p.setShowValue(true);
        C.dovalidate();
        ZC.paint(ZC.getGraphics());
        return p.getName();
    }
}
