/*
 * Copyright 2006 Maskat Project.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.maskat.xml.xmlbeans;

import java.util.ArrayList;
import java.util.List;

import javax.xml.namespace.QName;

import org.apache.xmlbeans.XmlBoolean;
import org.apache.xmlbeans.XmlByte;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlDate;
import org.apache.xmlbeans.XmlDateTime;
import org.apache.xmlbeans.XmlDouble;
import org.apache.xmlbeans.XmlFloat;
import org.apache.xmlbeans.XmlInt;
import org.apache.xmlbeans.XmlInteger;
import org.apache.xmlbeans.XmlLong;
import org.apache.xmlbeans.XmlShort;
import org.apache.xmlbeans.XmlString;
import org.apache.xmlbeans.XmlTime;
import org.apache.xmlbeans.impl.common.XmlWhitespace;
import org.apache.xmlbeans.impl.inst2xsd.Inst2XsdOptions;
import org.apache.xmlbeans.impl.inst2xsd.RussianDollStrategy;
import org.apache.xmlbeans.impl.inst2xsd.util.Element;
import org.apache.xmlbeans.impl.inst2xsd.util.Type;
import org.apache.xmlbeans.impl.inst2xsd.util.TypeSystemHolder;

public class EventDefStrategy extends RussianDollStrategy {
	protected QName processSimpleContentType(String lexicalValue,
			Inst2XsdOptions options, final XmlCursor xc) {
		if (lexicalValue == null || "".equals(lexicalValue)) {
			return XmlString.type.getName();
		}
		if ("byte".equalsIgnoreCase(lexicalValue)) {
			return XmlByte.type.getName();
		}
		if ("short".equalsIgnoreCase(lexicalValue)) {
			return XmlShort.type.getName();
		}
		if ("int".equalsIgnoreCase(lexicalValue)) {
			return XmlInt.type.getName();
		}
		if ("long".equalsIgnoreCase(lexicalValue)) {
			return XmlLong.type.getName();
		}
		if ("integer".equalsIgnoreCase(lexicalValue)) {
			return XmlInteger.type.getName();
		}
		if ("float".equalsIgnoreCase(lexicalValue)) {
			return XmlFloat.type.getName();
		}
		if ("double".equalsIgnoreCase(lexicalValue)) {
			return XmlDouble.type.getName();
		}
		if ("boolean".equalsIgnoreCase(lexicalValue)) {
			return XmlBoolean.type.getName();
		}
		if ("datetime".equalsIgnoreCase(lexicalValue)) {
			return XmlDateTime.type.getName();
		}
		if ("date".equalsIgnoreCase(lexicalValue)) {
			return XmlDate.type.getName();
		}
		if ("time".equalsIgnoreCase(lexicalValue)) {
			return XmlTime.type.getName();
		}

		return XmlString.type.getName();
	}

    static final String _xsi         = "http://www.w3.org/2001/XMLSchema-instance";
    static final QName _xsiNil          = new QName( _xsi, "nil", "xsi" );
    static final QName _xsiMinOccurs          = new QName( _xsi, "minOccurs", "xsi" );
    static final QName _xsiMaxOccurs          = new QName( _xsi, "maxOccurs", "xsi" );
//    static final QName _xsiMinInclusive          = new QName( _xsi, "minInclusive", "xsi" );
//    static final QName _xsiMaxInclusive          = new QName( _xsi, "maxInclusive", "xsi" );
//    static final QName _xsiMinLength          = new QName( _xsi, "minLength", "xsi" );
//    static final QName _xsiMaxLength          = new QName( _xsi, "maxLength", "xsi" );

    protected Element processElement(XmlCursor xc, String comment,
        Inst2XsdOptions options, TypeSystemHolder typeSystemHolder)
    {
        //assert xc.isStart();
        Element element = new Element();
        element.setName(xc.getName());
        element.setGlobal(false);
    	element.setMinOccurs(0);//デフォルトではminOccurs=0

        Type elemType = Type.createUnnamedType(Type.SIMPLE_TYPE_SIMPLE_CONTENT); //assume simple, set later
        element.setType(elemType);

        StringBuffer textBuff = new StringBuffer();
        StringBuffer commentBuff = new StringBuffer();
        List children = new ArrayList();
        List attributes = new ArrayList();

        loop: do
        {
            XmlCursor.TokenType tt = xc.toNextToken();
            switch (tt.intValue())
            {
                case XmlCursor.TokenType.INT_ATTR:
                    // todo check for xsi:type
                    // ignore xsi:... attributes other than xsi:nil
                    QName attName = xc.getName();
                    if (!_xsiNil.getNamespaceURI().equals(attName.getNamespaceURI()))
                        attributes.add(processAttribute(xc, options, element.getName().getNamespaceURI(), typeSystemHolder));
                    else{ 
                    	if (_xsiNil.equals(attName))
                            element.setNillable(true);
                    	if (_xsiMinOccurs.equals(attName)){
                    		// 属性minOccurs
                            String attribValue = xc.getTextValue();
                            try{
                            	element.setMinOccurs(Integer.parseInt(attribValue));
                            }catch(NumberFormatException e){}
                    	}
                    	if (_xsiMaxOccurs.equals(attName)){
                    		// 属性minOccurs
                            String attribValue = xc.getTextValue();
                            try{
                            	element.setMaxOccurs(Integer.parseInt(attribValue));
                            }catch(NumberFormatException e){}
                    	}
                    	if (element.getMaxOccurs()<element.getMinOccurs()){
                    		element.setMaxOccurs(Element.UNBOUNDED);
                    	}
                    }

                    break;

                case XmlCursor.TokenType.INT_START:
                    children.add(processElement(xc, commentBuff.toString(), options, typeSystemHolder));
                    commentBuff.delete(0, commentBuff.length());
                    break;

                case XmlCursor.TokenType.INT_TEXT:
                    textBuff.append(xc.getChars());
                    break;

                case XmlCursor.TokenType.INT_COMMENT:
                    commentBuff.append(xc.getTextValue());
                    break;

                case XmlCursor.TokenType.INT_NAMESPACE:
                    // ignore,
                    // each element and attribute will take care to define itself in the right targetNamespace
                    break;

                case XmlCursor.TokenType.INT_END:
                    break loop;

                case XmlCursor.TokenType.INT_PROCINST:
                    // ignore
                    break;

                case XmlCursor.TokenType.INT_ENDDOC:
                    break loop;

                case XmlCursor.TokenType.INT_NONE:
                    break loop;

                case XmlCursor.TokenType.INT_STARTDOC:
                    throw new IllegalStateException();

                default:
                    throw new IllegalStateException("Unknown TokenType.");
            }
        }
        while( true );

        String collapsedText =  XmlWhitespace.collapse(textBuff.toString(), XmlWhitespace.WS_COLLAPSE);

        String commnetStr = (comment == null ?
            ( commentBuff.length() == 0 ? null : commentBuff.toString() ) :
            ( commentBuff.length() == 0 ? comment : commentBuff.insert(0, comment).toString()) );
        element.setComment(commnetStr);

        if (children.size()>0)
        {
            // complex content
            if (collapsedText.length()>0)
            {
                elemType.setContentType(Type.COMPLEX_TYPE_MIXED_CONTENT);
            }
            else
            {
                elemType.setContentType(Type.COMPLEX_TYPE_COMPLEX_CONTENT);
            }
            processElementsInComplexType(elemType, children, element.getName().getNamespaceURI(), typeSystemHolder, options);
            processAttributesInComplexType(elemType, attributes);
        }
        else
        {
            // simple content
            // hack workaround for being able to call xc.getNamespaceForPrefix()
            XmlCursor xcForNamespaces = xc.newCursor();
            xcForNamespaces.toParent();

            if (attributes.size()>0)
            {
                elemType.setContentType(Type.COMPLEX_TYPE_SIMPLE_CONTENT);

                Type extendedType = Type.createNamedType(
                    processSimpleContentType(textBuff.toString(), options, xcForNamespaces), Type.SIMPLE_TYPE_SIMPLE_CONTENT);
                extendedType.setName(null);
                elemType.setExtensionType(extendedType);

                processAttributesInComplexType(elemType, attributes);
            }
            else
            {
                elemType.setContentType(Type.SIMPLE_TYPE_SIMPLE_CONTENT);
                elemType.setName(processSimpleContentType(textBuff.toString(), options, xcForNamespaces));
                // add enumeration value
                String enumValue = XmlString.type.getName().equals(elemType.getName()) ? textBuff.toString() : collapsedText;
                elemType.addEnumerationValue(enumValue, xcForNamespaces);
            }

            xcForNamespaces.dispose(); // end hack
        }

        checkIfReferenceToGlobalTypeIsNeeded( element, typeSystemHolder, options);

        return element;
    }
}
