package org.drools.brms.client.modeldriven.ui;
/*
 * Copyright 2005 JBoss Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */



import org.drools.brms.client.common.DirtyableComposite;
import org.drools.brms.client.common.DirtyableFlexTable;
import org.drools.brms.client.common.FieldEditListener;
import org.drools.brms.client.common.FormStylePopup;
import org.drools.brms.client.common.ImageButton;
import org.drools.brms.client.common.InfoPopup;
import org.drools.brms.client.common.Lbl;
import org.drools.brms.client.common.YesNoDialog;
import org.drools.brms.client.modeldriven.HumanReadable;
import org.drools.brms.client.modeldriven.SuggestionCompletionEngine;
import org.drools.brms.client.modeldriven.brl.ActionFieldValue;
import org.drools.brms.client.modeldriven.brl.ActionSetField;
import org.drools.brms.client.modeldriven.brl.ActionUpdateField;
import org.drools.brms.client.modeldriven.brl.FactPattern;
import org.drools.brms.client.modeldriven.brl.RuleModel;
import org.drools.brms.client.modeldriven.brl.SingleFieldConstraint;

import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;

/**
 * This widget is for setting fields on a bound fact or global variable.
 *
 * @author Michael Neale
 */
public class ActionSetFieldWidget extends DirtyableComposite {

    final private ActionSetField model;
    final private SuggestionCompletionEngine completions;
    final private DirtyableFlexTable layout;
    private boolean isBoundFact = false;

    final private String[] fieldCompletions;
    final private RuleModeller modeller;
    final private String variableClass;


    public ActionSetFieldWidget(RuleModeller mod,  ActionSetField set, SuggestionCompletionEngine com) {
        this.model = set;
        this.completions = com;
        this.layout = new DirtyableFlexTable();
        this.modeller = mod;

        layout.setStyleName( "model-builderInner-Background" );
        if (completions.isGlobalVariable( set.variable )) {
            this.fieldCompletions = completions.getFieldCompletionsForGlobalVariable( set.variable );
            this.variableClass = (String) completions.globalTypes.get( set.variable );
        } else {
            FactPattern pattern = mod.getModel().getBoundFact( set.variable );
            this.fieldCompletions = completions.getFieldCompletions( pattern.factType );
            this.variableClass = pattern.factType;
            this.isBoundFact = true;
        }

        doLayout();

        initWidget( this.layout );
    }


    private void doLayout() {
        layout.clear();
        layout.setWidget( 0, 0, getSetterLabel() );

        DirtyableFlexTable inner = new DirtyableFlexTable();

        for ( int i = 0; i < model.fieldValues.length; i++ ) {
            ActionFieldValue val = model.fieldValues[i];

            inner.setWidget( i, 0, fieldSelector(val) );
            inner.setWidget( i, 1, valueEditor(val) );
            final int idx = i;
            Image remove = new ImageButton("images/delete_item_small.gif");
            remove.addClickListener( new ClickListener() {
                public void onClick(Widget w) {
                    YesNoDialog diag = new YesNoDialog("Remove this item?", new Command() {
                        public void execute() {
                            model.removeField( idx );
                            modeller.refreshWidget();
                        }
                    });
                    diag.setPopupPosition( w.getAbsoluteLeft(), w.getAbsoluteTop() );
                    diag.show();
                }
            });
            inner.setWidget( i, 2, remove );
        }

        layout.setWidget( 0, 1, inner );


    }


    private Widget getSetterLabel() {

        HorizontalPanel horiz = new HorizontalPanel();


        Image edit = new ImageButton("images/add_field_to_fact.gif");
        edit.setTitle( "Add another field to this so you can set its value." );
        edit.addClickListener( new ClickListener() {
            public void onClick(Widget w) {
                showAddFieldPopup(w);
            }
        } );
        String modifyType = "set";
        if (this.model instanceof ActionUpdateField) {
            modifyType = "modify";
        }
        horiz.add( new Lbl(HumanReadable.getActionDisplayName(modifyType) + " [" + model.variable + "]", "modeller-action-Label") );
        horiz.add( edit );

        return horiz;
    }


    protected void showAddFieldPopup(Widget w) {
        final FormStylePopup popup = new FormStylePopup("images/newex_wiz.gif", "Add a field");
        popup.setStyleName( "ks-popups-Popup" );
        final ListBox box = new ListBox();
        box.addItem( "..." );

        for ( int i = 0; i < fieldCompletions.length; i++ ) {
            box.addItem( fieldCompletions[i] );
        }

        box.setSelectedIndex( 0 );

        popup.addAttribute( "Add field", box );
        box.addChangeListener( new ChangeListener() {
            public void onChange(Widget w) {
                String fieldName = box.getItemText( box.getSelectedIndex() );

                String fieldType = completions.getFieldType( variableClass, fieldName );
                model.addFieldValue( new ActionFieldValue( fieldName, "", fieldType ) );
                modeller.refreshWidget();
                popup.hide();
            }
        });



        popup.setPopupPosition( w.getAbsoluteLeft(), w.getAbsoluteTop() );
        popup.show();

    }


