/*
 * Smart GWT (GWT for SmartClient)
 * Copyright 2008 and beyond, Isomorphic Software, Inc.
 *
 * Smart GWT is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.  Smart GWT is also
 * available under typical commercial license terms - see
 * http://smartclient.com/license
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */
/* sgwtgen */
 
package com.smartgwt.client.tools;


import com.smartgwt.client.event.*;
import com.smartgwt.client.core.*;
import com.smartgwt.client.types.*;
import com.smartgwt.client.data.*;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.events.*;
import com.smartgwt.client.browser.window.*;
import com.smartgwt.client.rpc.*;
import com.smartgwt.client.ai.*;
import com.smartgwt.client.callbacks.*;
import com.smartgwt.client.tools.*;
import com.smartgwt.client.bean.*;
import com.smartgwt.client.widgets.*;
import com.smartgwt.client.widgets.ai.*;
import com.smartgwt.client.widgets.events.*;
import com.smartgwt.client.widgets.form.*;
import com.smartgwt.client.widgets.form.validator.*;
import com.smartgwt.client.widgets.form.fields.*;
import com.smartgwt.client.widgets.tile.*;
import com.smartgwt.client.widgets.tile.events.*;
import com.smartgwt.client.widgets.grid.*;
import com.smartgwt.client.widgets.grid.events.*;
import com.smartgwt.client.widgets.chart.*;
import com.smartgwt.client.widgets.layout.*;
import com.smartgwt.client.widgets.layout.events.*;
import com.smartgwt.client.widgets.menu.*;
import com.smartgwt.client.widgets.tour.*;
import com.smartgwt.client.widgets.notify.*;
import com.smartgwt.client.widgets.rte.*;
import com.smartgwt.client.widgets.rte.events.*;
import com.smartgwt.client.widgets.ace.*;
import com.smartgwt.client.widgets.ace.events.*;
import com.smartgwt.client.widgets.tab.*;
import com.smartgwt.client.widgets.toolbar.*;
import com.smartgwt.client.widgets.tree.*;
import com.smartgwt.client.widgets.tree.events.*;
import com.smartgwt.client.widgets.tableview.*;
import com.smartgwt.client.widgets.viewer.*;
import com.smartgwt.client.widgets.calendar.*;
import com.smartgwt.client.widgets.calendar.events.*;
import com.smartgwt.client.widgets.cube.*;
import com.smartgwt.client.widgets.notify.*;
import com.smartgwt.client.widgets.drawing.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gwt.event.shared.*;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.core.client.JavaScriptObject;

import com.smartgwt.client.util.*;
import com.smartgwt.client.util.events.*;
import com.smartgwt.client.util.workflow.*;
import com.smartgwt.client.util.workflow.Process; // required to override java.lang.Process
import com.smartgwt.client.util.tour.*;

import com.smartgwt.logicalstructure.core.*;
import com.smartgwt.logicalstructure.widgets.*;
import com.smartgwt.logicalstructure.widgets.drawing.*;
import com.smartgwt.logicalstructure.widgets.plugins.*;
import com.smartgwt.logicalstructure.widgets.form.*;
import com.smartgwt.logicalstructure.widgets.tile.*;
import com.smartgwt.logicalstructure.widgets.grid.*;
import com.smartgwt.logicalstructure.widgets.chart.*;
import com.smartgwt.logicalstructure.widgets.layout.*;
import com.smartgwt.logicalstructure.widgets.menu.*;
import com.smartgwt.logicalstructure.widgets.rte.*;
import com.smartgwt.logicalstructure.widgets.ace.*;
import com.smartgwt.logicalstructure.widgets.tab.*;
import com.smartgwt.logicalstructure.widgets.tableview.*;
import com.smartgwt.logicalstructure.widgets.toolbar.*;
import com.smartgwt.logicalstructure.widgets.tree.*;
import com.smartgwt.logicalstructure.widgets.viewer.*;
import com.smartgwt.logicalstructure.widgets.calendar.*;
import com.smartgwt.logicalstructure.widgets.cube.*;
import com.smartgwt.logicalstructure.widgets.tools.*;
import com.smartgwt.logicalstructure.widgets.tour.*;
import com.smartgwt.logicalstructure.widgets.ai.*;

