import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.print.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.regex.*;
import java.sql.*;

/** QuizMaker 0.1 by John Phillips last revised on September 21, 2003
  * This program is the final project for TS5513.
  * It is a GUI application that allows the creation of
  * multiple choice quizzes. The quizzes can be saved as 
  * XML text or loaded from XML text files. In addition,
  * the application allows the uploading of a quiz to an
  * SQL database. A separate JSP program written in TS5503 uses
  * the database quiz and presents it to students over the web.
  *
  * Many sources were used to help create this application including
  * "Java Programming Advanced Topics, 3rd ed." by Wigglesworth and McMillan
  * "Java How to Program, 5th ed." by Deitel
  * Sun's Java Documentation and MySQL's database documentation.
  */
public class QuizMaker extends JFrame implements Printable 
{
  private JTextField statusLine;
  private JTextArea  text;
  private int numQuestions = 3;
  private QuizPanel qp[] = new QuizPanel[numQuestions];
  private JPanel jp;
  private File file = null;
  private PrinterJob pj = null;
  private PageFormat upf = null;
  private Connection connRS;

  /** Class constructor
    * @param titleText Title bar text
    */
  public QuizMaker( String titleText ) 
  {
    super( titleText );
    setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    setJMenuBar( buildMenuBar() );
    Container cp = getContentPane();
    cp.setLayout( new BorderLayout() );

    text = new JTextArea("", numQuestions+1, 40);
    text.setEditable( false );
    text.setBackground( Color.white );
    JScrollPane sp2 = new JScrollPane( text );
      
    for( int i=0; i < numQuestions; i++ ) {
      qp[i] = new QuizPanel( Integer.toString(i+1) );
    }

    jp = new JPanel( new GridLayout(numQuestions+1,1, 3, 3) );
    jp.setBackground( Color.black );

    for( int i=0; i< 3; i++ ) {
      jp.add( qp[i] );
    }

    jp.add( sp2 );

    JScrollPane sp = new JScrollPane( jp );
    cp.add( sp, BorderLayout.CENTER );

    statusLine = new JTextField();
    cp.add( statusLine, BorderLayout.SOUTH );
    setSize( 500, 400 );
    setVisible( true );
  }

  /** Present a dialog box for the user to select and open
    * an existing quiz stored in XML format */
  public void loadFile() {
    JFileChooser fileDialog = new JFileChooser();
    fileDialog.setFileSelectionMode( JFileChooser.FILES_ONLY );
    int result = fileDialog.showOpenDialog( QuizMaker.this );
    file = fileDialog.getSelectedFile();
    if ( file != null && result == JFileChooser.APPROVE_OPTION )
      try {
           FileInputStream fis
              = new FileInputStream( file ); 
         FileChannel fc = fis.getChannel();
         ByteBuffer bb
            = ByteBuffer.allocate( (int) fc.size() );
         fc.read( bb );
         text.setText( new String( bb.array() ) ); 
         fc.close(); 
         fis.close();
      } catch( IOException ioe ) {
         statusLine.setText( ioe.toString() );
    }
    convertXMLtoQuiz( text.getText() );
  }

  /** Present a dialog box for the user to choose a filename
    * to save the quiz as in XML format */
  public void saveFile() {
    this.convertQuizToXML();
    JFileChooser fileDialog = new JFileChooser();
    int result = fileDialog.showSaveDialog( QuizMaker.this );
    file = fileDialog.getSelectedFile();
    if ( file != null && result == JFileChooser.APPROVE_OPTION ) {
      try {
        FileOutputStream fis = new FileOutputStream( file ); 
        FileChannel fc = fis.getChannel();
        ByteBuffer bb = ByteBuffer.wrap( text.getText().getBytes() );
        fc.write( bb );
        fc.close(); 
        fis.close();
      } catch( IOException ioe ) {
         statusLine.setText( ioe.toString() );
      }
    }
  }

  /** Present a dialog box to allow the user to set up
    * the page and printer properties. This code is based
    * in part of code from "Java Programming Advanced Topics" */
  public void setupPage() {
    this.convertQuizToXML();     
    if ( pj == null ) {
      pj = PrinterJob.getPrinterJob();
    }
    if ( upf == null ) {
      upf = pj.defaultPage();
    }
    upf = pj.pageDialog( upf );
  }

