/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 2001 The Apache Software Foundation.  All rights 
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:  
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written 
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 2001, International
 * Business Machines, Inc., http://www.apache.org.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.xerces.impl.v2;

import org.apache.xerces.impl.v2.datatypes.*;
import org.apache.xerces.xni.QName;
import java.util.Hashtable;
import java.util.Vector;

/**
 * @version $Id$
 */
public class XSSimpleTypeDecl implements XSType {

    public static final short INDEX_LENGTH         = 0;
    public static final short INDEX_MINLENGTH      = 1;
    public static final short INDEX_MAXLENGTH      = 2;
    public static final short INDEX_PATTERN        = 3; 
    public static final short INDEX_ENUMERATION    = 4;
    public static final short INDEX_WHITESPACE     = 5;
    public static final short INDEX_MAXINCLUSIVE   = 6;
    public static final short INDEX_MAXEXCLUSIVE   = 7;
    public static final short INDEX_MINEXCLUSIVE   = 8;
    public static final short INDEX_MININCLUSIVE   = 9;
    public static final short INDEX_TOTALDIGITS    = 10;
    public static final short INDEX_FRACTIONDIGITS = 11;
    public static final short INDEX_TOKENTYPE      = 12;

    public static final short FACET_PARAM_SIZE     = 13;

    public static final short DEFINED_LENGTH         = 1<<0;
    public static final short DEFINED_MINLENGTH      = 1<<1;
    public static final short DEFINED_MAXLENGTH      = 1<<2;
    public static final short DEFINED_PATTERN        = 1<<3; 
    public static final short DEFINED_ENUMERATION    = 1<<4;
    public static final short DEFINED_WHITESPACE     = 1<<5;
    public static final short DEFINED_MAXINCLUSIVE   = 1<<6;
    public static final short DEFINED_MAXEXCLUSIVE   = 1<<7;
    public static final short DEFINED_MINEXCLUSIVE   = 1<<8;
    public static final short DEFINED_MININCLUSIVE   = 1<<9;
    public static final short DEFINED_TOTALDIGITS    = 1<<10;
    public static final short DEFINED_FRACTIONDIGITS = 1<<11;
    public static final short DEFINED_TOKENTYPE      = 1<<12;

    public static final short VARIETY_NON    = 0;
    public static final short VARIETY_ATOMIC = 1;
    public static final short VARIETY_LIST   = 2;
    public static final short VARIETY_UNION  = 3;

    public static final short DV_ANYSIMPLETYPE = 0;
    public static final short DV_STRING        = 1;
    public static final short DV_BOOLEAN       = 2;
    public static final short DV_DECIMAL       = 3;
    public static final short DV_FLOAT         = 4;
    public static final short DV_DOUBLE        = 5;
    public static final short DV_DURATION      = 6;
    public static final short DV_DATETIME      = 7;
    public static final short DV_TIME          = 8;
    public static final short DV_DATE          = 9;
    public static final short DV_GYEARMONTH    = 10;
    public static final short DV_GYEAR         = 11;
    public static final short DV_GMONTHDAY     = 12;
    public static final short DV_GDAY          = 13;
    public static final short DV_GMONTH        = 14;
    public static final short DV_HEXBINARY     = 15;
    public static final short DV_BASE64BINARY  = 16;
    public static final short DV_ANYURI        = 17;
    public static final short DV_QNAME         = 18;
    public static final short DV_NOTATION      = 19;
    public static final short DV_ID            = 19;
    public static final short DV_IDREF         = 20;
    public static final short DV_ENTITY        = 21;
    public static final short DV_LIST          = 22;
    public static final short DV_UNION         = 23;
    
    static final TypeValidator[] fDVs = {
        /*new AnySimpleTypeDV(),
        new StringDV(),
        new BooleanDV(),
        new DecimalDV(),
        new FloatDV(),
        new DoubleDV(),
        new DurationDV(),
        new DateTimeDV(),
        new TimeDV(),
        new DateDV(),
        new YearMonthDV(),
        new YearDV(),
        new MonthDayDV(),
        new DayDV(),
        new MonthDV(),
        new HexBinaryDV(),
        new Base64BinaryDV(),
        new AnyURIDV(),
        new QNameDV(),
        new NotationDV(),
        new IdDV(),
        new IdRefDV(),
        new EntityDV(),
        new ListDV(),
        new UnionDV(),
        */
    };