/**
* <b><i><span style='color:red'>Note: this is currently an experimental feature and not
* covered by normal support guarantees. See {@link com.smartgwt.client.docs.Experimental
* Experimental Features} for more information.</span></i></b><br><br>
 * Selenese Recorder is a tool that can automatically record browser interaction as Selenese
 *  Commands, or play back Selenese previously recorded by launching a separate browser using
 *  Selenium APIs.  Rather than creating your own instance of the recorder, you can also use it
 *  from the the {@link com.smartgwt.client.docs.Debugging Developer Console}, which avoids having to integrate it
 *  with your application.
 *  <p>
 *  <h3>Initial Setup</h3>
 *  <p>
 *  To enable the Selenium Recorder to play back your Selenese, you must first configure the
 *  server with the location of the Gecko (Firefox) and/or Chrome driver on your system.  To do
 *  this, edit the {@link com.smartgwt.client.docs.Server_properties server properties} file to uncomment the
 *  following lines and define the associated properties to point at your driver executables:
 *  <pre>
 *  #system.setProperty.webdriver.gecko.driver: /usr/local/bin/geckodriver
 *  #system.setProperty.webdriver.chrome.driver: /usr/local/bin/chromedriver
 *  #system.setProperty.webdriver.edge.driver: /usr/local/bin/msedgedriver</pre>
 *  This will also make the "Recorder" tab visible in the
 *  {@link com.smartgwt.client.docs.Debugging Developer Console}, to allow you to use its Selenium Recorder
 *  targeting your main browser window without having to create or manage a
 *  <code>SeleneseRecorder</code> widget in your app.
 *  <p>
 *  If you want Selenium to launch from a different server than your page is loaded from, you
 *  can set {@link com.smartgwt.client.tools.SeleneseRecorder#getSeleneseRunnerActionURL seleneseRunnerActionURL} to the
 *  desired URL (or set the server property <code>DeveloperConsole.webdriver.actionURL</code> to
 *  affect the Developer Console's Recorder tab).  This will allow you to run Selenium locally
 *  and open a browser directly on your machine, even if your primary Smart GWT server is
 *  remote.
 *  <p>
 *  <h3>Recording Selenese</h3>
 *  <p>
 *  To record Selenese against the target page, make sure that the "Capture Commands" checkbox
 *  at the bottom of the tool is checked, and then interact normally with the target page.  Your
 *  clicks and typing will automatically be converted into Selenese commands and appear in the
 *  grid in the tool's main window.  Recorded commands will appear on a delay to allow double
 *  clicks and other complex interaction to be properly captured.
 *  <p>
 *  After you capture Selenese from such interaction, you can edit commands, reorder them, or
 *  even delete one or more commands from the grid as needed.  Options to copy and paste an
 *  existing command or add a new custom command to the script are available by
 *  context-clicking.  You can also "mass edit" certain fields for a set of selected Selenese
 *  command records via context click.
 *  <p>
 *  To save your script, type something in the "Test Name" box (or accept the default), and
 *  click "Export".  This will cause your browser to save the script as a downloaded file.
 *  Depending on your browser configuration, you may be prompted for where to save the script.
 *  <p>
 *  <b>Note:</b> support for recording is currently limited, with automatic command creation
 *  only working for clicks and typing.
 *  <p>
 *  <h3>Playing back Selenese</h3>
 *  <p>
 *  You can play back the current recorded script, or one you load via the tool's "Choose File"
 *  button.  To start playback, click the "Run" button.  This will open a new browser instance
 *  and begin to run the script in that browser.  While it's running, you can click the "Stop"
 *  button to abort execution, or "Pause" (the changed title of the "Run" button), to pause
 *  execution and allow for stepping.  In stepping mode, click "Step" to advance foward by one
 *  command so you can debug any issues or hand-modify the widget state or browser data.  When
 *  you're done, click "Go" to resume automatic execution of the script.  Use the "Settings"
 *  dialog accessible via the same-titled button to modify the base URL for your script or
 *  select which browser to run (Chrome/Firefox/MS Edge/IE).
 *  <p>
 *  As the script executes, each command in the grid will be highlighted green if the command
 *  succeeds, or red if it fails.  The output of each command also gets stored into the
 *  "Output" grid column where you can easily check it.  When the script is done, the circular
 *  indicator icon on the bottom right of the tool will be set green to indicate that the 
 *  script succeeded, or red to indicate that it failed, and you can download or view a report
 *  by clicking on the "Get Report" button.
 *  <p>
 *  For each command, you can configure whether it will stop the entire script if it fails,
 *  or the failure ignored (but still indicated in the highlighting and history), or the failing
 *  command reattempted.  The maximum number of tries can be set globally from the "Settings"
 *  dialog by changing "Max Retry Attempts".  The "screenshot" (three state) checkbox allows
 *  you to configure whether a screenshot is always taken for that command (checked), never
 *  taken (unchecked), or taken only upon failure (filled).  Screenshots can be downloaded by
 *  clicking on the icon in the record (if present) to the right of the "Output" column.
 *  <p>
 *  As each script command executes, an implicit Selenium "waitFor..." command is automatically
 *  run by default to wait for the target canvas or canvii to become
 *  {@link com.smartgwt.client.util.AutoTest#isSystemDone "ready"}, meaning that the target is not queued for redrawing,
 *  has no pending fetch or scroll, server communications, etc.  You can modify this behavior
 *  (if you know it's not needed for one or more commands) by unchecking the "Wait For" grid
 *  column.  The default global behavior can also be configured via the "Settings" dialog.
 *  <p>
 *  <h3>Running a Test Suite</h3>
 *  <p>
 *  While the Selenese Recorder only supports a single Selenese script, you can run a Selenium
 *  suite file using the 
 * <a href='https://smartclient.com/smartgwtee-latest/server/javadoc/com/isomorphic/webdriver/SeleneseRunner.html'
 * target='_blank'>SeleneseRunner</a>
 *  command line tool.  To see usage information, invoke the <code>SeleneseRunner</code> main
 *  Java class, picking up the provided Isomorphic and thirdy-party JARS:
 *  <pre>
 *  java -cp "../lib/*" com.isomorphic.webdriver.SeleneseRunner</pre>
 *  To run a suite file that targets a locally-deployed Smart GWT Showcase, for example, you
 *  can then issue the following command line, modifying the browser driver path as per your
 *  local installation:
 *  <pre style="white-space:pre-wrap;margin:10px 0px 10px 50px;text-indent:-50px">
 * java -Dwebdriver.gecko.driver=/usr/bin/local/geckodriver -cp "../lib/*" com.isomorphic.webdriver.SeleneseRunner
 * http://localhost:8080/ -j report.xml suite.html</pre>
 *  where <code>suite.html</code> is the suite to run, and <code>report.xml</code> is the name
 *  of file into which the output should be reported in XML format.
 * @see com.smartgwt.client.docs.Experimental Experimental Features
 */
