/* ======================================================================
The Bodington System Software License, Version 1.0
  
Copyright (c) 2001 The University of Leeds.  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 acknowledgement:  "This product includes
software developed by the University of Leeds
(http://www.bodington.org/)."  Alternately, this acknowledgement may
appear in the software itself, if and wherever such third-party
acknowledgements normally appear.

4.  The names "Bodington", "Nathan Bodington", "Bodington System",
"Bodington Open Source Project", and "The University of Leeds" must not be
used to endorse or promote products derived from this software without
prior written permission. For written permission, please contact
d.gardner@leeds.ac.uk.

5.  The name "Bodington" may not appear in the name of products derived
from this software without prior written permission of the University of
Leeds.

THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  TITLE,  THE IMPLIED WARRANTIES 
OF QUALITY  AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO 
EVENT SHALL THE UNIVERSITY OF LEEDS 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 was originally created by the University of Leeds and may contain voluntary 
contributions from others.  For more information on the Bodington Open Source Project, please 
see http://bodington.org/

====================================================================== */

package org.bodington.servlet.facilities;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
//import jdbc.sql.*;
import java.sql.*;

import org.apache.log4j.Logger;

import org.bodington.servlet.*;
import org.bodington.database.PrimaryKey;
import org.bodington.server.realm.Permission;
import org.bodington.server.resources.Resource;
import org.bodington.server.*;
import org.bodington.server.resources.*;
import org.bodington.util.BodingtonURL;