    public static final Integer TOKEN_NONE        = IntegerPool.getInteger(0);
    public static final Integer TOKEN_NAME        = IntegerPool.getInteger(1);
    public static final Integer TOKEN_NCNAME      = IntegerPool.getInteger(2);
    public static final Integer TOKEN_IDNAME      = IntegerPool.getInteger(3);
    public static final Integer TOKEN_IDNCNAME    = IntegerPool.getInteger(4);
    public static final Integer TOKEN_IDREFNAME   = IntegerPool.getInteger(5);
    public static final Integer TOKEN_IDREFNCNAME = IntegerPool.getInteger(6);
    public static final Integer TOKEN_ENTITY      = IntegerPool.getInteger(7);
    public static final Integer TOKEN_NMTOKEN     = IntegerPool.getInteger(8);
    
    String fTypeName;
    String fTargetNamespace;
    short fFinalSet = 0;
    int fBaseIdx = -1;
    String fBaseUri;
    short fFacetDefined = 0;
    int fLength;
    int fMinLength;
    int fMaxLength;
    Vector fPattern;
    Object fEnumeration[];
    short fWhiteSpace;
    Object fMaxInclusive;
    Object fMaxExclusive;
    Object fMinExclusive;
    Object fMinInclusive;
    int fTotalDigits;
    int fFractionDigits;
    short fTokeyType = 0;
    short fFixedFacet = 0;
    short fVariety;
    short fPrimitiveDV;
    short fValidateDV;
    int fItemIdx;
    String fItemUri;
    int[] fMemberIdx;
    String[] fMemberUri;
    int fFundFacets;

    public short getXSType () {
        return COMPLEX_TYPE;
    }
    
    public String getXSTypeName() {
        return fTypeName;
    }

    /**
     * Create a new simple type.
     */
    public XSSimpleTypeDecl(String name, String uri, String baseUri,
                            int baseIdx, short finalSet) {
        fTypeName = name;
        fTargetNamespace = uri;
        fBaseIdx = baseIdx;
        fBaseUri = baseUri;
        fFinalSet = finalSet;
    }

    /**
     * for built-in primitive types (and id/idref/entity)
     */
    public void init4BuiltInType(short validateDV) {
        fVariety = VARIETY_ATOMIC;
        fPrimitiveDV = fDVs[validateDV].getPrimitiveDV();
        fValidateDV = validateDV;
    }
        