@BeanFactory.FrameworkClass
@BeanFactory.ScClassName("SeleneseRecorder")
public class SeleneseRecorder extends VLayout {

    public static SeleneseRecorder getOrCreateRef(JavaScriptObject jsObj) {
        if (jsObj == null) return null;
        final BaseWidget refInstance = BaseWidget.getRef(jsObj);
        if (refInstance == null) {
            return new SeleneseRecorder(jsObj);
        } else {
            assert refInstance instanceof SeleneseRecorder;
            return (SeleneseRecorder)refInstance;
        }
    }
        


    /**
     * Changes the defaults for Canvas AutoChildren named <code>autoChildName</code>.
     *
     * @param autoChildName name of an AutoChild to customize the defaults for.
     * @param defaults Canvas defaults to apply. These defaults override any existing properties
     * without destroying or wiping out non-overridden properties.  For usage tips on this
     * param, see {@link com.smartgwt.client.docs.SGWTProperties}.
     * @see com.smartgwt.client.docs.AutoChildUsage
     */
    public static native void changeAutoChildDefaults(String autoChildName, Canvas defaults) /*-{
        if (defaults.@com.smartgwt.client.widgets.BaseWidget::isCreated()()) {
            @com.smartgwt.client.util.ConfigUtil::warnOfPreConfigInstantiation(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)(SeleneseRecorder.@java.lang.Object::getClass()(), "changeAutoChildDefaults", "Canvas");
        }
        defaults.@com.smartgwt.client.widgets.BaseWidget::setConfigOnly(Z)(true);
        var cleanDefaultsJS = @com.smartgwt.client.util.JSOHelper::cleanProperties(Lcom/google/gwt/core/client/JavaScriptObject;Z)(defaults.@com.smartgwt.client.widgets.BaseWidget::getConfig()(), true);
        $wnd.isc.SeleneseRecorder.changeDefaults(autoChildName + "Defaults", cleanDefaultsJS);
    }-*/;

