/*
* 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 java.util.Vector;

/**
 * ChoiceImpl class implements the basic functionality of the Choice interface.
 * <br> List and ChoiceGroup class use this class as their member to implement
 * their own functionality.
 */
final class ChoiceImpl
{

    /**
     * Internal class for storing Choice data.
     */
    private class ChoiceData
    {
        String text;
        Image img;
        Font font;
        boolean sel;

        ChoiceData(String text, Image img, Font font, boolean sel)
        {
            this.text = text;
            this.img = img;
            this.font = font;
            this.sel = sel;
        }
    }

    /**
     * Array of ChoiceData items.
     */
    private Vector items = new Vector();

    /**
     * Fit policy flag.
     */
    private int fitPolicy;

    /**
     * Stores if the choice allows single or multiple selections.
     */
    private boolean multiSel;

    /**
     * Constructor.
     *
     * @param multiSel multi or single selection
     */
    ChoiceImpl(boolean multiSel)
    {
        this.multiSel = multiSel;
    }

    /**
     * Check if the arrays are valid.
     *
     * @param textElements string array
     * @param imgElements  image array
     */
    void check(String[] textElements, Image[] imgElements)
    {
        // check textElements
        if(textElements == null)
        {
            throw new NullPointerException(
                MsgRepository.CHOICE_EXCEPTION_TEXT_ARRAY_NULL);
        }
        // check every text element
        for(int i = 0; i < textElements.length; i++)
        {
            if(textElements[i] == null)
            {
                throw new NullPointerException(
                    MsgRepository.CHOICE_EXCEPTION_ITEM_NULL + i);
            }
        }
        // check imgElements array length
        if(imgElements != null && imgElements.length != textElements.length)
        {
            throw new IllegalArgumentException(
                MsgRepository.CHOICE_EXCEPTION_INVALID_ARRAY_LENGTHS);
        }
    }

    /**
     * Append item with specified text and image.
     */
    int append(String text, Image image)
    {
        validateString(text);
        ChoiceData data = new ChoiceData(text, image, Font.getDefaultFont(),
                                         (!multiSel && size() == 0));
        items.addElement(data);
        // return index of added element
        return size() - 1;
    }

    /**
     * Insert item with specified text and image.
     */
    void insert(int elementNum, String text, Image image)
    {
        validateString(text);
        validatePosition(elementNum, size() + 1);
        ChoiceData data = new ChoiceData(text, image, Font.getDefaultFont(),
                                         (!multiSel && size() == 0));
        items.insertElementAt(data, elementNum);
    }

    /**
     * Set item with specified text and image.
     */
    void set(int elementNum, String text, Image image)
    {
        validateString(text);
        validatePosition(elementNum, size());
        ChoiceData data = (ChoiceData) items.elementAt(elementNum);
        data.text = text;
        data.img = image;
    }

    /**
     * Set item's font.
     */
    void setFont(int elementNum, Font font)
    {
        validatePosition(elementNum, size());
        Font checkedFont = (font == null ? Font.getDefaultFont() : font);
        ((ChoiceData) items.elementAt(elementNum)).font = checkedFont;
    }

    /**
     * Delete item from specified position.
     */
    void delete(int elementNum)
    {
        validatePosition(elementNum, size());
        if(!multiSel && isSelected(elementNum) && size() > 1)
        {
            // we are removing the selected item and there are more
            if(elementNum == size() - 1)
            {
                // select previous item
                setSelected(elementNum - 1, true);
            }
            else
            {
                // select next item
                setSelected(elementNum + 1, true);
            }
        }
        items.removeElementAt(elementNum);
    }

    /**
     * Delete all items.
     */
    void deleteAll()
    {
        items.removeAllElements();
    }

    /**
     * Get fit policy.
     */
    int getFitPolicy()
    {
        return fitPolicy;
    }

    /**
     * Get the Font of a given item.
     */
    Font getFont(int elementNum)
    {
        validatePosition(elementNum, size());
        return ((ChoiceData) items.elementAt(elementNum)).font;
    }

    /**
     * Get the image of a given item.
     */
    Image getImage(int elementNum)
    {
        validatePosition(elementNum, size());
        return ((ChoiceData) items.elementAt(elementNum)).img;
    }

