/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package javax.microedition.lcdui;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.internal.extension.TableExtension;
import org.eclipse.swt.widgets.*;

/**
 * Implementation of LCDUI <code>List</code> class.
 */
public class List extends Screen implements Choice
{

    /**
     * The default command triggered when selecting an item on IMPLICIT lists.
     */
    public static final Command SELECT_COMMAND = new Command("", Command.SCREEN, 0);

    private Command selectCommand;

    private int type;

    private ChoiceImpl choiceImpl;

    // eSWT
    private TableExtension eswtTable;

    private EswtTableSelectionListener eswtTableListener =
        new EswtTableSelectionListener();

    /**
     * Constructor.
     *
     * @param title the list's title
     * @param type the type
     */
    public List(String title, int type)
    {
        this(title, type, new String[] {}, null);
    }

    /**
     * Constructor.
     *
     * @param title the list's title
     * @param type the type
     * @param textElements text elements
     * @param imgElements image elements
     * @throws IllegalArgumentException if type is invalid
     * @throws IllegalArgumentException if image elements array is not null and
     *             the length of the arrays don't match
     * @throws NullPointerException if the text elements array is null
     * @throws NullPointerException if any of the text elements is null
     */
    public List(String title, int type, String[] textElements,
                Image[] imgElements)
    {
        super(title);
        switch(type)
        {
        case Choice.IMPLICIT:
        case Choice.EXCLUSIVE:
            choiceImpl = new ChoiceImpl(false);
            break;
        case Choice.MULTIPLE:
            choiceImpl = new ChoiceImpl(true);
            break;
        default:
            throw new IllegalArgumentException(
                MsgRepository.LIST_EXCEPTION_INVALID_TYPE);
        }
        choiceImpl.check(textElements, imgElements);
        this.selectCommand = SELECT_COMMAND;
        this.type = type;
        construct();
        // append elements
        for(int i = 0; i < textElements.length; i++)
        {
            append(textElements[i], imgElements != null
                   ? imgElements[i] : null);
        }
    }

    /* (non-Javadoc)
     * @see Displayable#eswtConstructContent(int)
     */
    Composite eswtConstructContent(int style)
    {
        Composite comp = super.eswtConstructContent(style);
        eswtTable = new TableExtension(comp, getStyle(type));
        eswtTable.getHorizontalBar().setVisible(false);
        return comp;
    }

    /**
     * Called by Display when Displayable should become visible.
     */
    void eswtHandleShowCurrentEvent()
    {
        super.eswtHandleShowCurrentEvent();
        eswtTable.addSelectionListener(eswtTableListener);
    }

    /**
     * Called by Display when Displayable should become hidden.
     */
    void eswtHandleHideCurrentEvent()
    {
        super.eswtHandleHideCurrentEvent();
        eswtTable.removeSelectionListener(eswtTableListener);
    }

    /* (non-Javadoc)
     * @see Displayable#eswtHandleResizeEvent(int, int)
     */
    void eswtHandleResizeEvent(int width, int height)
    {
        super.eswtHandleResizeEvent(width, height);
        eswtTable.setBounds(getContentComp().getClientArea());
    }

    /**
     * Get Table style based on list type.
     */
    private int getStyle(int listType)
    {
        int tableStyle = SWT.NONE;
        switch(listType)
        {
        case Choice.IMPLICIT:
            tableStyle |= SWT.SINGLE;
            break;
        case Choice.EXCLUSIVE:
            tableStyle |= SWT.SINGLE | SWT.RADIO;
            break;
        case Choice.MULTIPLE:
            tableStyle |= SWT.MULTI | SWT.CHECK;
            break;
        default:
            break;
        }
        return tableStyle;
    }