    /**
     * Changes the defaults for FormItem AutoChildren named <code>autoChildName</code>.
     *
     * @param autoChildName name of an AutoChild to customize the defaults for.
     * @param defaults FormItem defaults to apply. These defaults override any existing properties
     * without destroying or wiping out non-overridden properties.  For usage tips on this
     * param, see {@link com.smartgwt.client.docs.SGWTProperties}.
     * @see com.smartgwt.client.docs.AutoChildUsage
     */
    public static native void changeAutoChildDefaults(String autoChildName, FormItem defaults) /*-{
        if (defaults.@com.smartgwt.client.widgets.form.fields.FormItem::isCreated()()) {
            @com.smartgwt.client.util.ConfigUtil::warnOfPreConfigInstantiation(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)(SeleneseRecorder.@java.lang.Object::getClass()(), "changeAutoChildDefaults", "FormItem");
        }
        defaults.@com.smartgwt.client.widgets.form.fields.FormItem::setConfigOnly(Z)(true);
    	var cleanDefaultsJS = defaults.@com.smartgwt.client.widgets.form.fields.FormItem::getEditorTypeConfig()();
        $wnd.isc.SeleneseRecorder.changeDefaults(autoChildName + "Defaults", cleanDefaultsJS);
    }-*/;

    public SeleneseRecorder(){
        scClassName = "SeleneseRecorder";
    }

    public SeleneseRecorder(JavaScriptObject jsObj){
        scClassName = "SeleneseRecorder";
        setJavaScriptObject(jsObj);
    }

    protected native JavaScriptObject create()/*-{
        var config = this.@com.smartgwt.client.widgets.BaseWidget::getConfig()();
        var scClassName = this.@com.smartgwt.client.widgets.BaseWidget::scClassName;
        var widget = $wnd.isc[scClassName].create(config);
        if ($wnd.isc.keepGlobals) this.@com.smartgwt.client.widgets.BaseWidget::internalSetID(Lcom/google/gwt/core/client/JavaScriptObject;)(widget);
        this.@com.smartgwt.client.widgets.BaseWidget::doInit()();
        return widget;
    }-*/;


    // ********************* Properties / Attributes ***********************