    /**
     * If <restriction> is chosen, or built-in derived types by restriction
     */
    public void init4Restriction(XSSimpleTypeDecl base, Object[] facets,
                                 short presentFacet, short fixedFacet) {
        fVariety = base.fVariety;

        switch (fVariety) {
        case VARIETY_ATOMIC:
            fPrimitiveDV = base.fPrimitiveDV;
            fValidateDV = base.fValidateDV;
            break;
        case VARIETY_LIST:
            fItemIdx = base.fItemIdx;
            fItemUri = base.fItemUri;
            fValidateDV = DV_LIST;
            break;
        case VARIETY_UNION:
            fMemberIdx = base.fMemberIdx;
            fMemberUri = base.fMemberUri;
            fValidateDV = DV_UNION;
            break;
        }

        // step 1: inherit fixed facets from base
        fFacetDefined = fFixedFacet = base.fFixedFacet;

        // length
        if ((fFixedFacet & DEFINED_LENGTH) != 0)
            fLength = base.fLength;
        // minLength
        if ((fFixedFacet & DEFINED_MINLENGTH) != 0)
            fMinLength = base.fMinLength;
        // maxLength
        if ((fFixedFacet & DEFINED_MAXLENGTH) != 0)
            fMaxLength = base.fMaxLength;
        // pattern 
        if ((fFixedFacet & DEFINED_PATTERN) != 0)
            fPattern = base.fPattern;
        else
            fPattern = new Vector();
        // enumeration 
        if ((fFixedFacet & DEFINED_ENUMERATION) != 0)
            fEnumeration = base.fEnumeration;
        // whiteSpace 
        if ((fFixedFacet & DEFINED_WHITESPACE) != 0)
            fWhiteSpace = base.fWhiteSpace;
        // maxInclusive 
        if ((fFixedFacet & DEFINED_MAXINCLUSIVE) != 0)
            fMaxInclusive = base.fMaxInclusive;
        // maxExclusive 
        if ((fFixedFacet & DEFINED_MAXEXCLUSIVE) != 0)
            fMaxExclusive = base.fMaxExclusive;
        // minExclusive 
        if ((fFixedFacet & DEFINED_MINEXCLUSIVE) != 0)
            fMinExclusive = base.fMinExclusive;
        // minInclusive 
        if ((fFixedFacet & DEFINED_MININCLUSIVE) != 0)
            fMinInclusive = base.fMinInclusive;
        // totalDigits 
        if ((fFixedFacet & DEFINED_TOTALDIGITS) != 0)
            fTotalDigits = base.fTotalDigits;
        // fractionDigits 
        if ((fFixedFacet & DEFINED_FRACTIONDIGITS) != 0)
            fFractionDigits = base.fFractionDigits;

        if (facets == null)
            return;
            
        // step 2: parse present facets: check against base fixed
        
        short allowedFacet = fDVs[fValidateDV].getAllowedFacets();

        // length
        if ((presentFacet & DEFINED_LENGTH) != 0) {
            if ((allowedFacet & DEFINED_LENGTH) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_LENGTH) == 0) {
                reportError("fixed facet in base");
            } else {
                fLength = ((Integer)facets[INDEX_LENGTH]).intValue();
                fFacetDefined |= DEFINED_LENGTH;
                if ((fixedFacet & DEFINED_LENGTH) == 0)
                    fFixedFacet |= DEFINED_LENGTH;
                // check 4.3.1.c0 must: length >= 0
                if (fLength < 0)
                    reportError("length value '"+facets[INDEX_LENGTH]+"' must be a nonNegativeInteger.");
            }
        }
        // minLength
        if ((presentFacet & DEFINED_MINLENGTH) != 0) {
            if ((allowedFacet & DEFINED_MINLENGTH) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_MINLENGTH) == 0) {
                reportError("fixed facet in base");
            } else {
                fMinLength = ((Integer)facets[INDEX_MINLENGTH]).intValue();
                fFacetDefined |= DEFINED_MINLENGTH;
                if ((fixedFacet & DEFINED_MINLENGTH) == 0)
                    fFixedFacet |= DEFINED_MINLENGTH;
                // check 4.3.2.c0 must: minLength >= 0
                if (fMinLength < 0)
                    reportError("minLength value '"+facets[INDEX_MINLENGTH]+"' must be a nonNegativeInteger.");
            }
        }
        // maxLength
        if ((presentFacet & DEFINED_MAXLENGTH) != 0) {
            if ((allowedFacet & DEFINED_MAXLENGTH) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_MAXLENGTH) == 0) {
                reportError("fixed facet in base");
            } else {
                fMaxLength = ((Integer)facets[INDEX_MAXLENGTH]).intValue();
                fFacetDefined |= DEFINED_MAXLENGTH;
                if ((fixedFacet & DEFINED_MAXLENGTH) == 0)
                    fFixedFacet |= DEFINED_MAXLENGTH;
                // check 4.3.3.c0 must: maxLength >= 0
                if (fMaxLength < 0)
                    reportError("maxLength value '"+facets[INDEX_MAXLENGTH]+"' must be a nonNegativeInteger.");
            }
        }
        // pattern 
        if ((presentFacet & DEFINED_PATTERN) != 0) {
            if ((allowedFacet & DEFINED_PATTERN) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_PATTERN) == 0) {
                reportError("fixed facet in base");
            } else {
                //REVISIT:
                //fPattern.addElement(new RegularExpression((String)facts[INDEX_PATTERN], "X"));
                fPattern.addElement(facets[INDEX_PATTERN]);
                fFacetDefined |= DEFINED_PATTERN;
                if ((fixedFacet & DEFINED_PATTERN) == 0)
                    fFixedFacet |= DEFINED_PATTERN;
            }
        }
        // enumeration 
        if ((presentFacet & DEFINED_ENUMERATION) != 0) {
            if ((allowedFacet & DEFINED_ENUMERATION) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_ENUMERATION) == 0) {
                reportError("fixed facet in base");
            } else {
                String[] enumVals = (String[])facets[INDEX_ENUMERATION];
                fEnumeration = new Object[enumVals.length];
                for (int i = 0; i < fEnumeration.length; i++) {
                    try {
                        fEnumeration[i] = fDVs[fValidateDV].getCompiledValue(enumVals[i]);
                    } catch (InvalidDatatypeValueException ide) {
                        reportError("Value of enumeration '" + enumVals[i] + "' must be from the value space of base");
                        fEnumeration[i] = XSDHandler.EMPTY_STRING;
                    }
                }
                fFacetDefined |= DEFINED_ENUMERATION;
                if ((fixedFacet & DEFINED_ENUMERATION) == 0)
                    fFixedFacet |= DEFINED_ENUMERATION;
            }
        }
        // whiteSpace 
        if ((presentFacet & DEFINED_WHITESPACE) != 0) {
            if ((allowedFacet & DEFINED_WHITESPACE) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_WHITESPACE) == 0) {
                reportError("fixed facet in base");
            } else {
                fWhiteSpace = ((Integer)facets[INDEX_WHITESPACE]).shortValue();
                fFacetDefined |= DEFINED_WHITESPACE;
                if ((fixedFacet & DEFINED_WHITESPACE) == 0)
                    fFixedFacet |= DEFINED_WHITESPACE;
            }
        }
        // maxInclusive 
        if ((presentFacet & DEFINED_MAXINCLUSIVE) != 0) {
            if ((allowedFacet & DEFINED_MAXINCLUSIVE) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_MAXINCLUSIVE) == 0) {
                reportError("fixed facet in base");
            } else {
                try {
                    fMaxInclusive = fDVs[fValidateDV].getCompiledValue((String)facets[INDEX_MAXINCLUSIVE]);
                    fFacetDefined |= DEFINED_MAXINCLUSIVE;
                    if ((fixedFacet & DEFINED_MAXINCLUSIVE) == 0)
                        fFixedFacet |= DEFINED_MAXINCLUSIVE;
                } catch (InvalidDatatypeValueException ide) {
                    reportError("maxInclusive value '"+facets[INDEX_MAXINCLUSIVE]+"' is invalid.");
                }
            }
        }
        // maxExclusive 
        if ((presentFacet & DEFINED_MAXEXCLUSIVE) != 0) {
            if ((allowedFacet & DEFINED_MAXEXCLUSIVE) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_MAXEXCLUSIVE) == 0) {
                reportError("fixed facet in base");
            } else {
                try {
                    fMaxExclusive = fDVs[fValidateDV].getCompiledValue((String)facets[INDEX_MAXEXCLUSIVE]);
                    fFacetDefined |= DEFINED_MAXEXCLUSIVE;
                    if ((fixedFacet & DEFINED_MAXEXCLUSIVE) == 0)
                        fFixedFacet |= DEFINED_MAXEXCLUSIVE;
                } catch (InvalidDatatypeValueException ide) {
                    reportError("maxExclusive value '"+facets[INDEX_MAXEXCLUSIVE]+"' is invalid.");
                }
            }
        }
        // minExclusive 
        if ((presentFacet & DEFINED_MINEXCLUSIVE) != 0) {
            if ((allowedFacet & DEFINED_MINEXCLUSIVE) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_MINEXCLUSIVE) == 0) {
                reportError("fixed facet in base");
            } else {
                try {
                    fMinExclusive = fDVs[fValidateDV].getCompiledValue((String)facets[INDEX_MINEXCLUSIVE]);
                    fFacetDefined |= DEFINED_MINEXCLUSIVE;
                    if ((fixedFacet & DEFINED_MINEXCLUSIVE) == 0)
                        fFixedFacet |= DEFINED_MINEXCLUSIVE;
                } catch (InvalidDatatypeValueException ide) {
                    reportError("minExclusive value '"+facets[INDEX_MINEXCLUSIVE]+"' is invalid.");
                }
            }
        }
        // minInclusive 
        if ((presentFacet & DEFINED_MININCLUSIVE) != 0) {
            if ((allowedFacet & DEFINED_MININCLUSIVE) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_MININCLUSIVE) == 0) {
                reportError("fixed facet in base");
            } else {
                try {
                    fMinInclusive = fDVs[fValidateDV].getCompiledValue((String)facets[INDEX_MININCLUSIVE]);
                    fFacetDefined |= DEFINED_MININCLUSIVE;
                    if ((fixedFacet & DEFINED_MININCLUSIVE) == 0)
                        fFixedFacet |= DEFINED_MININCLUSIVE;
                } catch (InvalidDatatypeValueException ide) {
                    reportError("minInclusive value '"+facets[INDEX_MININCLUSIVE]+"' is invalid.");
                }
            }
        }
        // totalDigits 
        if ((presentFacet & DEFINED_TOTALDIGITS) != 0) {
            if ((allowedFacet & DEFINED_TOTALDIGITS) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_TOTALDIGITS) == 0) {
                reportError("fixed facet in base");
            } else {
                fTotalDigits = ((Integer)facets[INDEX_TOTALDIGITS]).intValue();
                fFacetDefined |= DEFINED_TOTALDIGITS;
                if ((fixedFacet & DEFINED_TOTALDIGITS) == 0)
                    fFixedFacet |= DEFINED_TOTALDIGITS;
                // check 4.3.11.c0 must: totalDigits >= 0
                if (fTotalDigits < 0)
                    reportError("totalDigits value '"+facets[INDEX_TOTALDIGITS]+"' must be a nonNegativeInteger.");
            }
        }
        // fractionDigits 
        if ((presentFacet & DEFINED_FRACTIONDIGITS) != 0) {
            if ((allowedFacet & DEFINED_FRACTIONDIGITS) == 0) {
                reportError("non-supported facet");
            } else if ((fFixedFacet & DEFINED_FRACTIONDIGITS) == 0) {
                reportError("fixed facet in base");
            } else {
                fFractionDigits = ((Integer)facets[INDEX_FRACTIONDIGITS]).intValue();
                fFacetDefined |= DEFINED_FRACTIONDIGITS;
                if ((fixedFacet & DEFINED_FRACTIONDIGITS) == 0)
                    fFixedFacet |= DEFINED_FRACTIONDIGITS;
                // check 4.3.12.c0 must: fractionDigits >= 0
                if (fFractionDigits < 0)
                    reportError("fractionDigits value '"+facets[INDEX_FRACTIONDIGITS]+"' must be a nonNegativeInteger.");
            }
        }
        // token type: internal use, so do less checking
        if ((presentFacet & DEFINED_TOKENTYPE) != 0) {
            fTokeyType = ((Integer)facets[INDEX_TOKENTYPE]).shortValue();
            fFacetDefined |= DEFINED_TOKENTYPE;
        }

        // REVISIT:
        // step 3: check facets against each other: length, bounds
        // step 4: check facets against base
        // step 5: inherit other facets from base (including fTokeyType)
    }
    
    /**
     * If <list> is chosen
     */
    public void init4List(String itemUri, int itemIdx) {
        fVariety = VARIETY_LIST;
        fItemIdx = itemIdx;
        fItemUri = itemUri;
    }
    
    /**
     * If <union> is chosen
     */
    public void init4Union(String[] memberUri, int[] memberIdx) {
        fVariety = VARIETY_UNION;
        fMemberIdx = memberIdx;
        fMemberUri = memberUri;
    }

    /**
     * validate a value, and return the compiled form
     */
    public Object validate(String content, ValidateContext context) throws InvalidDatatypeValueException {
        Object retVal = content;
        
        switch (fVariety) {
        case VARIETY_ATOMIC:
            retVal = validateRestriction(content, context);
            break;
        case VARIETY_LIST:
            retVal = validateList(content, context);
            break;
        case VARIETY_UNION:
            retVal = validateUnion(content, context);
            break;
        }

        return retVal;
    }
    
    Object validateRestriction(String content, ValidateContext context) {
        // REVISIT:
        // step 1: validate the value against the facets. we need to use the
        //         get***, compare, isEqual methods from TypeValidator
        // step 2: validate the value against token_type, if there is on specified.
        // step 3: call fValidateDV for extra validation. this is useful
        //         for ID/IDREF/ENTITY/QNAME, etc.
        return content;
    }

    Object validateList(String content, ValidateContext context) {
        // REVISIT: copy code from old ListDatatypeValidator
        return content;
    }

    Object validateUnion(String content, ValidateContext context) {
        // REVISIT: copy code from old UnionDatatypeValidator
        return content;
    }

    void reportError(String msg) {
        // REVISIT: how do we report datatype facet errors?
        // passing ErrorReporter in? return an array of errors back?
    }
    
    public class ValidateContext {
        XSGrammarResolver grammarResolver;
        Hashtable IDTbl;
        Hashtable IDREFTbl;
        //EntityResolver entityResolver;
    }
} // class XSComplexTypeDecl
