/*
 * MyArrayEditor.java
 *
 * Created on October 26, 2007, 5:32 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package usda.weru.mcrew;

import com.klg.jclass.cell.JCCellEditor;
import com.klg.jclass.cell.JCCellEditorEvent;
import com.klg.jclass.cell.JCCellEditorListener;
import com.klg.jclass.cell.JCCellEditorSupport;
import com.klg.jclass.cell.JCCellInfo;
import com.klg.jclass.cell.JCCellRenderer;
import com.klg.jclass.cell.JCKeyModifier;
import com.klg.jclass.table.CellStyleModel;
import com.klg.jclass.table.EditableTableDataModel;
import com.klg.jclass.table.JCCellStyle;
import com.klg.jclass.table.JCTableCellInfo;
import com.klg.jclass.table.JCTable;
import com.klg.jclass.table.JCTableEnum;
import com.klg.jclass.table.data.AbstractDataSource;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyEvent;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import usda.weru.mcrew.gui.ArrayEditor_n;
import java.util.Hashtable;

/**
 *
 * @author Joseph Levin
 */
public class MyArrayEditor extends JPanel implements JCCellEditor{
    private static final long serialVersionUID = 1L;
    
    private MyArrayRenderer c_renderer;
    private JCCellEditor c_editor;
    private Object[] c_values;
    private Object[] c_newValues;
    private boolean c_firstDraw;
    private  ArrayEditorPopup c_arraypopup;
    private JCCellEditorSupport  c_editorSupport;
    private Queue <JCCellEditorListener> c_listenerQueue;
    private Component c_owner;
    private InputLimits c_limits;
    private int location;
    
    private boolean c_recieving;//This boolean will determine whether our editor
                                //has been painted and is waiting to accept new data.
    
    private int c_width;
    private CellStyleModel c_cellStyle;
    
    
    private boolean c_isArray = false;
    /** Creates a new instance of MyArrayEditor
	 * @param renderer
	 * @param editor */
    protected MyArrayEditor(MyArrayRenderer renderer, JCCellEditor editor) {
        c_editorSupport = new JCCellEditorSupport();
        c_listenerQueue = new LinkedList <JCCellEditorListener> ();
        setOpaque(false);
        setLayout(new GridLayout());
        
        c_renderer = renderer;
        c_editor = editor;
        
        c_editor.getComponent().addFocusListener(new FocusAdapter(){
            @Override
            public void focusLost(FocusEvent fe){                
                if (!c_isArray){
                    return;
                }
                if (c_arraypopup != null && !c_arraypopup.isAncestorOf(fe.getOppositeComponent())){
                    c_editorSupport.fireCancelEditing(new JCCellEditorEvent(fe));
                }                             
            }
        });
        
        c_renderer.setEnabled(false);
        
        addComponentListener(new ComponentAdapter(){
            @Override
            public void componentMoved(ComponentEvent e) {
                if (c_arraypopup != null){
//                    c_arraypopup.hidePopup();
                }
                
            }
            
            @Override
            public void componentResized(ComponentEvent e) {
                if (c_arraypopup != null){
//                    c_arraypopup.hidePopup();
                }
            }
        });
        
    }

	/**
	 *
	 * @param renderer
	 * @param editor
	 * @return
	 */
	public static MyArrayEditor wrap(JCCellRenderer renderer, JCCellEditor editor){
        MyArrayRenderer arrayRenderer;
        if(renderer instanceof MyArrayRenderer){
            arrayRenderer = (MyArrayRenderer)renderer;
        } else{
            arrayRenderer = MyArrayRenderer.wrap(renderer);
        }
        
        return new MyArrayEditor(arrayRenderer, editor);
    }
    
    
    @Override
    public void initialize(AWTEvent ev, JCCellInfo info, Object o) {
        c_firstDraw = true;
        c_isArray = (o != null && o.getClass().isArray());
        
        removeAll();
        
        if (c_isArray){
            add(c_renderer.getRendererComponent(info, o, true));
            JCCellEditorListener listener = c_listenerQueue.poll();
            while (listener != null){
                c_editorSupport.addCellEditorListener(listener);
                listener = c_listenerQueue.poll();
            }
            if (info instanceof JCTableCellInfo){
                JCTableCellInfo info2 = (JCTableCellInfo) info;
                c_width = info2.getTable().getPixelWidth(info2.getColumn());
                c_cellStyle = (CellStyleModel) info2.getCellStyle().clone();
                c_owner = info2.getTable().getTopLevelAncestor();
                location = info2.getColumn();
                String columnVal = MCREWConfig.getColumnNames().get(location);
                JCTable table = info2.getTable();
                Object data = table.getUserData(info2.getRow(), 1);
                ParameterMeta parm = ((OperationObject) data).getParameterMeta(columnVal);
                c_limits = parm.getLimits();
            }
            c_values = (Object[]) o;
            c_newValues = Arrays.copyOf(c_values, c_values.length);
            
        } else{//Not an array
            JCCellEditorListener listener = c_listenerQueue.poll();
            while (listener != null){
                c_editor.addCellEditorListener(listener);
                listener = c_listenerQueue.poll();
            }
            
            Component comp = c_editor.getComponent();
            add(comp);
            c_editor.initialize(ev, info, o);
            comp.requestFocusInWindow();
            return;
        }
    }
    