  /** Present a dialog box to allow the user to print
    * the XML version of this quiz. This code is based
    * in part of code from "Java Programming Advanced Topics" */
  public void printFile() {
    this.convertQuizToXML();
    try {
      if ( pj == null ) {
        pj = PrinterJob.getPrinterJob();
      }
      if ( pj.printDialog() ) {
        if ( upf == null ) {
          pj.setPrintable( this );
        } else {
          pj.setPrintable( this, upf );
        }
        pj.print();
        String prtName = pj.getPrintService().getName();
        statusLine.setText( "Printing to " + prtName );
      }
    } catch( PrinterException pe ) {
      statusLine.setText( pe.toString() );
    }
  }

  /** Inserts the XML quiz text as a new record in a MySQL database */
  public void dbUpload()
  {
    this.convertQuizToXML(); 
    String myXMLQuiz = text.getText();
    StringBuffer results = new StringBuffer();
    
    try { 
      Class.forName("com.mysql.jdbc.Driver").newInstance(); 
    } 
    catch (Exception ex) { 
      System.out.println("exception thrown:");
    }

    try {
      connRS = DriverManager.getConnection("jdbc:mysql://localhost/ts5513?user=jp&password=jppw");
    }
    catch (SQLException ex) {
      System.out.println("SQLException: " + ex.getMessage()); 
      System.out.println("SQLState: " + ex.getSQLState()); 
      System.out.println("VendorError: " + ex.getErrorCode()); 
    }

    try { 
      String q = "INSERT INTO JPquizzes (cqid, qname, quiz) " +
                 "VALUES (null, 'quiz x', '" + myXMLQuiz + "')";
      PreparedStatement ps = connRS.prepareStatement( q );
      ps.executeUpdate();

      ps.close();
    }
    catch ( SQLException se ) {
      JOptionPane.showMessageDialog( null, se.getMessage(), "Database Error", JOptionPane.ERROR_MESSAGE);
      System.exit( 1 );
    }
    catch (Throwable e) {
      System.out.println("exception thrown:");
    } 
    statusLine.setText( "Quiz has been uploaded to DB" );  
  }

  /** Takes the XML stored in the text area at the bottom of the screen,
    * parses it and then populates the quiz form fields  */
  public void convertXMLtoQuiz( String s )
  {
    String output = "";
    Pattern p1 = Pattern.compile( "\\s*?<question>(.*?)</question>\\s*?" + 
                                  "<rba>(.*?)</rba>\\s*?<aa>(.*?)</aa>\\s*?" +
                                  "<rbb>(.*?)</rbb>\\s*?<ab>(.*?)</ab>\\s*?" +
                                  "<rbc>(.*?)</rbc>\\s*?<ac>(.*?)</ac>\\s*?" +
                                  "<rbd>(.*?)</rbd>\\s*?<ad>(.*?)</ad>\\s*?" +
                                  "<rbe>(.*?)</rbe>\\s*?<ae>(.*?)</ae>" );
    Matcher matcher = p1.matcher( s );
    int i = 0;
    while( matcher.find() ) {
      qp[i].setQuestion( matcher.group(1) );
      qp[i].setrba( matcher.group(2).compareTo("true")==0 ? true : false );
      qp[i].setaa(matcher.group(3) );
      qp[i].setrbb( matcher.group(4).compareTo("true")==0 ? true : false );
      qp[i].setab(matcher.group(5) );
      qp[i].setrbc( matcher.group(6).compareTo("true")==0 ? true : false );
      qp[i].setac(matcher.group(7) );
      qp[i].setrbd( matcher.group(8).compareTo("true")==0 ? true : false );
      qp[i].setad(matcher.group(9) );
      qp[i].setrbe( matcher.group(10).compareTo("true")==0 ? true : false );
      qp[i].setae(matcher.group(11) );
      ++i;       
    }
  }

  /** Converts the current quiz fields into an XML format text
    * and displays it in a text area at the bottom of the screen */
  public void convertQuizToXML()
  {
    text.setText( "" );
    for( int i=0; i< numQuestions; i++ ) {
      text.append( "<question>" + qp[i].getQuestion() + "</question>\n" );
      text.append( "<rba>" + qp[i].getrba() + "</rba>" );
      text.append( "<aa>" + qp[i].getaa() + "</aa>\n" );
      text.append( "<rbb>" + qp[i].getrbb() + "</rbb>" );
      text.append( "<ab>" + qp[i].getab() + "</ab>\n" );
      text.append( "<rbc>" + qp[i].getrbc() + "</rbc>" );
      text.append( "<ac>" + qp[i].getac() + "</ac>\n" );
      text.append( "<rbd>" + qp[i].getrbd() + "</rbd>" );
      text.append( "<ad>" + qp[i].getad() + "</ad>\n" );
      text.append( "<rbe>" + qp[i].getrbe() + "</rbe>" );
      text.append( "<ae>" + qp[i].getae() + "</ae>\n" );
    }
  }