    private void updateSelection()
    {
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtUpdateSelection();
            }
        });
    }

    /**
     * Update eSWT Table selection.
     */
    private void eswtUpdateSelection()
    {
        if(type == IMPLICIT || type == EXCLUSIVE)
        {
            int sel = choiceImpl.getSelectedIndex();
            if((sel == 0) || (eswtTable.getSelectionIndex() != sel))
            {
                eswtTable.setSelection(sel);
            }
        }
        else
        {
            int size = choiceImpl.size();
            for(int i = 0; i < size; i++)
            {
                if(choiceImpl.isSelected(i))
                {
                    eswtTable.select(i);
                }
                else
                {
                    eswtTable.deselect(i);
                }
            }
        }
    }

    private void eswtInsertItem(int index)
    {
        TableItem item = new TableItem(eswtTable, SWT.NONE, index);
        Image img = choiceImpl.getImage(index);
        item.setImage(0, Image.getESWTImage(img));
        item.setText(0, choiceImpl.getString(index));
    }

    private void eswtSetItem(int index)
    {
        TableItem item = eswtTable.getItem(index);
        Image img = choiceImpl.getImage(index);
        item.setImage(0, Image.getESWTImage(img));
        item.setText(0, choiceImpl.getString(index));
    }

    private void eswtDeleteItem(int index)
    {
        eswtTable.getItem(index).dispose();
        choiceImpl.delete(index);
    }

    private void eswtDeleteAllItems()
    {
        for(int i = eswtTable.getItemCount() - 1; i >= 0; i--)
        {
            if(type == Choice.IMPLICIT)
            {
                choiceImpl.delete(i);
            }
            eswtTable.getItem(i).dispose();
        }
    }

    private void eswtSetItemFont(int index)
    {
        org.eclipse.swt.graphics.Font font = Font.getESWTFont(choiceImpl
                                             .getFont(index));
        eswtTable.getItem(index).setFont(0, font);
    }

    /**
     * Append item with specified text and image.
     *
     * @param text the text
     * @param img the image
     * @return index of added item
     */
    public int append(String text, Image img)
    {
        final int index = choiceImpl.append(text, img);
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtInsertItem(index);
                eswtUpdateSelection();
            }
        });
        return index;
    }

    /**
     * Insert item with specified text and image.
     *
     * @param position the item index
     * @param text the text
     * @param img the image
     */
    public void insert(int position, String text, Image img)
    {
        choiceImpl.insert(position, text, img);
        final int index = position; // index of added element
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtInsertItem(index);
                eswtUpdateSelection();
            }
        });
    }

    /**
     * Set item with specified text and image.
     *
     * @param position the item index
     * @param text the text
     * @param img the image
     */
    public void set(int position, String text, Image img)
    {
        choiceImpl.set(position, text, img);
        final int index = position; // index of changed element
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtSetItem(index);
                eswtUpdateSelection();
            }
        });
    }

    /**
     * Remove item with at specified position.
     *
     * @param position the item index
     */
    public void delete(int position)
    {
        if (position < 0 || ( position >= size())) 
        {
            throw new IndexOutOfBoundsException(
                    MsgRepository.CHOICE_EXCEPTION_INVALID_ITEM_INDEX);
        }
        final int index = position; // index of changed element
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtDeleteItem(index);
                eswtUpdateSelection();
            }
        });
    }

    /**
     * Remove all items.
     */
    public void deleteAll()
    {
        if(type != Choice.IMPLICIT)
        {
            choiceImpl.deleteAll();
        }
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtDeleteAllItems();
            }
        });
    }

    /**
     * Get the fit policy of this list.
     *
     * @return the lists fir policy
     */
    public int getFitPolicy()
    {
        return choiceImpl.getFitPolicy();
    }

    /**
     * Get the font used in a list item.
     *
     * @param position the index of the item
     * @return the items font
     */
    public Font getFont(int position)
    {
        return choiceImpl.getFont(position);
    }

    /**
     * Get the image part of a list item.
     *
     * @param position the index of the item
     * @return the items image part
     */
    public Image getImage(int position)
    {
        return choiceImpl.getImage(position);
    }

    /**
     * Get the string part of a list item.
     *
     * @param position the index of the item
     * @return the items string part
     */
    public String getString(int position)
    {
        return choiceImpl.getString(position);
    }

    /**
     * Get selected flags.
     *
     * @param selectedArray an array with selected items
     * @return selected flags
     */
    public int getSelectedFlags(boolean[] selectedArray)
    {
        return choiceImpl.getSelectedFlags(selectedArray);
    }

    /**
     * Returns the selected index.
     *
     * @return the selected index
     */
    public int getSelectedIndex()
    {
        return choiceImpl.getSelectedIndex();
    }

    /**
     * Returns if the specified element is selected.
     *
     * @param position specified element index
     * @return true if its selected, false otherwise
     */
    public boolean isSelected(int position)
    {
        return choiceImpl.isSelected(position);
    }

    /**
     * Set the fit policy of this list.
     *
     * @param newFitPolicy the new fit policy
     */
    public void setFitPolicy(int newFitPolicy)
    {
        choiceImpl.setFitPolicy(newFitPolicy);
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtTable.setWordWrap(choiceImpl.getFitPolicy()
                                      == Choice.TEXT_WRAP_ON);
            }
        });
    }

    /**
     * Set the font of a list item.
     *
     * @param position the index of the item
     * @param font the desired font
     */
    public void setFont(int position, Font font)
    {
        choiceImpl.setFont(position, font);
        final int index = position; // index of added element
        ESWTUIThreadRunner.syncExec(new Runnable()
        {
            public void run()
            {
                eswtSetItemFont(index);
            }
        });
    }

    /**
     * Set the select command of an IMPLICIT list.
     *
     * @param cmd a command.
     */
    public void setSelectCommand(Command cmd)
    {
        if(type == Choice.IMPLICIT)
        {
            if(cmd != SELECT_COMMAND)
            {
                addCommand(cmd);
            }
            selectCommand = cmd;
        }
    }

    /**
     * Removes command from the List.
     *
     * @param cmd a command.
     */
    public void removeCommand(Command cmd)
    {
        if(cmd == selectCommand)
        {
            selectCommand = null;
        }
        super.removeCommand(cmd);
    }

    /**
     * Set selected flags.
     *
     * @param selectedArray an array with selected items
     */
    public void setSelectedFlags(boolean[] selectedArray)
    {
        choiceImpl.setSelectedFlags(selectedArray);
        updateSelection();
    }

    /**
     * Set selected index.
     *
     * @param position the index of the item
     * @param select selected or not
     */
    public void setSelectedIndex(int position, boolean select)
    {
        choiceImpl.setSelected(position, select);
        updateSelection();
    }

    /**
     * Returns the size of the list.
     *
     * @return the lists size
     */
    public int size()
    {
        return choiceImpl.size();
    }

    /**
     * Table Default Selection listener.
     */
    class EswtTableSelectionListener implements SelectionListener
    {

        private void update(SelectionEvent se)
        {
            if(se.widget != null && se.item != null)
            {
                int index = ((Table) se.widget).indexOf((TableItem) se.item);
                Logger.method(List.this, "updateSel", String.valueOf(index),
                              se);
                if(index >= 0)
                {
                    choiceImpl.setSelected(index, !isSelected(index));
                }
            }
        }

        public void widgetDefaultSelected(SelectionEvent se)
        {
            if(type == Choice.IMPLICIT)
            {
                if(size() > 0)
                {
                    callCommandAction(selectCommand);
                }
            }
        }

        public void widgetSelected(SelectionEvent se)
        {
            if(type == Choice.IMPLICIT || type == Choice.EXCLUSIVE)
            {
                update(se);
            }
            else if(type == Choice.MULTIPLE)
            {
                if(se.detail == SWT.CHECK)
                {
                    update(se);
                }
            }
        }

    }

}