    @Override
    public Component getComponent() {
        return this;
    }
    
    @Override
    public Object getCellEditorValue() {
        if (!c_isArray){
            return c_editor.getCellEditorValue();
        } else{
            return c_newValues != null ? Arrays.copyOf(c_newValues, c_newValues.length): null;
        }
    }
    
    @Override
    public boolean stopCellEditing() {
        if (!c_isArray){
            return c_editor.stopCellEditing();
        } else{
            if (c_arraypopup!= null){
//                c_arraypopup.hidePopup();
//                c_arraypopup = null;
            }
            return true;
        }
    }
    
    @Override
    public boolean isModified() {
        if (!c_isArray){
            return c_editor.isModified();
        } else{
            boolean temp = !Arrays.equals(c_values, c_newValues);
            return temp;
            
        }
    }
    
    @Override
    public void cancelCellEditing() {
        if (!c_isArray){
            c_editor.cancelCellEditing();
        } else{
            if (c_arraypopup!= null){
//                c_arraypopup.hidePopup();
//                c_arraypopup = null;
            }
            c_newValues = Arrays.copyOf(c_values, c_values.length);
        }
    }
    
    @Override
    public JCKeyModifier[] getReservedKeys() {
        if (!c_isArray){
            return c_editor.getReservedKeys();
        } else{
            return c_editor.getReservedKeys();
        }
        
    }
    
    @Override
    public void addCellEditorListener(JCCellEditorListener l) {
        c_listenerQueue.add(l);
    }
    
    @Override
    public void removeCellEditorListener(JCCellEditorListener l) {
        if (c_isArray){
            c_editorSupport.removeCellEditorListener(l);
        } else{
            c_editor.removeCellEditorListener(l);
        }
    }

	/**
	 *
	 * @param g
	 */
	@Override
    public void paint(Graphics g) {
        super.paint(g);
        if (c_isArray && c_firstDraw){
            //We need to popup something
            try{
                
                if(c_arraypopup != null){
//                    c_arraypopup.hidePopup();
//                    c_arraypopup = null;
                }
                c_arraypopup = new ArrayEditorPopup();
                c_arraypopup.showPopup();
                c_arraypopup.setLocation(getLocationOnScreen().x, getLocationOnScreen().y);
                c_arraypopup.setSize(c_arraypopup.getPreferredSize());
                c_arraypopup.requestFocusInWindow();
            } catch(Exception e){
                e.printStackTrace();
            }
            c_firstDraw = false;
        }
        
    }
    
    
    
    class ArrayEditorPopup extends ArrayEditor_n{
        private static final long serialVersionUID = 1L;
        
//        private Popup c_popup;
        private    int desiredLocationX,desiredLocationY;
        