    private Widget valueEditor(final ActionFieldValue val) {

        String enumKey = this.variableClass + "." + val.field;
        if (this.completions.dataEnumLists.containsKey( enumKey )) {
            return ConstraintValueEditor.enumDropDown( val.value, new ConstraintValueEditor.ValueChanged() {
                public void valueChanged(String newValue) {
                    val.value = newValue;
                }
            }, (String[]) this.completions.dataEnumLists.get( enumKey ) );
        } else {

            SimplePanel panel = new SimplePanel();

            if (val.value == null || val.value.equals( "" )) {
                //have to show a choice
                Image clickme = new ImageButton( "images/edit.gif", "Click to choose if it is a literal value or a formula" );
                clickme.addClickListener( new ClickListener() {
                    public void onClick(Widget w) {
                        showTypeChoice( w, val );
                    }
                } );
                panel.add( clickme );
            } else {

                if (val.value.startsWith( "=" )) {
                    HorizontalPanel horiz = new HorizontalPanel();

                    final TextBox box = new TextBox();
                    box.setText( val.value.substring( 1 ) );
                    if (val.value.length() != 0) {
                        box.setVisibleLength( val.value.length() );
                    }

                    box.addChangeListener( new ChangeListener() {

                        public void onChange(Widget w) {
                            val.value = "=" + box.getText();
                        }

                    });

                    box.addKeyboardListener( new FieldEditListener(new Command() {
                        public void execute() {
                            box.setVisibleLength( box.getText().length() );
                        }
                    }));

                    String msg = "This is a formula expression which will evaluate to a value.";

                    Image img = new Image("images/function_assets.gif");
                    img.setTitle( msg );
                    box.setTitle( msg );
                    horiz.add( img );
                    horiz.add( box );

                    panel.add( horiz );
                }else {

                    final TextBox box = new TextBox();
                    box.setText( val.value );
                    if (val.value.length() != 0) {
                        box.setVisibleLength( val.value.length() );
                    }


                if (val.type.equals( SuggestionCompletionEngine.TYPE_NUMERIC )) {
                    box.addKeyboardListener( getNumericFilter( box ));
                }

                box.addChangeListener( new ChangeListener() {
                    public void onChange(Widget w) {
                        val.value = box.getText();
                    }
                });

                    box.addKeyboardListener( new FieldEditListener(new Command() {
                        public void execute() {
                            box.setVisibleLength( box.getText().length() );
                        }
                    }));
                panel.add( box );
                }
            }
            return panel;
        }
    }


    protected void showTypeChoice(Widget w,
                                  final ActionFieldValue val) {
        final FormStylePopup form = new FormStylePopup( "images/newex_wiz.gif",
        "Field value" );

        Button lit = new Button( "Literal value" );
        lit.addClickListener( new ClickListener() {
            public void onClick(Widget w) {
                val.value = " ";
                doLayout();
                form.hide();
            }

        } );

        HorizontalPanel horiz = new HorizontalPanel();
        horiz.add( lit );
        horiz.add( new InfoPopup( "Literal",
                                  "A literal value means the " + "constraint is directly against the value that you type (ie. what you see on screen)." ) );
        form.addAttribute( "Literal value:", horiz);

        Button formula = new Button( "New formula" );
        lit.addClickListener( new ClickListener() {
            public void onClick(Widget w) {
                val.value = "=";
                doLayout();
                form.hide();
            }

        } );


        horiz = new HorizontalPanel();
        horiz.add( formula );
        horiz.add( new InfoPopup( "A formula",
                                  "A formula is an expression that calculates and returns a value (that value will be used to set the field value)." ) );

        form.addAttribute( "A formula:", horiz);
        form.setPopupPosition( w.getAbsoluteLeft(), w.getAbsoluteTop() );
        form.show();



    }


    /**
     * This will return a keyboard listener for field setters, which
     * will obey numeric conventions - it will also allow formulas
     * (a formula is when the first value is a "=" which means
     * it is meant to be taken as the user typed)
     */
    public static KeyboardListener getNumericFilter(final TextBox box) {
        return new KeyboardListener() {

            public void onKeyDown(Widget arg0, char arg1, int arg2) {

            }

            public void onKeyPress(Widget w, char c, int i) {
                if (Character.isLetter( c ) && c != '='
                    && !(box.getText().startsWith( "=" ))) {
                    ((TextBox) w).cancelKey();
                }
            }

            public void onKeyUp(Widget arg0, char arg1, int arg2) {
            }

        };
    }


    private Widget fieldSelector(final ActionFieldValue val) {
        return new Label(val.field);
    }

    /**
     * This returns true if the values being set are on a fact.
     */
    public boolean isBoundFact() {
        return isBoundFact;
    }

    public boolean isDirty() {
        return layout.hasDirty();
    }

}