/*
 * SSXTablePane.java  $Revision: 1.7 $  $Date: 2007/10/03 04:59:32 $
 *
 * Copyright (C) 2007 Meldcraft Corporation
 *
 * This file is part of Scaffold.
 * 
 * Scaffold 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.
 * 
 * Scaffold 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 Scaffold.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.meldcraft.application.guix;

import java.awt.Component;
import java.lang.reflect.InvocationTargetException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;

import javax.swing.JScrollPane;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;

import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.AlternateRowHighlighter;
import org.jdesktop.swingx.decorator.Highlighter;
import org.jdesktop.swingx.decorator.HighlighterPipeline;
import org.jdesktop.swingx.table.TableColumnExt;

import com.meldcraft.application.ApplicationContext;
import com.meldcraft.application.ApplicationElementContext;
import com.meldcraft.application.guis.SSFactory;
import com.meldcraft.gui.swing.SwingUtil;
import com.meldcraft.util.PropertiesHelper;
import com.meldcraft.util.ReflectUtil;

/**
 * @version $Revision: 1.7 $ $Date: 2007/10/03 04:59:32 $
 * @author Rob Richards
 */
public class SSXTablePane extends JScrollPane
{
    private JXTable                     mTable;
    private ApplicationElementContext   mContext;
    private Map                         mConfig;
    private String                      mName;
    private Object                      mLock = new Object();

    public SSXTablePane(ApplicationElementContext context)
    {
        String name = context.getElementId();
        mConfig = context.getConfiguration();
        getViewport().setView(createTable(mConfig, name, new Object[][]{}));
        mContext = context;
        mName = name;
        mTable = (JXTable)getViewport().getView();
    }

    public JXTable getTable()
    {
        return mTable;
    }

    public void setData(Object data)
    {
        synchronized(mLock)
        {
            if (data != null)
            {
                if (data instanceof Object[])
                {
                    data = Arrays.asList((Object[])data);
                }

                if (data instanceof Collection)
                {
                    TableColumn[] cols = (TableColumn[])mTable.getColumns().toArray(new TableColumn[0]);

                    ArrayList list = new ArrayList();
                    for (Iterator iter = ((Collection)data).iterator() ; iter.hasNext() ; )
                    {
                        Object item = iter.next();
                        Object[] row = new Object[cols.length];
                        if (item instanceof Map)
                        {
                            Map map = (Map)item;
                            String[] fields = new String[cols.length];
                            for (int j=0 ; j < cols.length ; j++)
                            {
                                fields[j] = ((HeaderValue)cols[j].getHeaderValue()).getName();
                            }
                            for (int j=0 ; j < row.length ; j++)
                            {
                                row[j] = map.get(fields[j]);
                            }
                        }
                        else if (item instanceof Object[])
                        {
                            Object[] arr = (Object[])item;
                            for (int j=0 ; j < row.length ; j++)
                            {
                                row[j] = arr[j];
                            }
                        }
                        else
                        {
                            // a POJO bean
                            String[] fields = new String[cols.length];
                            for (int j=0 ; j < cols.length ; j++)
                            {
                                fields[j] = "get" + ((HeaderValue)cols[j].getHeaderValue()).getName();
                            }
                            for (int j=0 ; j < row.length ; j++)
                            {
                                try
                                {
                                    row[j] = ReflectUtil.invokeMethod(item, fields[j]);
                                }
                                catch (NoSuchMethodException e)
                                {
                                    throw new RuntimeException(e);
                                }
                                catch (IllegalAccessException e)
                                {
                                    throw new RuntimeException(e);
                                }
                                catch (InvocationTargetException e)
                                {
                                    throw new RuntimeException(e);
                                }
                            }
                        }
                        list.add(row);
                    }
                    Object[][] arr = (Object[][])list.toArray(new Object[0][]);
                    final JXTable table = createTable(mConfig, mName, arr);
                    SwingUtil.waitForInvokeLater(new Runnable() {
                        public void run()
                        {
                            getViewport().remove(mTable);
                            getViewport().setView(table);
                        }
                    });
                    mTable = table;
                    ComponentHelper.configureComponent(mContext.getApplicationContext(), mName, mTable);
                }
            }
        }
    }

