/*
 * Copyright
 *   2007 axYus - www.axyus.com
 *   2005 C.Marchand - christophe.marchand@axyus.com
 *
 * This file is part of XEMELIOS.
 *
 * XEMELIOS is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * XEMELIOS 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with XEMELIOS; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package fr.gouv.finances.cp.xemelios.ui.resulttable;

import fr.gouv.finances.cp.xemelios.common.Constants;
import fr.gouv.finances.cp.xemelios.common.config.WidgetModel;
import fr.gouv.finances.cp.xemelios.widgets.AbstractWidget;
import java.awt.Component;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.Box;
import javax.swing.DefaultCellEditor;
import javax.swing.Icon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

import org.apache.log4j.Logger;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.AlternateRowHighlighter;
import org.jdesktop.swingx.decorator.HighlighterPipeline;
import org.w3c.dom.Document;

import fr.gouv.finances.cp.utils.Amount;
import fr.gouv.finances.cp.utils.IoUtils;
import fr.gouv.finances.cp.utils.Pair;
import fr.gouv.finances.cp.utils.StartUrl;
import fr.gouv.finances.cp.utils.ui.DisplayExceptionDlg;
import fr.gouv.finances.cp.utils.ui.IhmFactory;
import fr.gouv.finances.cp.xemelios.common.DematTransform;
import fr.gouv.finances.cp.xemelios.common.PJRef;
import fr.gouv.finances.cp.xemelios.common.ToolException;
import fr.gouv.finances.cp.xemelios.common.config.DocumentModel;
import fr.gouv.finances.cp.xemelios.common.config.ElementModel;
import fr.gouv.finances.cp.xemelios.common.config.HiddenModel;
import fr.gouv.finances.cp.xemelios.common.config.PluginModel;
import fr.gouv.finances.cp.xemelios.data.DataAccessException;
import fr.gouv.finances.cp.xemelios.data.DataConfigurationException;
import fr.gouv.finances.cp.xemelios.data.DataImpl;
import fr.gouv.finances.cp.xemelios.data.DataLayerManager;
import fr.gouv.finances.cp.xemelios.ui.HtmlViewer;
import fr.gouv.finances.cp.xemelios.ui.ImageResources;
import fr.gouv.finances.cp.xemelios.ui.SearchWindow;
import fr.gouv.finances.cp.xemelios.ui.ia.JTRIS;
import fr.gouv.finances.cp.xemelios.utils.XmlUtils;

public class ResultTable extends JXTable {
    /**
     *
     */
    private static final long serialVersionUID = 3311785168156347546L;