    /**
     * URL of the Smart GWT server to use to launch and run Selenium.  Left unset, the recorder will attermpt to run on the
     * server from which the framework was loaded.
     *
     * @param seleneseRunnerActionURL New seleneseRunnerActionURL value. Default value is null
     * @return {@link com.smartgwt.client.tools.SeleneseRecorder SeleneseRecorder} instance, for chaining setter calls
     * @throws IllegalStateException this property cannot be changed after the component has been created
     * @see com.smartgwt.client.rpc.RPCManager#actionURL
     * @see com.smartgwt.client.docs.URL URL 
     */
    public SeleneseRecorder setSeleneseRunnerActionURL(String seleneseRunnerActionURL)  throws IllegalStateException {
        return (SeleneseRecorder)setAttribute("seleneseRunnerActionURL", seleneseRunnerActionURL, false);
    }

    /**
     * URL of the Smart GWT server to use to launch and run Selenium.  Left unset, the recorder will attermpt to run on the
     * server from which the framework was loaded.
     *
     * @return Current seleneseRunnerActionURL value. Default value is null
     * @see com.smartgwt.client.rpc.RPCManager#actionURL
     * @see com.smartgwt.client.docs.URL URL 
     */
    public String getSeleneseRunnerActionURL()  {
        return getAttributeAsString("seleneseRunnerActionURL");
    }
    

    /**
     * Whether to always expand the command grid horizontally to match any overflow in the recorder toolbar.  To look good when
     * the tool is placed inside an app, this is enabled by default, but it can be turned off if the tool occupies the entire
     * browser width, where you might prefer that viewport overflow only clips the toolbar, and not the grid.
     *
     * @param sizeToControlsOverflow New sizeToControlsOverflow value. Default value is true
     * @return {@link com.smartgwt.client.tools.SeleneseRecorder SeleneseRecorder} instance, for chaining setter calls
     * @throws IllegalStateException this property cannot be changed after the component has been created
     */
    public SeleneseRecorder setSizeToControlsOverflow(boolean sizeToControlsOverflow)  throws IllegalStateException {
        return (SeleneseRecorder)setAttribute("sizeToControlsOverflow", sizeToControlsOverflow, false);
    }

    /**
     * Whether to always expand the command grid horizontally to match any overflow in the recorder toolbar.  To look good when
     * the tool is placed inside an app, this is enabled by default, but it can be turned off if the tool occupies the entire
     * browser width, where you might prefer that viewport overflow only clips the toolbar, and not the grid.
     *
     * @return Current sizeToControlsOverflow value. Default value is true
     */
    public boolean getSizeToControlsOverflow()  {
        Boolean result = getAttributeAsBoolean("sizeToControlsOverflow");
        return result == null ? true : result;
    }
    

    /**
     * Canvii that you want to ignore when capturing Selenese.  Any canvas from this list, or one of its descendants or menus,
     * as determined by the locator parent chain, will be excluded.  This allows you integrate the recorder into a page without
     * having interaction with the tool itself generate unwanted recording. <p> The tool widget itself and dialogs it spawns
     * will be automatically ignored, so this is required only to specify additional widgets to ignore, such as a window that
     * contains the tool, related forms, a launch button, etc.
     *
     * @param widgetsToIgnore New widgetsToIgnore value. Default value is null
     * @return {@link com.smartgwt.client.tools.SeleneseRecorder SeleneseRecorder} instance, for chaining setter calls
     * @throws IllegalStateException this property cannot be changed after the component has been created
     */
    public SeleneseRecorder setWidgetsToIgnore(Canvas... widgetsToIgnore)  throws IllegalStateException {
        return (SeleneseRecorder)setAttribute("widgetsToIgnore", widgetsToIgnore, false);
    }

    /**
     * Canvii that you want to ignore when capturing Selenese.  Any canvas from this list, or one of its descendants or menus,
     * as determined by the locator parent chain, will be excluded.  This allows you integrate the recorder into a page without
     * having interaction with the tool itself generate unwanted recording. <p> The tool widget itself and dialogs it spawns
     * will be automatically ignored, so this is required only to specify additional widgets to ignore, such as a window that
     * contains the tool, related forms, a launch button, etc.
     *
     * @return Current widgetsToIgnore value. Default value is null
     */
    public Canvas[] getWidgetsToIgnore()  {
        return com.smartgwt.client.util.ConvertTo.arrayOfCanvas(getAttributeAsJavaScriptObject("widgetsToIgnore"));
    }
    

