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

/**
 * This class represents card device abstraction.
 */

public abstract class CardDevice {
    /**
     * Number of start slot of the device. The slot factory holds
     * a table with global slot numbers. Every device operates with
     * local ones (numbers inside the device). 
     * <code>startSlotNumber</code> is global number of first
     * local slot.
     */
    private int startSlotNumber;
    
    /** Default SAT slot number. */
    private final static int DEFAULT_SAT_SLOT = 0;

    /**
     * Answer-To-Reset bytes receiving during last reset.
     */
    private byte[] ATR;
    
    /**
     * Initializes the device.
     * 
     * @throws IOException If a device initialization failed.
     * @throws CardDeviceException If a device configuration is bad.
     */
    public abstract void init() throws IOException, CardDeviceException;
    
    /**
     * Closes the device.
     * No exceptions thrown to avoid mess in exception handlers trying
     * to clean up and close the device.
     */
    public abstract void close();
    
    /**
     * Performs platform lock of the device. This is intended to make
     * sure that no other native application
     * uses the same device during a transaction.
     *
     * @throws IOException If a device locking failed.
     */
    public abstract void lock() throws IOException;
    
    /**
     * Unlocks the device.
     *
     * @throws IOException If a device unlocking failed.
     */
    public abstract void unlock() throws IOException;

    /**
     * Open the specified slot. This is for situations when slot creation
     * requires additional actions. By default doing nothing but this is
     * not an abstract method because most devices don't need it.
     *
     * @param slot Slot number
     * @param token Security token for this slot
     * @throws IOException If slot opening failed.
     */
    public void openSlot(int slot, SecurityToken token) throws IOException {
    }
    
    /**
     * Close the specified slot. This is for situations when slot closure
     * requires additional actions. By default doing nothing but this is
     * not an abstract method becuase most devices don't need it.
     *
     * @param slot Slot number
     * @throws IOException If slot close failed.
     */
    public void closeSlot(int slot) throws IOException {
    }
    
    /**
     * Selects the current slot for the subsequent transfer operations.
     * For the one-slot devices the default slot number is 0.
     *
     * @param slot Slot number
     * @throws IOException If a slot selection failed.
     */
    public abstract void selectSlot(int slot) throws IOException;

    /**
     * Gets number of slots on a device. Default implementation returns 1 which
     * is ok for most devices.
     *
     * @return Number of slots on a device
     */
    public int getSlotCount() { 
        return 1; 
    }

    /**
     * Checks if this slot is SAT slot. Default implementation returns true
     * if <code>slotNumber</code> is 0.
     *
     * @param slotNumber Slot number
     * @return <code>true</code> if the slot is dedicated for SAT,
     *         <code>false</code> otherwise
     * @throws IOException If an error occured.
     */
    public boolean isSatSlot(int slotNumber) throws IOException { 
        return slotNumber == DEFAULT_SAT_SLOT; 
    }

    /**
     * Resets the device. Wrapper method for 
     * <code>cmdReset</code>.
     *
     * @throws IOException If a reset failed.
     */
    public void reset() throws IOException {
        int bytes_read;
        byte[] r = new byte[255];
        
        bytes_read = cmdReset(r);
        if (bytes_read > 0) {
            ATR = new byte[bytes_read];
            System.arraycopy(r, 0, ATR, 0, bytes_read);
        } else
            throw new IOException("Empty ATR");
    }
	
    /**
     * Performs reset of device.
     * Returns ATR into provided buffer
     *
     * @param atr Buffer for ATR bytes
     * @return Length of ATR
     * @throws IOException If a reset failed.
     */
    protected abstract int cmdReset(byte[] atr) throws IOException;
	
    /**
     * Performs 'POWER DOWN' command. By default does nothing.
     *
     * @throws IOException If command failed.
     */
    public void cmdPowerDown() throws IOException {
    }

    /**
     * Gets Answer-To-Reset bytes from the device.
     *
     * @return ATR bytes
     * @throws IOException If data transfer failed.
     */
    public byte[] getATR() throws IOException {
        return ATR;
    }

    /**
     * Performs data transfer to the device. Wrapper
     * method for <code>cmdXfer</code>.
     *
     * @param request Request bytes
     * @param response Response bytes
     * @return Length of response
     * @throws IOException If a data transfer failed.
     */
    public int xfer(byte[] request, byte[] response) throws IOException {
        return cmdXfer(request, response);
    }

    /**
     * Performs data transfer to the device.
     *
     * @param request Request bytes
     * @param response Response bytes
     * @return Length of response
     * @throws IOException If a data transfer failed.
     */
    protected abstract int cmdXfer(byte[] request, byte[] response) 
	throws IOException;

    /**
     * Checks if the card in the selected slot was changed 
     * since last call or since last reset.
     * Called after any transfer operation, so
     * implementation can check and store that status during 
     * this operation.
     *
     * @return true if was changed, false otherwise.
     * @throws IOException If something fails.
     */
    public abstract boolean isCardChanged() throws IOException;
    
    /**
     * Gets start slot number in the global slot numbering scheme.
     *
     * @return Number of the first device slot in the global numbering scheme
     */
    public int getStartSlotNumber() {
        return this.startSlotNumber; 
    }

    /**
     * Stores start slot number.
     *
     * @param slot Number of the first device slot in the global 
     *             numbering scheme
     */
    public void setStartSlotNumber(int slot) {
        this.startSlotNumber = slot;
    }

    /**
     * Checks if the global slot number belongs to the device.
     *
     * @param slot Global slot number
     * @return True if slot belongs to this device, otherwise false
     */
    public boolean checkSlotNumber(int slot) {
        return ((slot >= this.startSlotNumber) && 
                (slot < this.startSlotNumber + getSlotCount()));
    }
}