    protected JXTable createTable(Map config, String tableName, Object[][] data)
    {
        String[] cols = PropertiesHelper.getStringArray(config, "Columns", new String[0]);

        JXTable table = new JXTable(data, cols);

        if (cols.length > 0)
        {
            // TODO: do also for names and prototypes
            // TODO: also, get individual overrides <tableName>.Column.<colName>.{name|weight|prototype}
            String[] weightstrs = PropertiesHelper.getString(config, "Columns.weights").split(",");
            for (int i=0 ; i < weightstrs.length ; i++)
            {
                int width = PropertiesHelper.getInt(weightstrs[i].trim(), 0);
                TableColumnExt col = table.getColumnExt(i);
                col.setPreferredWidth(width);
                String name = (String)col.getHeaderValue();
                String pre = "Column." + name + ".";

                Map colcfg = PropertiesHelper.getSubset(config, pre);
                configure(col, name, colcfg);
            }
        }

        table.setShowGrid(PropertiesHelper.getBoolean(config, "showGrid", false));
        table.setEditable(PropertiesHelper.getBoolean(config, "editable", false));
        table.setHighlighters(new HighlighterPipeline());
        table.getHighlighters().addHighlighter((Highlighter)PropertiesHelper.getObject(config, "highlighter", Highlighter.class, AlternateRowHighlighter.quickSilver));
        table.getTableHeader().setReorderingAllowed(PropertiesHelper.getBoolean(config, "columnReorderingAllowed", false));

        return table;
    }

    protected void configure(TableColumnExt col, String colName, Map colcfg)
    {
        String value = PropertiesHelper.getString(colcfg, "name", colName);
        col.setHeaderValue(new HeaderValue(colName, value));

        TableCellRenderer rend = col.getCellRenderer();
        String[] parms = PropertiesHelper.getStringArray(colcfg, "renderer.format");
        if ((parms != null) && (parms.length > 0))
        {
            String type = parms[0];
            if (type.equals("date"))
            {
                String format = (parms.length > 1) ? parms[1] : "yyyy/MM/dd HH:mm:ss";
                rend = new JXTable.DateRenderer(new SimpleDateFormat(format));
            }
            else if (type.equals("number"))
            {
                String[] format = (parms.length > 1) ? PropertiesHelper.getStringArray(parms[1], ":", null, false) : null;
                if (format != null)
                {
                    if (format[0].startsWith("/"))
                    {
                        rend = new DivRenderer(format);
                    }
                    else
                    {
                        rend = new JXTable.DoubleRenderer(new DecimalFormat(format[0]));
                    }
                }
                else
                {
                    rend = new JXTable.NumberRenderer();
                }
            }
        }
        col.setCellRenderer(rend);
    }

    public static class HeaderValue
    {
        private String  mName;
        private String  mValue;

        public HeaderValue(String name, String value)
        {
            super();
            // TODO Auto-generated constructor stub
            mName = name;
            mValue = value;
        }

        public String getName()
        {
            return mName;
        }

        public String toString()
        {
            return mValue;
        }
    }

    public static class DivRenderer extends JXTable.DoubleRenderer
    {
        private String[]        mFormat;
        private DecimalFormat   mFormatter;
        private DecimalFormat   mNodivFormatter;
        private double          mDiv;

        public DivRenderer(String[] format)
        {
            mFormat = format;
            String str = format[0];
            String[] arr = PropertiesHelper.getStringArray(str, ";", null, true);
            mDiv = Double.parseDouble(arr[0].substring(1));
            if (arr.length > 1)
            {
                mFormatter = new DecimalFormat(arr[1]);
                if (arr.length > 2)
                {
                    mNodivFormatter = new DecimalFormat(arr[2]);
                }
                else
                {
                    mNodivFormatter = new DecimalFormat();
                }
            }
            else
            {
                mFormatter = new DecimalFormat();
                mNodivFormatter = new DecimalFormat();
            }
        }

        public void setValue(Object value)
        {
            String suffix = "";
            int divcnt = 0;
            if (value instanceof Number)
            {
                double val = ((Number)value).doubleValue();
                if (mFormat.length > 1)
                {
                    suffix = mFormat[1];
                    for (int i=2 ; (val > mDiv) && (i < mFormat.length) ; i++)
                    {
                        divcnt++;
                        val /= mDiv;
                        suffix = mFormat[i];
                    }
                    value = new Double(val);
                }
            }

            DecimalFormat fmt = (divcnt == 0) ? mNodivFormatter : mFormatter;
            String text = (value == null) ? "" : fmt.format(value);
            text += suffix;
            setText(text);
        }
    }

    public static class ComponentHelper extends SSFactory
    {
        public static void configureComponent(ApplicationContext context, String elementId, Component comp)
        {
            SSFactory.configureComponent(context, elementId, comp);
        }
        
    }
}



