/*
 *   
 *
 * Copyright  1990-2006 Sun Microsystems, Inc. All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 only, as published by the Free Software Foundation. 
 * 
 * This program 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
 * General Public License version 2 for more details (a copy is
 * included at /legal/license.txt). 
 * 
 * You should have received a copy of the GNU General Public License
 * version 2 along with this work; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA 
 * 
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
 * Clara, CA 95054 or visit www.sun.com if you need additional
 * information or have any questions. 
 */
 
package com.sun.cardreader;

import java.io.IOException;
import com.sun.midp.security.*;
import com.sun.midp.main.Configuration;

/**
 * This is an internal class to hold a device configuration records.
 */
class DeviceRecord {
    /** Class name. */
    public String className;
    /** Reference to device object. */
    public CardDevice device;
}

/**
 * This class represents card slot factory.
 */
public class SlotFactory {
    /**
     * Property which contains number of devices.
     */
    private static final String DEVICE_NUMBER_PROP = 
        "com.sun.cardreader.deviceNumber";
    /**
     * Prefix of properties which contain class names.
     */
    private static final String DEVICE_CLASS_PROP = 
        "com.sun.cardreader.deviceClass";

    /**
     * Reads devices and slots configuration.
     *
     * @throws CardDeviceException When the configuration is broken
     * @throws IOException When the device creatiom failed
     */
    public static void init() throws IOException, CardDeviceException {
        synchronized (initialized) {
            if (initialized.booleanValue()) {
                return;
            }
        
            /* 
             * Get device number from system property.
             * It is ok to have zero devices in configuration, we'll just 
             * do nothing in this case. 
             */
            int deviceNum = Configuration.getIntProperty(DEVICE_NUMBER_PROP, 0);
            if (deviceNum < 0) {
                throw new CardDeviceException("Number of devices must be > 0");
            }

            deviceRecords = new DeviceRecord[deviceNum];
            
            /* 
             * Read device classes and create objects
             * Also we count total slot number here 
             */
            CardDevice device = null;
            int slotCount = 0;

            for (int i = 0; i < deviceNum; i++) {
                deviceRecords[i] = new DeviceRecord();

                String pName = DEVICE_CLASS_PROP + Integer.toString(i);
                deviceRecords[i].className = Configuration.getProperty(pName);
                if (deviceRecords[i].className == null) {
                    throw new CardDeviceException("Class not found the device "
                                                  + Integer.toString(i));
                }
                
                /* Create card device */
                try {
                    Class cl = Class.forName(deviceRecords[i].className);
                    device = (CardDevice)(cl.newInstance());
                    deviceRecords[i].device = device;
                }
                catch (ClassNotFoundException e) {
                    throw new CardDeviceException("Class not found: " +
                                                  deviceRecords[i].className);
                }
                catch (InstantiationException ie) {  
                    throw new IOException(ie.getMessage());
                }
                catch (IllegalAccessException iae) {  
                    throw new IOException(iae.getMessage());
                }
                device.init();

                device.setStartSlotNumber(slotCount);
                slotCount += device.getSlotCount();
            }

            slots = new CardSlot[slotCount];
            initialized = Boolean.TRUE;
        }
    }
    
    /**
     * Creates specified card slot for the specified device.
     *
     * @param slot Global slot number
     * @param securityToken Security token for the slot
     * @return CardSlot object or null if slot number is out of bounds
     * @throws IOException If CardSlot creation failed
     * @throws CardDeviceException If something wrong with the configuration
     */
    public static CardSlot getCardSlot(int slot, SecurityToken securityToken)
	throws IOException, CardDeviceException {

        synchronized (initialized) {
            if ((!initialized.booleanValue()) || 
                (slot < 0) || 
                (slot >= slots.length)) {
                return null;
            }

            if (slots[slot] != null) {
                return slots[slot];
            } else {
                /* Find device for the slot */
                CardDevice device = null;
                for (int i = 0; i < deviceRecords.length; i++) {
                    if (deviceRecords[i].device.checkSlotNumber(slot)) {
                        device = deviceRecords[i].device;
                        break;
                    }
                }

                if (device == null) {
                    /* Should not be here, paranoic check */
                    throw new CardDeviceException();
                }
                    
                int localSlot = slot - device.getStartSlotNumber();
                slots[slot] = new CardSlot(device, localSlot, securityToken);

                return slots[slot];
            }
        }
    }
    
    /**
     * Get total number of card slots.
     *
     * @return Total number of card slots even if they are not created yet
     */
    public static int getCardSlotCount() {        
        synchronized (initialized) {
            return slots.length;
        }
    }

    /**
     * Get total number of configured devices.
     *
     * @return Total number of configured devices
     */
    public static int getCardDeviceCount() {
        synchronized (initialized) {
            return deviceRecords.length;
        }
    }

    /**
     * Checks if this slot is SAT slot.
     *
     * @param slot Slot number.
     * @return <code>true</code> if the slot is dedicated for SAT,
     *         <code>false</code> otherwise
     * @exception IOException If an error occured.
     * @exception IllegalArgumentException if illegal slot number provided
     */
    public static boolean isSatSlot(int slot) throws IOException {
        synchronized (initialized) {
            if ((!initialized.booleanValue()) || 
                (slot < 0) || 
                (slot >= slots.length)) {
                throw new IllegalArgumentException("Slot does not exist");
            }

            if (slots[slot] != null) {
                return slots[slot].isSAT();
            } else {
                /* Find device for the slot */
                CardDevice device = null;
                for (int i = 0; i < deviceRecords.length; i++) {
                    if (deviceRecords[i].device.checkSlotNumber(slot)) {
                        device = deviceRecords[i].device;
                        break;
                    }
                }

                if (device == null) {
                    /* Should not be here, paranoic check */
            throw new IllegalArgumentException("Slot's device does not exist");
                }
                    
                int localSlot = slot - device.getStartSlotNumber();
                return device.isSatSlot(localSlot);
            }
        }
    }

    /**
     * Device table, holds class names and object references.
     */
    private static DeviceRecord deviceRecords[] = new DeviceRecord[0];

    /**
     * All slots created by the factory.
     */
    private static CardSlot slots[] = new CardSlot[0];

    /**
     * Initialization flag. Also used in synchronized().
     */
    private static Boolean initialized = Boolean.FALSE;
}
