/* ======================================================================
   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 java.io.*;
import java.util.Vector;

import javax.xml.parsers.ParserConfigurationException;  
import javax.xml.parsers.DocumentBuilderFactory;   
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.xml.sax.ext.LexicalHandler;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;

import org.bodington.server.BuildingServerException;

public abstract class ImsQtiHandler extends DefaultHandler implements LexicalHandler
    {
    private static Logger log = Logger.getLogger(ImsQtiHandler.class);
    
    private boolean in_item = false;
    private int item_depth=0;
    private CharArrayWriter current_item_ca=null;
    private PrintWriter current_item=null;
    
    private Document document;

    private Vector questions=null;
    
    private boolean in_cdata=false;
    
    private XMLReader reader;
    
    //===========================================================
    // SAX DocumentHandler methods
    //===========================================================
    public ImsQtiHandler( XMLReader reader )
        {
        this.reader = reader;
        current_item_ca = new CharArrayWriter();
        current_item = new PrintWriter( current_item_ca );
        }

    public Vector getQuestions()
        {
        return questions;
        }

    public void startDocument ()
    throws SAXException
        {
        questions = new Vector();
        }

    public void endDocument ()
    throws SAXException
        {
        }

    public void startElement ( String uri, String localName, String name, Attributes attrs)
    throws SAXException
        {
        
//            try
//                {

            if ( name.equals( "item" )  )
                {
                if ( in_item )
                    throw new SAXException ("File format error: Questions cannot be embedded inside other questions." );
                in_item = true;
                item_depth=0;

                current_item_ca.reset();
                // Each item (question) becomes a separate XML doc(!):
                current_item.print( "<?xml version=\"1.0\" standalone=\"yes\"?>" );
                current_item.print( "<questestinterop>" );
                }
            else
                {
                // if we are inside an item keep track of item_depth.
                if ( in_item )
                    item_depth++;
                }

            
            if ( in_item )
                {
                // output the XML to the current item buffer for later DOM processing
                current_item.print( "<"+name );
                if (attrs != null)
                    {
                    for (int i = 0; i < attrs.getLength (); i++)
                        {
                        current_item.print(" ");
                        current_item.print(attrs.getQName(i)+"=\""+attrs.getValue (i)+"\"");
                        }
                    }
                current_item.print(">");
                }

//            }
//        catch (IOException e)
//            {
//            throw new SAXException ("I/O error", e);
//            }
        }

    public void endElement ( String uri, String localName, String name)
        throws SAXException
        {
        try
            {

        
            if ( in_item )
                {
                current_item.print("</"+name+">");
                }

            if ( name.equals( "item" )  )
                {
                if ( in_item )
                    {
                    if ( item_depth!=0 )
                        throw new SAXException ("File format error: Question content has unmatched tags." );
                    
                    in_item=false;

                    current_item.print( "</questestinterop>" );
                    current_item.flush();

                    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                    DocumentBuilder builder = factory.newDocumentBuilder();
                    
                    log.debug( "=======================" );
                    log.debug( current_item_ca.toString() );
                    char[] carray = current_item_ca.toCharArray();
		    StringBuffer buffer = new StringBuffer();
                    for ( int i=0; i<carray.length; i++ )
                  		buffer.append( "/u" + Integer.toHexString( carray[i] ) );
		    log.debug( buffer.toString() );
		    log.debug( "=======================" );
                    
                    InputSource item_source = 
                  	new InputSource( 
                  		new ByteArrayInputStream( current_item_ca.toString().getBytes( "utf-16" ) ) );
                    item_source.setEncoding( "utf-16" );
                    document = builder.parse( item_source );
                  			
                    //XmlDocument xdoc = (XmlDocument)document;
                    //xdoc.write( System.out );
            
                    Object question = parseQuestion( document );
                    if ( question !=null )
                        questions.addElement( question );
                    }
                else
                    throw new SAXException ("File format error: End of question without matching start." );
                }
            else
                {
                if ( in_item )
                    {
                    item_depth--;
                    }
                }
            }
       catch ( ParserConfigurationException pcex )
            {
            throw new SAXException( "Error reading question", pcex );
            }
        catch (IOException e)
            {
            throw new SAXException ("I/O error", e);
            }
        catch (BuildingServerException bsexe)
            {
            throw new SAXException ("Error creating question", bsexe);
            }
            
        }


    public void startDTD(java.lang.String name,
                         java.lang.String publicId,
                         java.lang.String systemId)
              throws SAXException
        {
        }

    public void endDTD()
            throws SAXException
        {
        }
        
    public void startEntity(java.lang.String name)
                 throws SAXException        

        {
        }

    public void endEntity(java.lang.String name)
               throws SAXException
        {
        }
        
    public void startCDATA()
                throws SAXException
        {
//        try
//            {
            if ( in_item )
                {
                in_cdata=true;

                current_item.print( "<![CDATA[" );
                }
//            }
//        catch (IOException e)
//            {
//            throw new SAXException ("I/O error", e);
//            }
        }
                
    public void endCDATA()
              throws SAXException       
        {
//        try
//            {
            if ( in_item )
                {
                in_cdata=false;
                current_item.print( "]]>" );
                }
//            }
//        catch (IOException e)
//            {
//            throw new SAXException ("I/O error", e);
//            }
        }

    public void comment(char[] ch,
                    int start,
                    int length)
             throws SAXException
        {
        }
             
    public void characters (char buf [], int offset, int len)
        throws SAXException
        {
  //      try
  //          {

   	
	StringBuffer buffer = new StringBuffer();
   	for ( int i=offset; i<(offset+len); i++ )
   		buffer.append( "/u" + Integer.toHexString( buf[i] ) );
   	log.debug( buffer.toString() );

            if ( in_item )
                {
                String s = new String(buf, offset, len);

                current_item.print( s );
                }
  //          }
  //      catch (IOException e)
  //          {
  //          throw new SAXException ("I/O error", e);
  //          }
        }

    void parseMaterial( Element material, StringBuffer buffer )
        {
        int j, k;
        NodeList matlist, alist;
        Node matthing, tnode;
        CharacterData text;
        
        matlist = material.getChildNodes();
        for ( j=0; j<matlist.getLength(); j++ )
            {
            matthing = matlist.item( j );
            if ( matthing.getNodeType() == Node.ELEMENT_NODE )
                {
                if ( matthing.getNodeName().equals( "mattext" ) )
                    {
                    alist = matthing.getChildNodes();
                    for ( k=0; k<alist.getLength(); k++ )
                        {
                                
                        tnode = alist.item( k );
                        log.debug( tnode.getNodeName() );
                        if ( tnode.getNodeType() == Node.TEXT_NODE )
                            {
                            text = (CharacterData)tnode;
                            buffer.append( text.getData() );
                            }
                        if ( tnode.getNodeType() == Node.CDATA_SECTION_NODE )
                            {
                            text = (CharacterData)tnode;
                            buffer.append( text.getData() );
                            }
                        }
                    }
                }
            }
        }


    abstract Object parseQuestion( Document document )
        throws SAXException, BuildingServerException;
    }
    