//    private static final IhmFactory iF = IhmFactory.newInstance();
    private static Icon iconPapier = IhmFactory.getIconFromResource(ImageResources.PJ_PAPER);
    private static Icon iconDvd = IhmFactory.getIconFromResource(ImageResources.PJ_DVD);
    private static Icon iconOnline = IhmFactory.getIconFromResource(ImageResources.PJ_ONLINE);
    private static Icon iconAvailable = IhmFactory.getIconFromResource(ImageResources.PJ_AVAILABLE);
    private static Icon iconUnavailable = IhmFactory.getIconFromResource(ImageResources.PJ_UNAVAILABLE);

    private static Logger logger = Logger.getLogger(ResultTable.class);
    @SuppressWarnings("unused")
    private ElementModel em;
    private SearchWindow searchWindow;
    private DataImpl impl = null;
    
    @SuppressWarnings("serial")
    public ResultTable(TableModel model, final SearchWindow sw, final ElementModel em) {
        super(model);
        this.em = em;
        this.searchWindow = sw;
        AmountRenderer amrenderer = new AmountRenderer();
        setDefaultRenderer(Amount.class,amrenderer);
        DateRenderer drenderer = new DateRenderer();
        setDefaultRenderer(Date.class,drenderer);
        PjRenderer renderer = new PjRenderer();
        setDefaultRenderer(PjRefHandler.class, renderer);
        setDefaultEditor(PjRefHandler.class, new PjEditor(sw.getCurrentCollectivite(),sw.getCurrentBudget()));
        setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        setColumnControlVisible(true);
        setRolloverEnabled(true);
        if(!"remove".equals(System.getProperty("xemelios.jxtable.highlighter"))) {
            HighlighterPipeline hp = new HighlighterPipeline();
            hp.addHighlighter(new AlternateRowHighlighter());
            setHighlighters(hp);
        }
        InputMap im = getInputMap();
        KeyStroke ks = KeyStroke.getKeyStroke("control P");
        im.put(ks, "print");
        im.put(KeyStroke.getKeyStroke("control T"), "TT");
        ActionMap am = getActionMap();
        am.put("TT", new AbstractAction() {
            public void actionPerformed(ActionEvent evt) {
                JDialog tetris = new JTRIS(sw.getParentFrame());
                tetris.setVisible(true);
            }
        });
        // pour le menu contextuel
        MouseListener popupListener = new PopupListener();
        getTableHeader().addMouseListener(popupListener);
        
        // double-click sur une ligne pour visualisation dans navigateur
        addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent evt) {
                Point p = evt.getPoint();
                int row = sw.getTable().rowAtPoint(p);
                if(row>=0 && evt.getClickCount()==2 && evt.getButton()==evt.BUTTON1) {
                    if(em.getXslt()==null || em.getXslt().length()==0) return;
                    try {
                        row=((JXTable)sw.getTable()).convertRowIndexToModel(row);
                        EtatResultTableModel etrm = (EtatResultTableModel)getModel();
                        DataHandler dh = etrm.getRowAt(row,0);
                        Document doc = dh.getDocument();
                        Hashtable<String,Object> parameters = new Hashtable<String,Object>();
                        parameters.put(HtmlViewer.PARAM_DESTINATION,em.getParent().useExternalBrowser()?HtmlViewer.VALUE_DESTINATION_EXTERNAL:HtmlViewer.VALUE_DESTINATION_INTERNAL);
                        for(int pos=0;pos<etrm.getHiddens().size();pos++) {
                            HiddenModel hm = etrm.getHiddens().get(pos);
                            if(hm.getName().startsWith("xsl-param:")) {
                                String paramName = hm.getName().substring("xsl-param:".length());
                                Object paramValue = dh.getValueAtColumn(etrm.getListeChamps().size()+pos);
                                parameters.put(paramName,paramValue);
                            }
                        }
                        Document repository = DataLayerManager.getImplementation().getRepositoryAsDom(em.getParent().getParent(),sw.getCurrentCollectivite());
                        parameters.put("repository",repository);
                        parameters.put("config",searchWindow.getParentFrame().getDocModels().getSmallDOM());
                        parameters.put("collectivite",sw.getCurrentCollectivite().key);
                        parameters.put("budget",sw.getCurrentBudget().key);
                        InputStream is = IoUtils.getInputStream(em.getParent().getParent().getBaseDirectory()+"/"+em.getXslt());
                        InputStream htmlStream = DematTransform.transform(doc,is,parameters);
                        sw.parentFrame.displayHtmlStream(htmlStream,em.getParent(),sw.getDesktopPane());
                    } catch(Exception ex) {
                        Component comp2 = sw.getTable();
                        while(comp2!=null && !(comp2 instanceof JFrame))
                            comp2 = comp2.getParent();
                        if(comp2!=null)
                            new DisplayExceptionDlg((JFrame)comp2,ex);
                        else
                            ex.printStackTrace();
                    }
                } else if(evt.getButton()==MouseEvent.BUTTON2 || evt.getButton()==MouseEvent.BUTTON3) {
                    EtatResultTableModel etrm = (EtatResultTableModel)getModel();
                    JPopupMenu pm = new JPopupMenu();
                    for(WidgetModel wm:etrm.getListeResultat().getWidgets()) {
                        AbstractWidget aw = null;
                        try {
                            Class clazz = Class.forName(wm.getClassName());
                            Constructor cc = clazz.getConstructor(WidgetModel.class,ElementModel.class);
                            aw = (AbstractWidget)cc.newInstance(wm,em);
                        } catch(Exception ex) {
                            new DisplayExceptionDlg(sw.getParentFrame(),ex);
                        }
                        if(aw!=null) {
                            WidgetAction wa = new WidgetAction(aw,etrm,((JXTable)sw.getTable()).convertRowIndexToModel(row));
                            pm.add(new JMenuItem(wa));
                        }
                    }
                    if(pm.getComponentCount()>0)
                        pm.show(evt.getComponent(), evt.getX(), evt.getY());
                }
            }
        });
        try {
            impl = DataLayerManager.getImplementation();
        } catch(Throwable t) {}
    }
    
    class PopupListener extends MouseAdapter implements ActionListener {
        Pair[] operators;
        HashMap<String, String> operatorList = new HashMap<String, String>();
        int columnIndex=0;
        
        @Override
        public void mouseReleased(MouseEvent e) {
            if(!e.isConsumed())
                maybeShowPopup(e);
        }
        @Override
        public void mousePressed(MouseEvent e) {
            if(!e.isConsumed())
                maybeShowPopup(e);
        }
        
        /**
         * Décide s'il faut afficher le menu contextuel des opérations sur les colonnes
         */
        private void maybeShowPopup(MouseEvent e) {
            Point p = e.getPoint();
            JPopupMenu popup;
            
            if(e.isPopupTrigger()) {
                e.consume();
                EtatResultTableModel etrm = (EtatResultTableModel)searchWindow.getTable().getModel();
                /** il faut vérifier qu'il s'agit bien d'une colonne dont la donnée est de type
                 * integer, decimal ou date */
                columnIndex = convertColumnIndexToModel(columnAtPoint(p));
                boolean showPopup = etrm.isSumOperationPermittedOnColumn(columnIndex);
//                logger.debug("showPopup="+(showPopup?"true":"false"));
                // maintenant on regarde si on est sur un plugin de recherche, et s'il autorise ces opérations (somme, moyenne, ...)
                PluginModel pm = searchWindow.getPluginModel();
                if(pm!=null) {
                    showPopup = showPopup && pm.isSumPossible();
                }
                try {
                    if(showPopup) {
                        DataImpl impl = DataLayerManager.getImplementation();
                        operators = impl.getAggregateOperators(etrm.getColumnDatatype(columnIndex));
                        if(operators.length>0) {
                            popup = new JPopupMenu();
                            JMenuItem menuItem;
                            for(Pair ope : operators) {
                                menuItem = new JMenuItem(ope.libelle);
                                menuItem.addActionListener(this);
                                popup.add(menuItem);
                                operatorList.put(ope.libelle, ope.key);
                            }
                            popup.show(e.getComponent(), e.getX(), e.getY());
                        }
                    }
                } catch(DataConfigurationException dce) { dce.printStackTrace(); logger.debug("DataLayer not implemented"); }
            }
        }
        
        /**
         * Appelée quand un item de menu est sélectionné (dans le menu contextuel sur les colonnes)
         * @param ae L'ActionEvent à l'origine de cet appel
         */
        public void actionPerformed(ActionEvent ae) {
            JMenuItem item = (JMenuItem)ae.getSource();
            
            try {
                EtatResultTableModel etrm = (EtatResultTableModel)searchWindow.getTable().getModel();
                DocumentModel dm = em.getParent().getParent();
                String xpath = etrm.getColumnXPath(columnIndex);
                xpath = XmlUtils.normalizeNS(xpath,dm.getNamespaces());
                logger.debug(xpath);
                final String resultat = DataLayerManager.getImplementation().calculateAggregate(searchWindow.getDocumentModel(),
                        em.getParent(),
                        xpath,
                        operatorList.get(item.getText()),
                        etrm.getListeChamps().get(columnIndex),
                        etrm.getResultSet());
                
                // now we display the result
                final JDialog dialog = new JDialog();
                dialog.setTitle(searchWindow.getTitle());
                JLabel texte = new JLabel(item.getText()+" de "+etrm.getColumnHeader(columnIndex)+" : "+resultat);
                JButton boutonFermer = new JButton("Fermer");
                JButton boutonCopier = new JButton("Copier");
                boutonCopier.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        Clipboard system = Toolkit.getDefaultToolkit().getSystemClipboard();
                        StringSelection ss = new StringSelection(resultat);
                        system.setContents(ss, ss);
                    }
                });
                boutonFermer.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent e) {
                        dialog.setVisible(false);
                        dialog.dispose();
                    }
                });
                
                Box haut = Box.createVerticalBox();
                Box bas = Box.createHorizontalBox();
                bas.add(boutonFermer);
                bas.add(Box.createHorizontalStrut(20));
                bas.add(boutonCopier);
                texte.setAlignmentX(Component.CENTER_ALIGNMENT);
                bas.setAlignmentX(Component.CENTER_ALIGNMENT);
                haut.add(texte);
                haut.add(Box.createVerticalStrut(20));
                haut.add(bas);
                dialog.add(haut);
                dialog.setSize(250,100);
                dialog.setLocation(600,500);
                dialog.setVisible(true);
            } catch(DataConfigurationException dce) {
                dce.printStackTrace();
            } catch(DataAccessException dae) { }
        }
    }
    
    @SuppressWarnings("serial")
    class PjRenderer extends JButton implements TableCellRenderer {
        
        public PjRenderer() {
            setOpaque(true);
            setText("...");
        }
        
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            setForeground(table.getSelectionForeground());
            setBackground(table.getSelectionBackground());
            return this;
        }
    }
    
    @SuppressWarnings("serial")
    class AmountRenderer extends DefaultTableCellRenderer {
        public AmountRenderer() {
            super();
        }
        
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            Component parent = super.getTableCellRendererComponent(table,value,isSelected,hasFocus,row,column);
            JLabel lbl = (JLabel)parent;
            if(value!=null && value instanceof Amount) {
                Amount am = (Amount)value;
                lbl.setText(am.stringRepresentation());
                lbl.setHorizontalAlignment(SwingConstants.RIGHT);
            }
            return parent;
        }
    }
    
    class DateRenderer extends DefaultTableCellRenderer {
        public DateRenderer() {
            super();
        }
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            Component parent = super.getTableCellRendererComponent(table,value,isSelected,hasFocus,row,column);
            JLabel lbl = (JLabel)parent;
            if(value!=null && value instanceof Date) {
                Date d = (Date)value;
                SimpleDateFormat sdf = new SimpleDateFormat(System.getProperty(Constants.SYS_PROP_DATE_FORMAT,"yyyy-MM-dd"));
                lbl.setText(sdf.format(d));
                lbl.setHorizontalAlignment(SwingConstants.LEFT);
            }
            return parent;
        }
    }
    
    @SuppressWarnings("serial")
    class PjEditor extends DefaultCellEditor {
        protected JButton button;
        private Object data;
        private boolean isPushed;
        private Pair collectivite, budget;
        private Point point;
        private JTable table;
        
        public PjEditor(final Pair collectivite, final Pair budget) {
            super(new JCheckBox());
            this.collectivite=collectivite;
            this.budget=budget;
            button = new JButton("...");
            button.setOpaque(true);
            button.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    fireEditingStopped();
                }
            });
        }
        
        @Override
        public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
            button.setForeground(table.getSelectionForeground());
            button.setBackground(table.getSelectionBackground());
            data=value;
            Rectangle rect = table.getCellRect(row,column,false);
            point = rect.getLocation();
            this.table=table;
            isPushed = true;
            return button;
        }
        
        @Override
        public Object getCellEditorValue() {
            if (isPushed) {
                JPopupMenu popup = new JPopupMenu("Pièces jointes");
                if(data instanceof PjRefHandler) {
                    PjRefHandler pjs = (PjRefHandler)data;
                    int count = 0;
                    for(PJRefInfo pj:pjs.getPjs()) {
                        count++;
                        // modifs
                        Icon icon = null;
                        boolean available = false;
                        if(PJRefInfo.SUPPORT_PAPIER.equals(pj.getSupport())) icon = iconPapier;
                        else if(PJRefInfo.SUPPORT_CD_DVD.equals(pj.getSupport())) icon = iconDvd;
                        else if(PJRefInfo.SUPPORT_ONLINE.equals(pj.getSupport())) icon = iconOnline;
                        else if(PJRefInfo.SUPPORT_LOCAL.equals(pj.getSupport())) {
                            try {
                                available = impl.isPjAvailable(collectivite,pj.getNom());
                            } catch(Throwable t) {}
                            if(available) {
                                icon = iconAvailable;
                            } else {
                                icon = iconUnavailable;
                            }
                        }
                        JMenuItem mnu=new JMenuItem(pj.getNom(),icon);
                        if(available) {
                            mnu.addActionListener(new PjDisplayAction(collectivite,budget,pj));
                        }
                        // fin modifs
                        popup.add(mnu);
                    }
                } else {
                    logger.debug(data.getClass().getName()+" --> "+data.toString());
                }
                popup.show(table,point.x,point.y);
            }
            isPushed = false;
            return "...";
        }
        
        @Override
        public boolean stopCellEditing() {
            isPushed = false;
            return super.stopCellEditing();
        }
        
        @Override
        protected void fireEditingStopped() {
            super.fireEditingStopped();
        }
        
    }
    private class PjDisplayAction implements ActionListener {
        private Pair collectivite, budget;
        private PJRefInfo pj;
        public PjDisplayAction(Pair collectivite, Pair budget, PJRefInfo pj) {
            super();
            this.collectivite=collectivite;
            this.budget=budget;
            this.pj=pj;
        }
        public void actionPerformed(ActionEvent evt) {
//			JOptionPane.showMessageDialog(null,"Collectivite="+collectivite.libelle+"\nbudget="+budget.libelle+"\nPJ="+pj.getId());
            try {
                DataImpl impl = DataLayerManager.getImplementation();
                PJRef pjRef = impl.getPj(collectivite,pj.getNom());
                if(pjRef!=null) {
                    File tmpDir = new File(System.getProperty("java.io.tmpdir"));
                    File tmpFile = File.createTempFile("tmp-",pjRef.getFileName(),tmpDir);
                    FileOutputStream fos = new FileOutputStream(tmpFile);
                    fos.write(pjRef.getData());
                    fos.flush();
                    fos.close();
                    StartUrl.startUrl(tmpFile.toURL().toString());
                } else {
                    JOptionPane.showMessageDialog(null,"Pièce non trouvée.");
                }
            } catch (DataConfigurationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch(DataAccessException daEx) {
                // TODO
                daEx.printStackTrace();
            } catch(IOException ioEx) {
                // TODO
                ioEx.printStackTrace();
            } catch(ToolException tEx) {
                // TODO
                tEx.printStackTrace();
            }
        }
    }
    private class WidgetAction extends AbstractAction {
        private AbstractWidget aw;
        private EtatResultTableModel etrm;
        private int row;
        public WidgetAction(AbstractWidget aw,EtatResultTableModel etrm,int row) {
            super(aw.getWidgetModel().getLibelle());
            this.aw=aw;
            this.etrm=etrm;
            this.row=row;
        }
        public void actionPerformed(ActionEvent evt) {
            aw.run(etrm,row);
        }
    }
}