  /** Build the menu bar, menus, and menu items for
    * the QuizMaker application. Parts of this method are from
    * "Java Programming Advanced Topics" */
  public JMenuBar buildMenuBar() {
    JMenuBar menuBar = new JMenuBar();
      JMenu fileMenu = new JMenu( "File" );
        JMenuItem exitItem = new JMenuItem( "Exit" );
        JMenuItem fileOpenItem = new JMenuItem( "File Open..." );
        JMenuItem fileSaveAsItem = new JMenuItem( "Save As..." );
        JMenuItem filePrintItem = new JMenuItem( "Print..." );
        JMenuItem filePageSetupItem = new JMenuItem( "Page Setup..." );
      JMenu databaseMenu = new JMenu( "Database" );
        JMenuItem dbUploadItem = new JMenuItem( "Upload Quiz to Database" );
      JMenu helpMenu = new JMenu( "Help" );
        JMenuItem helpAboutItem = new JMenuItem( "About QuizMaker" );

    fileOpenItem.addActionListener(
      new ActionListener() {
        public void actionPerformed( ActionEvent event ) {
          loadFile();
        }
      }
    );

    fileSaveAsItem.addActionListener(
      new ActionListener() {
        public void actionPerformed( ActionEvent event ) {
          saveFile();
        }
      }
    );

    filePrintItem.addActionListener(
      new ActionListener() {
        public void actionPerformed( ActionEvent event ) {
          printFile();
        }
      }
    );

    filePageSetupItem.addActionListener(
      new ActionListener() {
        public void actionPerformed( ActionEvent event ) {
          setupPage();
        }
      }
    );

    exitItem.addActionListener(
      new ActionListener() {
        public void actionPerformed( ActionEvent event ) {
          dispose();
          System.exit( 0 );
        }
      }
    );

    dbUploadItem.addActionListener(
      new ActionListener() {
        public void actionPerformed( ActionEvent event ) {
          statusLine.setText( "Uploading Quiz to Database..." );
          dbUpload();
        }
      }
    );

    helpAboutItem.addActionListener(
      new ActionListener() {
        public void actionPerformed( ActionEvent event ) {
          JOptionPane.showMessageDialog( null, "QuizMaker .10 by John Phillips for TS5513" );
          statusLine.setText( "QuizMaker .10 by John Phillips for TS5513" );
        }
      }
    );

    menuBar.add( fileMenu );
      fileMenu.add( fileOpenItem );
      fileMenu.add( fileSaveAsItem );
      fileMenu.add( filePageSetupItem );
      fileMenu.add( filePrintItem );
      fileMenu.add( exitItem );
    menuBar.add( databaseMenu );
      databaseMenu.add( dbUploadItem );
    menuBar.add( helpMenu );
      helpMenu.add( helpAboutItem );
    return menuBar;
  }

  /** Required by the Printable interface, this method
    * defines what is printed and how that's done
    * I borrowed this as is from Java Programming Advanced Topics.
    */
  public int print(Graphics g, PageFormat ppf, int pageIndex) {
    int pageLen = (int) ppf.getImageableHeight();
    if ( pageIndex * pageLen > text.getHeight() ) {
      return NO_SUCH_PAGE;
    }
    Graphics2D g2 = (Graphics2D) g;
    int pageShift = pageIndex * pageLen;
    g2.translate( ppf.getImageableX(), ppf.getImageableY() - pageShift );
    boolean dblbuff = text.isDoubleBuffered();
    if ( dblbuff ) {
      text.setDoubleBuffered( false );
    }
    text.paint( g2 );
    text.setDoubleBuffered( dblbuff );
    return PAGE_EXISTS;
  }

  /** The test method for the class
    * @param args not used
    */
  public static void main( String[] args ) {
    new QuizMaker( "QuizMaker" );
  }
}