public class DocumentFacility extends MediaDocumentFacility
	{
	
    private static Logger log = Logger.getLogger(DocumentFacility.class);
    private static final boolean defaultUTF8 = ("UTF-8").equals(System
        .getProperty("file.encoding"));

    public boolean canCopy( Resource resource )
    {
        return true;
    }

	public void insert( Request req, PrintWriter out, String command, String insertname )
		throws ServletException, IOException
		{
		log.debug( Thread.currentThread().getName() + " DocumentFacility insert()" );


		if ( out==null )
			{
			super.insert( req, out, command, insertname );
			return;
			}
		
		try
			{
			BuildingSession session;
			
			if ( command.equals( "menu" ) || command.equals( "editmenu" ) )
				{
				log.debug( Thread.currentThread().getName() + " DocumentFacility insert() menu" );
				session = req.getBuildingSession();
				menu( req, out, session, command.equals( "editmenu" )  );
				return;
				}

			if ( command.equals( "editpage" ) || command.equals( "saveinput" ) || command.equals( "newpage" ) )
				{
	   		session = BuildingSessionManagerImpl.getSession( req.getResource() );
				if ( command.equals( "editpage" ) )
					{
					editpage( req, out, session  );
					return;
					}

				if ( command.equals( "saveinput" ) )
					{
					if ( out!=null )
						saveinput( req, out, session );
					return;
					}
					
				if ( command.equals( "newpage" ) )
					{
					if ( out!=null )
						newpage( req, out, session );
					return;
					}
				}

			}
		catch ( BuildingServerException bsex )
			{
			logException( out, "DocumentFacility", "insert", 
			    "A technical problem occured.",
			    bsex );
			return;
			}

		log.debug( Thread.currentThread().getName() + " end of DocumentFacility insert()" );
			
		super.insert( req, out, command, insertname );
		}

	int[] parseFileName( UploadedFileSummary uf )
		{
		StringTokenizer tokenizer = new StringTokenizer( uf.getName(), "_." );
		int j;
		String bit;
		int[] index = new int[4];
		
		if ( tokenizer.countTokens()!=5 )
			return null;
			
		try
			{
			for ( j=0; j<4; j++ )
				{
				bit = tokenizer.nextToken();
				index[j] = Integer.parseInt( bit );
				}
			}
		catch ( Exception ex )
			{
			return null;
			}
				
		bit = tokenizer.nextToken();
		if ( bit==null )
			return null;
				
		if ( !bit.equalsIgnoreCase( "html" ) && !bit.equalsIgnoreCase( "htm" ) )
			return null;
			
		return index;
		}


	private void menu( Request req, PrintWriter out, BuildingSession session, boolean edit  )
		throws IOException, BuildingServerException
		{
		String name, bit, link, line, title;
		int index[][];
		int swap[];
		UploadedFileSummary ufswap;
		int i, j, min, depth=0;
		int level, lastlevel;
		//File publish = req.getResource().getWebPublishFolder();
		File page;
		BufferedReader reader;
		boolean started_invisible=false, started_visible=false;
		
		log.debug( "DocumentFacility.menu()" );

		UploadedFileSummary root = session.getUploadedFileSession().getFileSummary( (String)null );
		if ( root == null )
			return;
		UploadedFileSummary[] files = session.getUploadedFileSession().getFileAndDescendentSummaries( null, true );

		index = new int[files.length][];
		for ( i=0; i<files.length; i++ )
			{
			if ( files[i].isFolder() )
				continue;
			//ignore file not in root folder
			if ( !files[i].getParentUploadedFileId().equals( root.getUploadedFileId() ) )
				continue;
			
			//ignore files with the wrong type of name
			index[i] = parseFileName( files[i] );
			if ( index[i] == null )
				continue;
			
			log.debug( "next record" );

			//skip unplaced sections if not in edit mode
			if ( index[i][0] < 1 && !edit )
				continue;

			for ( j=depth; j<4; j++ )
				if ( index[i][j]>0 )
					depth=j;
			}
			
		//sort (doesn't have to be very fast)
		for ( i=0; i<files.length; i++ )
			{
			// find lowest value among remaining files
			min=i;
			if ( index[min]==null )
				continue;             // nothing comes before a null so no sorting here

			for ( j=(i+1); j<files.length; j++ )
				{
				if ( index[j]==null )  // if it is null it comes before any non null
					{
					min = j;
					break;				//need serach no further - must already have lowest
					}

				if ( index[j][0] > index[min][0] )
					continue;
				if ( index[j][0] < index[min][0] )
					{
					min = j;
					continue;
					}
				if ( index[j][1] > index[min][1] )
					continue;
				if ( index[j][1] < index[min][1] )
					{
					min = j;
					continue;
					}
				if ( index[j][2] > index[min][2] )
					continue;
				if ( index[j][2] < index[min][2] )
					{
					min = j;
					continue;
					}
				if ( index[j][3] > index[min][3] )
					continue;
				if ( index[j][3] < index[min][3] )
					{
					min = j;
					continue;
					}
				}
			if ( min!=i )
				{
				swap = index[i];
				index[i] = index[min];
				index[min] = swap;
				ufswap = files[i];
				files[i] = files[min];
				files[min] = ufswap;
				}
			}
			
		lastlevel=-1;
		for ( i=0; i<files.length; i++ )
			{
			if ( index[i] == null )
				continue;

			//skip unplaced sections if not in edit mode
			if ( index[i][0] < 1 && !edit )
				continue;
				
			page =  session.getUploadedFileSession().getFile( files[i].getUploadedFileId() );
			if ( page == null || !page.exists() )
				continue;
			
			if ( index[i][0] == 0 )
				{
				if ( !started_invisible )
					{
					out.println( "<H4>\"Invisible\" sections</H4>" );
					out.println( "<TABLE>" );
					started_invisible=true;
					}
				}
			else
				{
				if ( !started_visible )
					{
					if ( started_invisible )
						{
						out.println( "</TABLE>" );
						out.println( "<H4>\"Visible\" sections</H4>" );
						lastlevel=-1;
						}
					out.println( "<TABLE>" );
					started_visible=true;
					}
				}
			
			// find title
			title = "Untitled Section";
			reader = new BufferedReader( new FileReader( page ) );
			boolean open_utf8=defaultUTF8;
			while ( (line = reader.readLine()) != null )
				{
				//if we find the utf8 char encoding declaration close the file
				//and reopen with utf8 encoding
				if ( !open_utf8 && line.equals("<META http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ) )
					{
					open_utf8 = true;
					reader.close();
					reader = new BufferedReader( new InputStreamReader( new FileInputStream( page ), "UTF8" ) );
					}
				//read title if present
				if ( line.startsWith("<TITLE>") && line.endsWith("</TITLE>") )
					{
					title = line.substring( 7, line.length()-8 );
					break;
					}
				}
			reader.close();
			//

			level=1;
			if ( index[i][3] > 0 )
				{level=4;}
			else
				{
				if ( index[i][2] > 0 )
					{level=3;}
				else
					{if ( index[i][1] > 0 ) level=2;}
				}		
			
			

			if ( edit )
				link="<A TARGET=viewer HREF=\""+req.absoluteURL()+files[i].getName()+"/bs_template_editdoc.html\">";
			else
				link="<A TARGET=viewer HREF=\""+req.absoluteURL()+files[i].getName()+"\">";

			
			out.print( "<TR><TD>" );
			out.println( "<H" + (3+level) + ">" );
			out.print( index[i][0] );
			if ( level>1 )
				out.print( "." + index[i][1] );
			if ( level>2 )
				out.print( "." + index[i][2] );
			if ( level>3 )
				out.print( "." + index[i][3] );
			out.println( "<H" + (3+level) + ">" );
			out.print( "</TD><TD>" );
			out.println( "</H" + (3+level) + ">" );
			out.print( link );
			out.print( title );
			out.print( "</A> " );
			out.println( "</H" + (3+level) + ">" );
			out.print( "</TD></TR>" );
			
			lastlevel=level;
			}
			
		if ( started_visible || started_invisible )
			out.println( "</TABLE>" );
		}


	private void editpage( Request req, PrintWriter out, BuildingSession session )
		throws IOException, BuildingServerException
		{
		int i;
		int[] index;
		UploadedFileSummary ufpage;
		File page;
		String title, output, line;
		BufferedReader reader;
        EscapedHtmlWriter t_out;
		
	    t_out = new EscapedHtmlWriter( out );
		        
		if ( req.getTemplateParameterCount()<1 )
			return;

		ufpage = session.getUploadedFileSession().getFileSummary( (String)req.getTemplateParameter( 0 ) );
		if ( ufpage == null )
			{
			out.println( "The requested page was not found." );
			return;
			}

		index = parseFileName( ufpage );
		if ( index == null )
			{
			out.println( "The requested page had an invalid file name." );
			return;
			}

		page =  session.getUploadedFileSession().getFile( ufpage.getUploadedFileId() );
		if ( !page.exists() )
			{
			out.println( "The file corresponding to the page was not found." );
			return;
			}
		
		out.println( "Section Number<BR>" );
		out.println( "<INPUT SIZE=3 MAXLENGTH=3 NAME=ordinal_1 VALUE=\"" + index[0] + "\"> . " );
		out.println( "<INPUT SIZE=3 MAXLENGTH=3 NAME=ordinal_2 VALUE=\"" + index[1] + "\"> . " );
		out.println( "<INPUT SIZE=3 MAXLENGTH=3 NAME=ordinal_3 VALUE=\"" + index[2] + "\"> . " );
		out.println( "<INPUT SIZE=3 MAXLENGTH=3 NAME=ordinal_4 VALUE=\"" + index[3] + "\"><BR>" );

		// find title
		title = "Untitled Section";
		//open with default char encoding first
		reader = new BufferedReader( new InputStreamReader( new FileInputStream( page ) ) );
		boolean open_utf8=defaultUTF8;
		while ( (line = reader.readLine()) != null )
			{
			//if we find the utf8 char encoding declaration close the file
			//and reopen with utf8 encoding
			if ( !open_utf8 && line.equals("<META http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">" ) )
				{
				open_utf8 = true;
				reader.close();
				reader = new BufferedReader( new InputStreamReader( new FileInputStream( page ), "UTF8" ) );
				}
			//read title if present
			if ( line.startsWith("<TITLE>") && line.endsWith("</TITLE>") )
				{
				title = line.substring( 7, line.length()-8 );
				break;
				}
			}
		out.println( "Section Title<BR>" );
		out.print( "<INPUT NAME=title     VALUE=\"" );
		//actual value ouput with some encoding
		t_out.print( title.trim() );
		out.println( "\"><BR>" );

		//ready for content...
		out.print( "<SPAN CLASS=bs-textarea><TEXTAREA COLS=45 ROWS=15 NAME=html WRAP=SOFT>" );
		//skip through to body
		while ( (line = reader.readLine()) != null )
			{
			if ( line.startsWith("<BODY") )
				break;
			}
			
		//skip past heading
		while ( (line = reader.readLine()) != null )
			{
			if ( line.startsWith("<H3>") )
				break;
			}
		
		out.flush();
		
		//output through to end of body
		while ( (line = reader.readLine()) != null )
			{
			if ( line.startsWith( "</BODY>" ) )
				break;

		    // editable text has to be output with some
		    // encoding
			t_out.print( line );
            t_out.print( "\n" );
			}

		out.println( "</TEXTAREA></SPAN>" );
		
		reader.close();
		}




	private void saveinput( Request req, PrintWriter out, BuildingSession session )
		throws IOException, BuildingServerException
		{
		int[] index, newindex;
		UploadedFileSummary ufpage, otherufpage;
		File page;
		String title, output, line, newname;
		Writer writer;
		
		String fields[], values[];
		
		log.debug( Thread.currentThread().getName() + " saveinput" );
		
		if ( !BuildingContext.getContext().checkPermission( Permission.EDIT ) || !BuildingContext.getContext().checkPermission( Permission.UPLOAD ) )
			{
			out.println( "<PRE>You don't have permission to save records in this location of the building. (requires edit and upload access rights.)</PRE>\n" );
			return;
			}

		if ( req.getTemplateParameterCount()<1 )
			return;

		ufpage = session.getUploadedFileSession().getFileSummary( (String)req.getTemplateParameter( 0 ) );
		if ( ufpage == null )
			{
			out.println( "The requested page was not found." );
			return;
			}

		index = parseFileName( ufpage );
		if ( index == null )
			{
			out.println( "The requested page had an invalid file name." );
			return;
			}

		
		newindex=new int[4];
		fields = new String[6];
		fields[0]="ordinal_1";
		fields[1]="ordinal_2";
		fields[2]="ordinal_3";
		fields[3]="ordinal_4";
		fields[4]="title";
		fields[5]="html";
		values = new String[6];
		try
			{
			for ( int i=0; i<6; i++ )
				{
				values[i]=req.getParameter( fields[i] );
				if ( i<4 )
					newindex[i] = Integer.parseInt( values[i] );
				}
			}
		catch ( NumberFormatException nfex )
			{
			out.println( "Changes were not saved because you didn't enter a valid number in one or more of the index boxes. " );
			out.println( "Please backtrack and correct this." );
			return;
			}

		if ( fields[4] == null || fields[4].trim().length()==0 )		
			{
			out.println( "Changes were not saved because you didn't enter a section title. " );
			out.println( "Please backtrack and correct this." );
			return;
			}

		newname = "" + newindex[0] + "_"+ newindex[1] + "_"+ newindex[2] + "_"+ newindex[3] + ".html";
		if ( !newname.equals( ufpage.getName() ) )
			{
			// the user wants to rename the file.
			otherufpage = session.getUploadedFileSession().getFileSummary( newname );
			if ( otherufpage != null )
				{
				out.println( "The index numbers you entered clash with another page - the old index numbers have been retained." );
				newname = ufpage.getName();
				}
			else
				{
				session.getUploadedFileSession().renameFile( ufpage.getName(), newname );
				}
			}
		
		page =  session.getUploadedFileSession().getFile( ufpage.getUploadedFileId() );
                page = File.createTempFile( "strcdoc", "html" );

		log.debug( Thread.currentThread().getName() + " saveinput A" );

                /*
                 * WebLearn inserted code; A Corfield 01/12/2003.
                 */
                  BodingtonURL bodUrl = new BodingtonURL(req);
                  values[5] = bodUrl.parseHtml(values[5]);

                  if (bodUrl.isParseWarning()) {
                    out.println( "<B>Error parsing HTML markup</B><BR>" +
                                 "Check the syntax of anchor elements and href attributes (&lt;a href=\"...\"&gt;). " +
                                 "It is also possible that a Bodington resource or uploaded file referenced by an href attribute may have been deleted." +
                                 "<BR><BR>The following erros occured:<BR>");
                    Vector warning = bodUrl.getParseWarning();
                    for (int i = 0; i < warning.size(); i++) {
                      out.println((String)warning.get(i) + "<BR>");
                    }
                    out.println("<BR><CENTER><B>The page has NOT been saved</B></CENTER>");
                    return;
                  }
		writer = new OutputStreamWriter( new FileOutputStream( page ), "UTF8" );
		writer.write( "<HTML>\r\n<HEAD>\r\n" );
		writer.write( "\r\n<LINK HREF=\"bs_virtual_auto.css\" TYPE=\"text/css\"  REL=\"STYLESHEET\">" );
		writer.write( "\r\n<TITLE>" );
		writer.write( values[4] );
		writer.write( "</TITLE>\r\n</HEAD>\r\n<BODY>\r\n<H3>" );
		writer.write( values[4] );
		writer.write( "</H3>\r\n" );
		writer.write( values[5] );
		writer.write( "\r\n</BODY>\r\n</HTML>\r\n" );
		writer.close();

         session.getUploadedFileSession().transferFile( page.getAbsolutePath(), "/" + newname, "text/html" );
                
		out.println( "<CENTER><B>The page has been saved.</B></CENTER>" );
		out.println( "<SCRIPT LANGUAGE=JavaScript>" );
		out.println( "top.buildmain.contents.location.reload()" );
		out.println( "</SCRIPT>" );
		//ufpage.setUpdatedTime( new Timestamp( System.currentTimeMillis() ) );
		//ufpage.setSize( page.length() );
		}



	private static int inc=100000;
	
	private void newpage( Request req, PrintWriter out, BuildingSession session )
		throws IOException, BuildingServerException
		{
		String name, bit, link, line, title;
		int index[][];
		int swap[];
		UploadedFileSummary ufswap;
		int i, j, min, depth=0, x;
		int level, lastlevel;
		BufferedReader reader;
		boolean started_invisible=false, started_visible=false;
		
		log.debug( "DocumentFacility.menu()" );

		x=0;
		
		UploadedFileSummary root = session.getUploadedFileSession().getFileSummary( (String)null );
		if ( root!=null )
			{
			UploadedFileSummary[] files = session.getUploadedFileSession().getFileAndDescendentSummaries( null, true );
			
			//decide on a file name - 0.x.0.0
			index = new int[files.length][];
			for ( i=0; i<files.length; i++ )
				{
				if ( files[i].isFolder() )
					continue;
				//ignore file not in root folder
				if ( !files[i].getParentUploadedFileId().equals( root.getUploadedFileId() ) )
					continue;
				
				//ignore files with the wrong type of name
				index[i] = parseFileName( files[i] );
				if ( index[i] == null )
					continue;
					
				//ignore visible pages
				if ( index[i][0]!=0 )
					continue;
					
				if ( index[i][1]>=x )
					x = index[i][1]+1;
				}
			}
			
		name = "0_" + x + "_0_0.html";
	
		File file = BuildingContext.createTempFile( "docpage", ".html" );
		if ( file == null )
		    throw new BuildingServerException( "Unable to create a temporary file." );

		FileWriter writer = new FileWriter( file );
		writer.write( "<HTML>\r\n<HEAD>\r\n<LINK HREF=\"bs_virtual_auto.css\" TYPE=\"text/css\"  REL=\"STYLESHEET\">\r\n<TITLE>" );
		writer.write( "New Section" );
		writer.write( "</TITLE>\r\n</HEAD>\r\n<BODY>\r\n<H3>" );
		writer.write( "New Section" );
		writer.write( "</H3>\r\n" );
		writer.write( "<P>\r\nEnter your text here.\r\n</P>\r\n" );
		writer.write( "\r\n</BODY>\r\n</HTML>\r\n" );
		writer.close();

		session.getUploadedFileSession().transferFile( file.getAbsolutePath(), "/"+name, "text/html" );

		out.println( "<CENTER><B>The new page has been created.</B></CENTER>" );
		out.print( "<P>The new page has been given the title 'New Section' and index number 0." );
		out.print( x );
		out.println( ".0.0 in the contents page.  Click on that heading to enter " );
		out.println( "text and change its title and/or index number. " );
		out.println( "It will remain invisible to readers of your document " );
		out.println( "until you change the index number to 1.0.0.0 or higher.</P>" );
		out.println( "<SCRIPT LANGUAGE=JavaScript>" );
		out.println( "top.buildmain.contents.location.reload()" );
		out.println( "</SCRIPT>" );
		}
		


/*
	private void deleterecord( Request req, PrintWriter out, Query q )
		throws IOException
		{
		StringTokenizer	tok;
		Enumeration enumeration;
		String sql, item, tablename, idfieldname, key, value, idfield, id;
		StringBuffer buf;
		Hashtable fields;
		int count;
		
		Logger.getLogger("org.bodington").fine( Thread.currentThread().getName() + " deleterecord" );
		
		if ( !BuildingContext.getContext().checkPermission( "edit" ) )
			{
			out.println( "<PRE>You don't have permission to save records in this location of the building.</PRE>\n" );
			return;
			}

		if ( q.idfields==null )
			return;

		//must have an id in the parameter part of the URL
		if ( req.getTemplateParameterCount() < 1 )
			return;

		//main record or sub record
		sql=null;
		if ( req.getTemplateParameterCount() > 1 )
			{
			if ( q.idfields.size()<2 )
				return;
			idfield=(String)q.idfields.elementAt( 1 );
			id=(String)req.getTemplateParameter( 1 );
			sql=q.subdeletesql;
			}
		else
			{
			if ( q.idfields.size()<1 )
				return;
			idfield=(String)q.idfields.elementAt( 0 );
			id=(String)req.getTemplateParameter( 0 );
			sql=q.deletesql;
			}
		if ( sql==null )
			return;


		// could use form fields for confirmation
		// item=req.httpreq.getParameter(name);

		sql+=" WHERE " + idfield + " = " + id;
		// add ID number or text from url

		Logger.getLogger("org.bodington").fine( sql );
		try
			{
			count=db.update( sql );
			out.println( "<P>" + count + " record deleted OK.</P>\n" );
			db.unlock();
			}
		catch ( SQLException e )
			{
			Logger.getLogger("org.bodington").fine( "</UL><P>QueryFacility SQL error:<HR> " + e + "<HR>\n" );
			out.println( "</UL><P>QueryFacility SQL error:<HR> " + e + "<HR>\n" );
			db.unlock();
			}
		catch ( IOException ioe )
			{
			Logger.getLogger("org.bodington").fine( "</UL><P>QueryFacility IO error:<HR> " + ioe + "<HR>\n" );
			out.println( "</UL><P>QueryFacility IO error:<HR> " + ioe + "<HR>\n" );
			db.unlock();
			}
		}
*/
	}