        public ArrayEditorPopup(){
            g_table.setDataSource(new ArrayDataSource(c_newValues));
            g_table.setRowLabelDisplay(false);
            g_table.setColumnLabelDisplay(false);
            g_table.setAllowCellResize(JCTableEnum.RESIZE_NONE);
            g_table.setPixelWidth(0, c_width);
            c_cellStyle.setCellEditor(c_editor);
            for(int index = 0; index < c_values.length; index ++)
            {
                InputLimits.TableStatus limited = c_limits.evaluateInput((String) c_values[index]);
                MCREWConfig.ColumnDefn tempColumnDef = MCREWConfig.getColumns().get(location);
                JCCellStyle dataStyle = tempColumnDef.getSubCellStyle(limited);
                g_table.setCellStyle(index, 0, dataStyle);
            }
            g_table.setHorizSBDisplay(JCTableEnum.SCROLLBAR_NEVER);
            g_table.setVertSBAttachment(JCTableEnum.SIZE_TO_TABLE);
            
            
            KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
            getInputMap(WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(stroke, "cancel");
            getActionMap().put("cancel", new AbstractAction(){
                private static final long serialVersionUID = 1L;
                
                @Override
                public void actionPerformed(ActionEvent e) {
                    c_editorSupport.fireCancelEditing(new JCCellEditorEvent(e));
                }
            });
            
        }
        
        @Override
        public Dimension getPreferredSize() {
            Dimension size = super.getPreferredSize();
            size.width = MyArrayEditor.this.getWidth();
            size.height += g_table.getPreferredSize().height;
            return size;
        }
        
        
        
        
        public void showPopup(){
            setVisible(true);
            if(c_owner != null && (c_owner instanceof TablePanel))
            {
                TablePanel mc = (TablePanel) c_owner;
                mc.getTable().hookEditor(this);
            }
        }
        
        public void hidePopup(){
            setVisible(false);
            if(c_owner != null && (c_owner instanceof TablePanel))
            {
                TablePanel mc = (TablePanel) c_owner;
                mc.getTable().unhookEditor(this);
            }
        }
        
        
        Point adjustPopupLocationToFitScreen(int xposition, int yposition) {
            Point p = new Point(xposition, yposition);
            
            if(GraphicsEnvironment.isHeadless()){
                return p;
            }
            
            Toolkit toolkit = Toolkit.getDefaultToolkit();
            Rectangle screenBounds;
            Insets screenInsets;
            GraphicsConfiguration gc = null;
            // Try to find GraphicsConfiguration, that includes mouse
            // pointer position
            GraphicsEnvironment ge =GraphicsEnvironment.getLocalGraphicsEnvironment();
            GraphicsDevice[] gd = ge.getScreenDevices();
			for (GraphicsDevice gd1 : gd) {
				if (gd1.getType() == GraphicsDevice.TYPE_RASTER_SCREEN) {
					GraphicsConfiguration dgc = gd1.getDefaultConfiguration();
					if(dgc.getBounds().contains(p)) {
						gc = dgc;
						break;
					}
				}
			}
            
            // If not found and we have invoker, ask invoker about his gc
            if(gc == null && getInvoker() != null) {
                gc = getInvoker().getGraphicsConfiguration();
            }
            
            if(gc != null) {
                // If we have GraphicsConfiguration use it to get
                // screen bounds and insets
                screenInsets = toolkit.getScreenInsets(gc);
                screenBounds = gc.getBounds();
            } else {
                // If we don't have GraphicsConfiguration use primary screen
                // and empty insets
                screenInsets = new Insets(0, 0, 0, 0);
                screenBounds = new Rectangle(toolkit.getScreenSize());
            }
            
            int scrWidth = screenBounds.width -
                    Math.abs(screenInsets.left+screenInsets.right);
            int scrHeight = screenBounds.height -
                    Math.abs(screenInsets.top+screenInsets.bottom);
            
            Dimension size;
            
            size = getPreferredSize();
            
            // Use long variables to prevent overflow
            long pw = (long) p.x + (long) size.width;
            long ph = (long) p.y + (long) size.height;
            
            if( pw > screenBounds.x + scrWidth )
                p.x = screenBounds.x + scrWidth - size.width;
            
            if( ph > screenBounds.y + scrHeight)
                p.y = screenBounds.y + scrHeight - size.height;
            
        /* Change is made to the desired (X,Y) values, when the
           PopupMenu is too tall OR too wide for the screen
         */
            if( p.x < screenBounds.x )
                p.x = screenBounds.x ;
            if( p.y < screenBounds.y )
                p.y = screenBounds.y;
            
            return p;
        }
        
        public Component getInvoker(){
            return MyArrayEditor.this;
        }
        
        @Override
        protected void okButtonActionPerformed(ActionEvent evt) {
            g_table.commitEdit(true);
            c_editorSupport.fireStopEditing(new JCCellEditorEvent(evt));
            hidePopup();
        }
        
        @Override
        protected void cancelButtonActionPerformed(ActionEvent evt) {
            c_editorSupport.fireCancelEditing(new JCCellEditorEvent(evt));
            hidePopup();
        }
        
        @Override
        protected void focusLost(FocusEvent evt) {
            if (!c_isArray){
                return;
            }
            if (!isAncestorOf(evt.getOppositeComponent())){
                c_editorSupport.fireCancelEditing(new JCCellEditorEvent(evt));
            }
            
        }
        
        
    }
    
    static class ArrayDataSource extends AbstractDataSource implements EditableTableDataModel{
        private static final long serialVersionUID = 1L;
        
        private final Object[] c_data;
        
        public ArrayDataSource(Object[] data){
            c_data = data;
        }
        @Override
        public Object getTableDataItem(int row, int column) {
            return c_data[row];
        }
        
        @Override
        public int getNumRows() {
            return c_data.length;
        }
        
        @Override
        public int getNumColumns() {
            return 1;
        }
        
        @Override
        public Object getTableRowLabel(int row) {
            return null;
        }
        
        @Override
        public Object getTableColumnLabel(int column) {
            return null;
        }
        
        @Override
        public boolean setTableDataItem(Object o, int row, int column) {
            c_data[row] = o;
            return true;
        }
        
    }
    
}