    // ********************* Methods ***********************

    // ********************* Static Methods ***********************

    /** 
     * Class level method to set the default properties of this class.  If set, then all
     * existing and subsequently created instances of this class will automatically have
     * default properties corresponding to
     * the properties set on the SmartGWT class instance passed to this function before its
     * underlying SmartClient JS object was created.
     * This is a powerful feature that eliminates the need for users to create a separate
     * hierarchy of subclasses that only alter the default properties of this class. Can also
     * be used for skinning / styling purposes.  <P> <b>Note:</b> This method is intended for
     * setting default attributes only and will affect all instances of the underlying class
     * (including those automatically generated in JavaScript).  This method should not be used
     * to apply standard EventHandlers or override methods for a class - use a custom subclass
     * instead.  Calling this method after instances have been created can result in undefined
     * behavior, since it bypasses any setters and a class instance may have already examined 
     * a particular property and not be expecting any changes through this route.
     *
     * @param seleneseRecorderProperties properties that should be used as new defaults when instances of this class are created
     * @see com.smartgwt.client.docs.SGWTProperties
     */
    public static native void setDefaultProperties(SeleneseRecorder seleneseRecorderProperties) /*-{
        if (seleneseRecorderProperties.@com.smartgwt.client.widgets.BaseWidget::isCreated()()) {
            @com.smartgwt.client.util.ConfigUtil::warnOfPreConfigInstantiation(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)(SeleneseRecorder.@java.lang.Object::getClass()(), "setDefaultProperties", seleneseRecorderProperties.@java.lang.Object::getClass()());
        }
        seleneseRecorderProperties.@com.smartgwt.client.widgets.BaseWidget::setConfigOnly(Z)(true);
    	var properties = seleneseRecorderProperties.@com.smartgwt.client.widgets.BaseWidget::getConfig()();
        properties = @com.smartgwt.client.util.JSOHelper::cleanProperties(Lcom/google/gwt/core/client/JavaScriptObject;Z)(properties,true);
        $wnd.isc.SeleneseRecorder.addProperties(properties);
    }-*/;

    // ***********************************************************

    /**
     * Setter implementing the {@link com.smartgwt.client.core.LogicalStructure} interface,
     * which supports Eclipse's logical structure debugging facility.
     */
    public LogicalStructureObject setLogicalStructure(SeleneseRecorderLogicalStructure s) {
        super.setLogicalStructure(s);
        try {
            s.seleneseRunnerActionURL = getAttributeAsString("seleneseRunnerActionURL");
        } catch (Throwable t) {
            s.logicalStructureErrors += "SeleneseRecorder.seleneseRunnerActionURL:" + t.getMessage() + "\n";
        }
        try {
            s.sizeToControlsOverflow = getAttributeAsString("sizeToControlsOverflow");
        } catch (Throwable t) {
            s.logicalStructureErrors += "SeleneseRecorder.sizeToControlsOverflow:" + t.getMessage() + "\n";
        }
        try {
            s.widgetsToIgnore = getWidgetsToIgnore();
        } catch (Throwable t) {
            s.logicalStructureErrors += "SeleneseRecorder.widgetsToIgnoreArray:" + t.getMessage() + "\n";
        }
        return s;
    }

    /**
     * Getter implementing the {@link com.smartgwt.client.core.LogicalStructure} interface,
     * which supports Eclipse's logical structure debugging facility.
     */
    public LogicalStructureObject getLogicalStructure() {
        SeleneseRecorderLogicalStructure s = new SeleneseRecorderLogicalStructure();
        setLogicalStructure(s);
        return s;
    }
}
