package applet;


import javacard.framework.*;

public class DFile extends File {
    public static final byte EF_File = 0x01;
    public static final byte DF_File = 0x02;
    public static final byte ALL_File = 0x03;

    private byte currChildrenNum;
    private File[] children;

    public DFile(short fileID, byte[] fileControlInformation) {
        super(fileID, fileControlInformation);
        this.currChildrenNum = 0;
        this.children = new File[5];
    }

    void clear() {
        for(short i = 0; i < currChildrenNum; i++) {
            children[i].clear();
            children[i] = null;
        }
    }

    public byte getChildrenNum() {
        return this.currChildrenNum;
    }

    public boolean checkNameTag(byte[] name, short offset, short length) {
        short namePos;
        // Find the position of the DF name tag (84) in the FCI.
        try {
            namePos = TLVHelper.findTag(fcp, (short)2, fcp[(short)1], (byte) 0x84);
            if(length != TLVHelper.getLengthFieldValue(fcp, (short)(namePos+1))) {
                return false;
            }
            namePos += 1 + TLVHelper.getLengthFieldLength(length);
        } catch (Exception e) {
            return false;
        }
        return ( (byte)0 == Util.arrayCompare(name, offset, fcp, namePos, length) );
    }

    public File getChildrenByPos(byte pos) {
        if(pos >= this.currChildrenNum) {
            // 9404: FID not found, record not found or comparison pattern not found.
            ISOException.throwIt((short)37892);
        }
        return this.children[pos];
    }

    public void deleteChildren(short fileID) {
        short childNum = -1;

        for(short i = 0; i < this.currChildrenNum; i++) {
            if(fileID == this.children[i].getFileID()) {
                childNum = i;
                break;
            }
        }
        if(childNum == -1) {
            // 9404: FID not found, record not found or comparison pattern not found.
            ISOException.throwIt((short)37892);
            return;
        }
        if( !JCSystem.isObjectDeletionSupported()) {
            children[childNum].clear();
        }
        this.currChildrenNum--;
        this.children[childNum] = childNum == currChildrenNum? null:children[currChildrenNum];

        if( JCSystem.isObjectDeletionSupported()) {
            JCSystem.requestObjectDeletion();
        }
    }

    public void addChildren(File childFile) {
        // Check if the current children space is exceeded
        if(this.currChildrenNum >= (short)this.children.length) {
            File[] tmp = new File[(short)(this.children.length + 10)];
            for (short i=0; i<this.children.length;i++){
                tmp[i] = this.children[i];
            }
            this.children = tmp;
            if(JCSystem.isObjectDeletionSupported()) {
                JCSystem.requestObjectDeletion();
            }
        }
        this.children[currChildrenNum++] = childFile;
    }


    /**
     * Find the EF under this DF with file ID.
     */
    public EFile findChildElementaryFile (short fID) {
        for(short i=0; i < currChildrenNum; i++) {
            if(children[i].getFileID() == fID) {
                if(children[i] instanceof EFile) {
                    return (EFile) children[i];
                }
            }
        }
        // 9404: FID not found, record not found or comparison pattern not found.
        ISOException.throwIt((short)37892);
        return null;
    }

    /**
     * Find the EF under this DF with the Short EF identifier (5 least significant bits).
     */
    public EFile findChildEFileBySFI (byte sfi) {
        if((sfi & 0xE0) > (byte) 0) {
            // SFI has more than 5 bits, exception
            // 9404: FID not found, record not found or comparison pattern not found.
            ISOException.throwIt((short)37892);
        }
        for(short i=0; i < currChildrenNum; i++) {
            if(children[i] instanceof EFile &&
                    ((EFile)children[i]).getShortFileID() == sfi) {
                return (EFile) children[i];
            }
        }
        // 9404: FID not found, record not found or comparison pattern not found.
        ISOException.throwIt((short)37892);
        return null;
    }

    /**
     * Find the DF under this DF with file ID.
     */
    public DFile findChildDF(short fileID) {
        short i;
        for(i=0; i < this.currChildrenNum; i++) {
            if(this.children[i].getFileID() == fileID) {
                // File ID is matching, perform a type check and return
                if(this.children[i] instanceof DFile) {
                    return (DFile) this.children[i];
                }else{
                    // 9404: FID not found, record not found or comparison pattern not found.
                    ISOException.throwIt((short)37892);
                }
            }
        }
        // 9404: FID not found, record not found or comparison pattern not found.
        ISOException.throwIt((short)37892);
        return null;
    }

    /**
     * Find the DF under this DF with name.
     */
    public DFile findDFByNameRec(byte[] name, short nameOffset, short nameLength) {
        short i;
        for(i=0; i < currChildrenNum; i++) {
            if(children[i] instanceof DFile) {
                if(((DFile)children[i]).checkNameTag(name, nameOffset, nameLength)) {
                    return (DFile) children[i];
                }
                try {
                    return ((DFile)children[i]).findDFByNameRec(name, nameOffset, nameLength);
                } catch(Exception ignored) { }
            }
        }
        // 9404: FID not found, record not found or comparison pattern not found.
        ISOException.throwIt((short)37892);
        return null;
    }

    public File findChildrenRec(short fileID, byte flag) {
        short i;
        for(i=0; i < this.currChildrenNum; i++) {
            if(this.children[i].getFileID() == fileID) {
                if((flag == ALL_File)
                        || (flag == DF_File && children[i] instanceof DFile)
                        || (flag == EF_File && children[i] instanceof EFile)) {
                    return children[i];
                } else {
                    // 9404: FID not found, record not found or comparison pattern not found.
                    ISOException.throwIt((short)37892);
                }
            }
            if(this.children[i] instanceof DFile) {
                try {
                    return ((DFile)children[i]).findChildrenRec(fileID, flag);
                } catch(Exception ignored) { }
            }
        }
        // 9404: FID not found, record not found or comparison pattern not found.
        ISOException.throwIt((short)37892);
        return null;
    }

    public File findChildrenByPath(byte[] path, short pathOffset, short pathLength) {
        short nextFileID;

        DFile df = this;
        for(short pathPos=pathOffset; pathPos < (short) ((pathLength+pathOffset)-1); pathPos+=2) {
            nextFileID = Util.getShort(path, pathPos);
            for(byte childPos=0; childPos < df.getChildrenNum(); childPos++) {
                if(nextFileID == df.getChildrenByPos( childPos ).getFileID()) {
                    if(pathPos == (short) (pathOffset+pathLength-2)) {
                        return df.getChildrenByPos(childPos);
                    } else if(df.getChildrenByPos( childPos ) instanceof DFile) {
                        df = (DFile) df.getChildrenByPos( childPos);
                        break;
                    } else {
                        // 9404: EF in the middle of path, wrong file type
                        ISOException.throwIt((short)37892);
                    }
                }
            }
        }
        // 9404: FID not found, record not found or comparison pattern not found.
        ISOException.throwIt((short)37892);
        return null;
    }

}








