
package org.openspml.browser;

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.List;

import org.openspml.message.*;

public class ModifyPanel extends RequestPanel {

    IdentifierPanel _identifier;
    JLabel _objectclass;
    JButton _newAttribute;
    JButton _clear;
    JButton _clearValues;
    JButton _load;
    AttributesPanel _attributes;
    ModifyInstaller _installer;
    ObjectClassDefinition _class;

    public ModifyPanel(State s) {
        super(s);
        _installer = new ModifyInstaller();

        addRequestIdPanel();

        _identifier = new IdentifierPanel();
        _identifier.addActionListener(this);
        _form.add("Identifier", _identifier);

        _objectclass = new JLabel();
        _form.add("Object Class", _objectclass);

        _attributes = new AttributesPanel();
        _attributes.setModifications(true);
        _attributes.addActionListener(this);
        _attributes.addRow();
        _form.add("Modifications", _attributes);

        JPanel p = new JPanel();
        p.setLayout(new LinearLayout());
        _form.add("", p);

        _newAttribute = new JButton("New Modification");
        _newAttribute.addActionListener(this);
        p.add(_newAttribute);
        _clear = new JButton("Clear Form");
        _clear.addActionListener(this);
        p.add(_clear);
        _clearValues = new JButton("Clear Values");
        _clearValues.addActionListener(this);
        p.add(_clearValues);
        _load = new JButton("Read Current");
        _load.addActionListener(this);
        p.add(_load);
        _submit = new JButton("Submit");
        _submit.addActionListener(this);
        p.add(_submit);
    }

    public void actionPerformed(ActionEvent e) {
        
        Object src = e.getSource();

        if (src == _newAttribute) {
            _attributes.addRow();
            resizeSplit();
        }
        else if (src == _clear) {
            _identifier.setText(null);
            setObjectClass(null);
            refreshAttributes();
            refreshRequest();
        }
        else if (src == _clearValues) {
            refreshAttributes();
            refreshRequest();
        }
        else if (src == _load) {
            loadCurrent();
        }
        else {
            SpmlRequest req = refreshRequest();
            if (req != null && src == _submit)
                _state.doRequest(req, _installer);
        }
    }

    public void setObjectClass(ObjectClassDefinition def) {
        _class = def;
        if (def != null)
            _objectclass.setText(def.getName());
        else
            _objectclass.setText(null);
    }

    public Installer getInstaller() {
        return _installer;
    }

    public SpmlRequest getRequest() {

        ModifyRequest req = new ModifyRequest();

        req.setIdentifier(_identifier.getIdentifier());
        req.setRequestId(_reqid.getRequestId());
        req.setAsynchronous(_reqid.isAsync());

        // pass true to filter null values
        // !! will need a way to send null values
        req.setModifications(_attributes.getAttributes(true));

        return req;
    }

    private void loadCurrent() {

        Identifier id = _identifier.getIdentifier();
        if (id != null) {
            SearchRequest sr = new SearchRequest();

            // technically not allowed to do this
            sr.setIdentifier(id);
            
            // kludge: lighthouse requires a signal to return
            // all of the available attributes rather than just
            // the summary attributes, perhaps it should just
            // assume that if an explicit identity is passed, or
            // of there is only one row in the result that it can
            // return all of them?
            sr.addAttribute("view");

            // if we haven't loaded the schema yet, get it too
            if (_state.getSchemas() != null)
                _state.doRequest(sr, _installer);
            else {
                BatchRequest batch = new BatchRequest();
                batch.addRequest(new SchemaRequest());
                batch.addRequest(sr);
                _state.doRequest(batch, _installer);
            }
        }
    }

    private void refreshAttributes() {

        List attdefs = null;
        
        if (_class != null)
            attdefs = _class.getAttributeDefinitions();

        // !! any classes with variable attributes?  
        // what if we have attributes but no class?

        _attributes.reset(attdefs);
        resizeSplit();
    }

    private void assimilateLoad(SearchResponse res) {

        List results = res.getResults();
        if (results == null || results.size() == 0) {
            // might be other errors...
            SwingUtil.displayError("Invalid identifier");
        }
        else if (results.size() > 1)
            SwingUtil.displayError("Ambiguous identifier");

        else {
            _attributes.reset();
            SearchResult r = (SearchResult)results.get(0);
            Map atts = r.getAttributeMap();

            // Lighthouse should be returning this, would be
            // nice if we could depend on it.
            String oc = (String)r.getAttributeValue("objectclass");
            ObjectClassDefinition def = null;
            if (oc == null)
                def = _state.getDefaultObjectClass();
            else
                def = _state.getObjectClass(oc);

            // display this if we know
            setObjectClass(def);

            if (def != null) {
                List attdefs = def.getAttributeDefinitions();
                if (attdefs != null) {
                    Iterator it = attdefs.iterator();
                    while (it.hasNext()) {
                        AttributeDefinition attdef = 
                            (AttributeDefinition)it.next();
                        String name = attdef.getName();
			// filter objectclass, we handle it as a 
			// non-editable label above
                        if (name != null && !name.equals("objectclass")) {
                            Object value = null;
                            if (atts != null)
                                value = atts.remove(name);
                            _attributes.addRow(name, value);
                        }
                    }
                }
            }
            
            // anything left over in the query result map 
            // is extra stuff that wasn't in the schema
            // filter out some implicit things, should mark these
            // in some way if we had a class definition

            if (atts != null) {
                atts.remove("objectclass");
                Iterator it = atts.entrySet().iterator();
                while (it.hasNext()) {
                    Map.Entry entry = (Map.Entry)it.next();
                    String name = (String)entry.getKey();
                    Object value = entry.getValue();
                    _attributes.addRow(name, value);
                }
            }

            resizeSplit();
        }
    }

    /**
     * Handler eventually called via State.doRequest in the Swing
     * event thread to install a response into the GUI.
     */
    private class ModifyInstaller extends Installer {

        public void install(SpmlResponse response) {
            
            if (response instanceof SearchResponse) {
                // must have run the Load query
                assimilateLoad((SearchResponse)response);
                refreshRequest();
            }
            else if (response instanceof BatchResponse) {
                // must have run the combo Schema/Load query
                BatchResponse br = (BatchResponse)response;
                SearchResponse sr = (SearchResponse)
                    br.getResponse(SearchResponse.class);
                if (sr != null) {
                    assimilateLoad(sr);
                    refreshRequest();
                }
            }
            else {
                refreshResponse(response);
            }
        }
    }

}
