/*
 * Copyright (C) 2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (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.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.webdevsupport;

import com.arsdigita.bebop.ControlLink;
import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.SimpleContainer;
import com.arsdigita.bebop.Table;
import com.arsdigita.bebop.event.TableActionEvent;
import com.arsdigita.bebop.event.TableActionListener;
import com.arsdigita.bebop.table.TableModel;
import com.arsdigita.bebop.table.TableModelBuilder;
import com.arsdigita.caching.CacheTable;
import com.arsdigita.caching.CacheTable.TimestampedEntry;
import com.arsdigita.util.LockableImpl;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Set;


/**
 * @author  Vadim Nasardinov (vadimn@redhat.com)
 * @since   2004-01-13
 * @version $Revision: #7 $ $DateTime: 2004/04/07 16:07:11 $
 */
final class CacheTableBrowser extends SimpleContainer {

    private final Table m_listOfTables;
    private final CacheTableContents m_tableContents;

    CacheTableBrowser() {
        m_listOfTables = new ListOfCacheTables();
        m_tableContents = new CacheTableContents();
        m_listOfTables.addTableActionListener(new TableActionListener() {
                public void cellSelected(TableActionEvent ev) {
                    PageState state = ev.getPageState();
                    m_tableContents.setTableID(state,(String) ev.getRowKey());
                }

                public void headSelected(TableActionEvent e) { }
            });

        add(m_listOfTables);
        add(m_tableContents);
    }

    /**
     * Shows all the CacheTable instances that have been instantiated.
     **/
    private static class ListOfCacheTables extends Table {
        public ListOfCacheTables() {
            super(new ModelBuilder(),
                  new String[] {"Cache table", "Max size", "Current size",
                                "Max age, sec"});
        }

        private static class ModelBuilder extends LockableImpl
            implements TableModelBuilder {

            public TableModel makeModel(Table table,
                                        PageState state) {

                List keys = new ArrayList(CacheTable.BROWSER.getTableIDs());
                Collections.sort(keys);
                return new Model(keys.iterator());
            }
        }

        private static class Model implements TableModel {
            private final Iterator m_keys;
            private String m_key;

            Model(Iterator keys) {
                m_keys = keys;
            }

            public boolean nextRow() {
                if (m_keys.hasNext()) {
                    m_key = (String) m_keys.next();
                    return true;
                }
                m_key = null;
                return false;
            }
            public int getColumnCount() {
                return 4;
            }

            public Object getElementAt(int columnIndex) {
                switch (columnIndex) {
                case 0:
                    if ("0".equals(CacheTable.BROWSER.getCurrentSize(m_key))) {
                        return new Label(m_key);
                    } else {
                        return new ControlLink(new Label(m_key));
                    }
                case 1:
                    return CacheTable.BROWSER.getMaxSize(m_key);
                case 2:
                    return CacheTable.BROWSER.getCurrentSize(m_key);
                case 3:
                    return CacheTable.BROWSER.getMaxAge(m_key);
                default:
                    throw new IllegalArgumentException
                        ("columnIndex: " + columnIndex);
                }

            }

            public Object getKeyAt(int columnIndex) {
                return m_key;
            }
        }
    }

    /**
     * Shows the contents of a single CacheTable
     **/
    private static class CacheTableContents extends Table {
        private final static String TABLE_ID_ATTR =
            CacheTableContents.class.getName();

        private String m_tableID;

        public CacheTableContents() {
            super(new ModelBuilder(),
                  new String[] { "Key", "Class", "Value", "Hash code", "Timestamp" });
        }

        public void setTableID(PageState state, String tableID) {
            state.getRequest().setAttribute(TABLE_ID_ATTR, tableID);
        }

        private static class ModelBuilder extends LockableImpl
            implements TableModelBuilder {

            public TableModel makeModel(Table table,
                                        PageState state) {

                String tableID =
                    (String) state.getRequest().getAttribute(TABLE_ID_ATTR);
                return new Model(tableID);
            }
        }

        private static class Model implements TableModel {
            private final static DateFormat FORMATTER =
                new SimpleDateFormat("HH:mm:ss.S");
            private final Iterator m_entries;
            private TimestampedEntry m_entry;


            Model(String tableID) {
                if ( tableID==null ) {
                    m_entries = Collections.EMPTY_LIST.iterator();
                    return;
                }

                Set set = CacheTable.BROWSER.getEntrySet(tableID);
                if ( set==null ) {
                    m_entries = Collections.EMPTY_LIST.iterator();
                    return;
                }

                List list = new ArrayList(set);
                Collections.sort(list, new Comparator() {
                        public int compare(Object o1, Object o2) {
                            if ( o1==null || o2==null ) {
                                throw new NullPointerException();
                            }
                            TimestampedEntry t1 = (TimestampedEntry) o1;
                            TimestampedEntry t2 = (TimestampedEntry) o2;
                            return t1.getKey().compareTo(t2.getKey());
                        }
                    });
                m_entries = list.iterator();

            }

            public boolean nextRow() {
                if (m_entries.hasNext()) {
                    m_entry = (TimestampedEntry) m_entries.next();
                    return true;
                }
                m_entry = null;
                return false;
            }
            public int getColumnCount() {
                return 5;
            }

            private static String classname(Object obj) {
                if ( obj == null ) { return null; }
                final String cName = obj.getClass().getName();
                final int dotIdx = cName.lastIndexOf('.');
                return dotIdx<0 ? cName : cName.substring(dotIdx+1);
            }

            public Object getElementAt(int columnIdx) {
                switch (columnIdx) {
                case 0:
                    return m_entry.getKey();
                case 1:
                    return classname(m_entry.getValue());
                case 2:
                    return m_entry.getValue();
                case 3:
                    return m_entry.getValue()==null ?
                        "null" : toHex(m_entry.getValue().hashCode());
                case 4:
                    return FORMATTER.format(m_entry.getTimestamp());
                default:
                    throw new IllegalArgumentException("idx=" + columnIdx);
                }
            }


            private static String toHex(int value) {
                String hex = Integer.toHexString(value).toUpperCase();
                return "00000000".substring(0, 8-hex.length()) + hex;
            }

            public Object getKeyAt(int columnIndex) {
                return m_entry.getKey();
            }
        }
    }
}
