/* ======================================================================
   Parts Copyright 2006 University of Leeds, Oxford University, University of the Highlands and Islands.

   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.bodington.assessment.ims;

import org.apache.log4j.Logger;



import org.bodington.assessment.*;
import org.bodington.assessment.ims.*;
import org.bodington.logging.LoggingUtils;
import org.bodington.server.*;

import java.io.*;

import org.xml.sax.*;


import javax.xml.parsers.ParserConfigurationException;  
import javax.xml.parsers.DocumentBuilderFactory;  
import javax.xml.parsers.FactoryConfigurationError;  
import javax.xml.parsers.DocumentBuilder;


import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.CharacterData;
import org.w3c.dom.NodeList;
import org.w3c.dom.DOMException;


public class QuestionnaireImsQtiHandler extends org.bodington.assessment.ims.ImsQtiHandler
    {
    
    private static Logger log = Logger.getLogger(QuestionnaireImsQtiHandler.class);
	public QuestionnaireImsQtiHandler(XMLReader reader)
	    {
		super(reader);
	    }
    
    Object parseQuestion( Document document )
        throws SAXException, BuildingServerException
        {
        int i, j, k;
        
        
        try
            {
            NodeList list = document.getElementsByTagName( "presentation" );
            if ( list==null || list.getLength()!=1 )
                throw new SAXException( "File format error" );
            Node presentation = list.item( 0 );
            
            StringBuffer intro = new StringBuffer();
            StringBuffer[] statements = new StringBuffer[0];
            
            list = presentation.getChildNodes();
            Node child;
            Element matthing;
            boolean response_lid_present = false;
            boolean response_str_present = false;
            boolean is_multiple_response = false;
            
            for ( i=0; i<list.getLength(); i++ )
                {
                child = list.item( i );
                // hunt out all the material elements that are children of the presentation element
                // (author may intend that material is scattered between user interface elements
                // but bod questionnaires can only have one block of text so these are concatenated.
                if ( child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals( "material" ) )
                    {
                    //for each one pull out all mattext elements and append to the intro text.
                    parseMaterial( (Element)child, intro );
                    continue;
                    }

                // look for a response_lid element
                if ( child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals( "response_lid" ) )
                    {
                    // can't handle multiple MCQs
                    if ( response_lid_present )
                        return null;
                    response_lid_present = true;
                    
                    // need to parse content....
                    Element response_lid = (Element)child;
                    // Multiple | Single response?
                    is_multiple_response = "Multiple".equals(
                        response_lid.getAttribute( "rcardinality" ));
                    NodeList rlid_list = response_lid.getElementsByTagName( "render_choice" );
                    if ( rlid_list.getLength() != 1 )
                        {
                        // must be one render choice element to make it a pure MCQ
                        return null;
                        }

                    //parse the render_choice element
                    // material and response labels may be intermixed - we will discard material
                    // that lies between labels.
                    Element render_choice = (Element)rlid_list.item( 0 );
                    NodeList rchoice_list = render_choice.getElementsByTagName( "response_label" );
                    if ( rchoice_list.getLength()== 0 )
                        {
                        // no choices in the multiple choice!
                        // skip this response_lid and look for another
                        response_lid_present = false;
                        continue;
                        }
                        
                    if ( rchoice_list.getLength() >10 )
                        {
                        // exceeded maximum number of choices
                        return null;
                        }
                        
                    Element response_label;
                    // put together text for each choice...
                    statements = new StringBuffer[rchoice_list.getLength()];
                    for ( j=0; j< rchoice_list.getLength(); j++ )
                        {
                        response_label = (Element)rchoice_list.item( j );
                        statements[j] = new StringBuffer();
                        rlid_list = response_label.getElementsByTagName( "material" ); 
                        if ( rlid_list != null )
                            for ( k=0; k< rlid_list.getLength(); k++ )
                                parseMaterial( (Element)rlid_list.item( k ), statements[j] );
                        }


                    // may be an extra material element in front of or after multiple choice section
                    // just add to the other introductory text.
                    rlid_list = response_lid.getElementsByTagName( "material" ); 
                    for ( j=0; j< rlid_list.getLength(); j++ )
                        {
                        matthing = (Element)rlid_list.item( j );
                        //check that material element is at top level
                        if ( matthing.getParentNode() == response_lid )
                            parseMaterial( matthing, intro );
                        }
                        
                    continue;
                    }
                    
                // look for a response_str element
                if ( child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals( "response_str" ) )
                    {
                    // can't handle multiple text boxes
                    if ( response_str_present )
                        return null;
                    response_str_present = true;
                    
                    // need to parse content....
                    continue;
                    }

                // look for a qticomment element
                if ( child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals( "qticomment" ) )
                    {
                    //skip it
                    continue;
                    }
                
                //ignore any spurious text nodes:
                if ( child.getNodeType() == Node.TEXT_NODE )
                {
                //skip it
                continue;
                }
                    
                // if reached this far must have encountered unsupported tag.
                intro.append( "<P>This imported question contained unsupported features.</P>" );
                log.error( "Unsupported question type, encountered " + child.getNodeName() + "." );
                // just drop through - if there is no multiple choice or comment box
                // then there is just introductory text with no user interface.
                }

            QuestionnaireQuestion question = new QuestionnaireQuestion();
            question.setStandardQuestion( false );
            // this will store a BigString in the database so what if we need to roll back?
            // empty strings go into unused statements
            for ( i=0; i<10; i++ )
                {
                question.setStatement( i , (i<statements.length)?(statements[i].toString()):("") );
                }

            question.setMultipleResponse( is_multiple_response );
            question.setIntroduction( intro.toString() );
            question.setCanComment( response_str_present );
            
                
            return question;
            }
       catch (SAXParseException spe)
       {
           LoggingUtils.logSAXException(log,spe);
       }
            
        
        return null;
        }

    }