    /**
     * Get the text of a given item.
     */
    String getString(int elementNum)
    {
        validatePosition(elementNum, size());
        return ((ChoiceData) items.elementAt(elementNum)).text;
    }

    /**
     * Set fit policy.
     */
    void setFitPolicy(int newFitPolicy)
    {
        switch(newFitPolicy)
        {
        case Choice.TEXT_WRAP_DEFAULT:
        case Choice.TEXT_WRAP_OFF:
        case Choice.TEXT_WRAP_ON:
            fitPolicy = newFitPolicy;
            break;
        default:
            throw new IllegalArgumentException(
                MsgRepository.CHOICE_EXCEPTION_INVALID_FIT_POLICY);
        }
    }

    /**
     * Get the selection index. Returns the first selection index if multiple
     * selection is allowed.
     */
    int getSelectedIndex()
    {
        final int size = size();
        if(!multiSel)
        {
            for(int i = 0; i < size; i++)
            {
                if(isSelected(i))
                {
                    return i;
                }
            }
        }
        return -1;
    }

    /**
     * Checks if an item is selected.
     */
    boolean isSelected(int elementNum)
    {
        validatePosition(elementNum, size());
        return ((ChoiceData) items.elementAt(elementNum)).sel;
    }

    /**
     * Sets an item's selection.
     */
    void setSelected(int elementNum, boolean select)
    {
        validatePosition(elementNum, size());
        if(multiSel)
        {
            ((ChoiceData) items.elementAt(elementNum)).sel = select;
        }
        else
        {
            if(select)
            {
                // clear all
                final int size = size();
                for(int i = 0; i < size; i++)
                {
                    ((ChoiceData) items.elementAt(i)).sel = false;
                }
                // set just one
                ((ChoiceData) items.elementAt(elementNum)).sel = true;
            }
        }
    }

    /**
     * Get selection flags.
     */
    int getSelectedFlags(boolean[] selectedArray)
    {
        validateSelectedArray(selectedArray);
        final int size = size();
        int numSelected = 0;
        for(int i = 0; i < size; i++)
        {
            if(((ChoiceData) items.elementAt(i)).sel)
            {
                selectedArray[i] = true;
                numSelected++;
            }
            else
            {
                selectedArray[i] = false;
            }
        }
        return numSelected;
    }

    /**
     * Set selection flags.
     */
    void setSelectedFlags(boolean[] selectedArray)
    {
        validateSelectedArray(selectedArray);
        final int size = size();
        if(size > 0)
        {
            if(multiSel)
            {
                for(int i = 0; i < size; i++)
                {
                    ((ChoiceData) items.elementAt(i)).sel = selectedArray[i];
                }
            }
            else
            {
                int firstSelected = 0;
                for(int i = 0; i < size; i++)
                {
                    if(selectedArray[i])
                    {
                        firstSelected = i;
                        break;
                    }
                }
                setSelected(firstSelected, true);
            }
        }
    }

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

    /**
     * Validates position.
     *
     * @param position an index of the text array
     * @param upperBoundary upper boundary for position
     */
    private void validatePosition(int position, int upperBoundary)
    {
        if(position < 0 || position >= upperBoundary)
        {
            throw new IndexOutOfBoundsException(
                MsgRepository.CHOICE_EXCEPTION_INVALID_ITEM_INDEX);
        }
    }

    /**
     * Validates a string.
     *
     * @param str string
     */
    private void validateString(String str)
    {
        if(str == null)
        {
            throw new NullPointerException(
                MsgRepository.CHOICE_EXCEPTION_STRING_NULL);
        }
    }

    /**
     * Validates an array containing selections.
     *
     * @param selectedArray selected array
     */
    private void validateSelectedArray(boolean[] selectedArray)
    {
        if(selectedArray == null)
        {
            throw new NullPointerException(
                MsgRepository.CHOICE_EXCEPTION_ARRAY_NULL);
        }
        if(selectedArray.length < size())
        {
            throw new IllegalArgumentException(
                MsgRepository.CHOICE_EXCEPTION_INVALID_ARRAY_SIZE);
        }
    }

}
