/*
 * @(#)Rewriter.java	0.1 2001/08/27
 *
 * Copyright 2001 by Carnegie Mellon, All rights reserved.
 */

package ksdb;
import java.awt.event.*;
import java.sql.SQLException;
import java.util.*;
import javax.swing.*;

import edu.cmu.lti.kantoo.network.SQLConnection;
import edu.cmu.lti.kantoo.shared.*;


/**
 * Rewriter allows the user to browse and edit some sentences from the KSDB
 *
 * @author  David Svoboda
 * @version 0.1 2001/08/27
 */
public class Rewriter extends edu.cmu.lti.kantoo.clc.text.CLNotepad {

  /**
   * Properties for the Rewriter object
   */
  private PropertyManager properties = new PropertyManager(Rewriter.class);


  /**
   * The database used by this Rewriter
   */
  private final Ksdb ksdb;

  /**
   * KSDBStartup associated with this tester
   */
  private KSDBStartup startup = null;

  /**
   * Sets the startup, used upon exit
   * @param KSDBStartup startup to close upon exit
   */
  void setKSDBStartup(KSDBStartup startup) {this.startup = startup;}


  /**
   * Document being browsed. 0 = no document
   */
  private int document;

  /**
   * String of the query used to get sentences
   */
  private String query;

  /**
   * The sentences being managed
   */
  private java.sql.ResultSet sentences;

  private int sentenceCount;


  private String getSentenceText() {
    StringBuffer result = new StringBuffer();
    SQLConnection.beforeFirst( sentences);
    while (SQLConnection.next( sentences)) {
      result.append( SQLConnection.getString( sentences, 2));
      result.append( Log.LINE_SEPARATOR);
    }
    return result.toString();
  }

  /**
   * Constructs a rewriter
   * @param db        KSDB connection
   * @param document  Document being edited (0 means query)
   * @param query     Query to be sent. (NULL means document)
   * @throws LockInterruptedException if aborted while locking db
   * @throws SQLException if unable to obtain lock
   */
  public Rewriter(Ksdb db, int document, String query)
    throws Ksdb.LockInterruptedException, SQLException {
    super(new QueryDocument());
    this.ksdb = db;
    this.document = document;
    this.query = (query != null) ? query :
      "Sentence.id, Sentence.text, Sentence.previous FROM Sentence, Context"
      + " WHERE Sentence.id=Context.sentence"
      + " AND Context.document=" + document
      + " ORDER BY Context.position";
    this.sentences = ksdb.select( this.query);
    SQLConnection.last( this.sentences);
    this.sentenceCount = SQLConnection.getRow( sentences) + 1;

    setDocumentText( getSentenceText());
    if (document == 0)
      ((QueryDocument) getDocument()).setLineBreaksDisabled( true);
  }

  /**
   * Constructs a rewriter
   * @param db        KSDB connection
   * @param document  Document being edited (0 means query)
   * @param query     Query to be sent. (NULL means document)
   * @return          top-level frame
   * @throws SQLException if unable to obtain lock
   */
  public static Rewriter createFrame(Ksdb ksdb, int document, String query)
    throws Ksdb.LockInterruptedException, SQLException {
    System.setProperty("clc.text.Segmentor.sentenceDelimiters", "\n");
    final Rewriter rewriter = new Rewriter( ksdb, document, query);
    rewriter.setEnabled( true);

    final JFrame frame = new JFrame();
    JMenuBar mb = new JMenuBar();
    /*needs to be overhauled, due to new command structure
    new PropertyActionMap( rewriter.properties, "menubar",
			   rewriter.getCommands()).addtoMenubar( mb);
    new PropertyActionMap( rewriter.getProperties(), "menubar",
			   rewriter.getCommands()).addtoMenubar( mb);
    */

    if (query == null) rewriter.contextAction.setEnabled( false);

    /*
    frame.getContentPane().add("North", mb);
    frame.getContentPane().add("Center", rewriter);
    frame.pack();
    new PropertyActionMap( rewriter.properties, "Rewriter").loadDimensions( frame);
    */

    rewriter.getSaveAction().setEnabled( false);
    rewriter.getRevertAction().setEnabled( false);

    frame.addWindowListener(new WindowAdapter() {
	public void windowClosing(WindowEvent w) {rewriter.close( false);}});
    return rewriter;
  }


  /// Actions

  class ExitAction extends NonLockableAction {
    ExitAction() {super("exit");}
    public void actionPerformed(ActionEvent e) {close( true);}
  }

  /**
   * Closes the window, if user wishes it. May also exit system
   * @param cancel If true, user may cancel the exit
   */
  public void close(boolean cancel) {
    if (saveChangesDialog( cancel) == false) return;

    //    getProperties().store();
    properties.store();
    getFrame( getText()).setVisible( false);
    if (startup != null) startup.close( this);
    else System.exit(0);
  }


  class ContextAction extends NonLockableAction {

    ContextAction() {super("context");}

    public void actionPerformed(ActionEvent e) {
      int caret = getText().getCaretPosition();
      // Is there a better way to find out what line we're on?
      String text = getText().getText();
      int lines = 0;
      for (int trace = 0; trace < caret; trace = text.indexOf("\n", trace+1))
	lines++;
      SQLConnection.absolute( sentences, lines);
      String sentenceText = SQLConnection.getString( sentences, 2);
      int sentenceID = SQLConnection.getInt( sentences, 1);

      String context = new String(sentenceText);
      int prevSentences = 0;
      while ((sentenceID = ksdb.selectInt("previous FROM Sentence WHERE id="
					  + sentenceID)) != 0) {
	String contextText = ksdb.selectString("text FROM Sentence WHERE id="
					       + sentenceID);
	context = contextText + '\n' + context;
	prevSentences++;
      }

      /*
      JOptionPane.showMessageDialog( Rewriter.this, ((prevSentences == 0) ?
		     "No context" : prevSentences + " sentences of context")
				     + " for this sentence:\n\n" + context);
      */
    }
  }

  private Action contextAction = new ContextAction();


  class SaveDBAction extends LockableAction {

    SaveDBAction() {super("savedb", false);}

    public void actionPerformed(ActionEvent e) {
      List newSentences = new ArrayList();
      //      String text = getParagraph();
      StringTokenizer st = new StringTokenizer( null /*text*/, Log.LINE_SEPARATOR);
      while (st.hasMoreTokens())
	newSentences.add( st.nextToken());
      if (document != 0) {
	document = ksdb.reloadDocument( document, newSentences);
	query = "Sentence.id, Sentence.text, Sentence.previous"
	  + " FROM Sentence, Context"
	  + " WHERE Sentence.id=Context.sentence"
	  + " AND Context.document=" + document
	  + " ORDER BY Context.position";
      } else {
	SQLConnection.beforeFirst( sentences);
	Iterator iterator = newSentences.iterator();
	try {
	  while (SQLConnection.next( sentences))
	    ksdb.modifySentence( SQLConnection.getInt( sentences, 1),
				 (String) iterator.next());
	} catch (SQLException x) {ReportError.warning(x);}
      }
      Rewriter.this.sentences = ksdb.select( query);

      getSaveAction().setEnabled( false);
      getRevertAction().actionPerformed(e);
    }
  }

  class RevertDBAction extends LockableAction {

    RevertDBAction() {super("revertdb", false);}

    public void actionPerformed(ActionEvent e) {
      if (saveChangesDialog() == false) return;
      if (document == 0)
	((QueryDocument) getDocument()).setLineBreaksDisabled( false);
      //      setDocument( getDocument(), getSentenceText());
      if (document == 0)
	((QueryDocument) getDocument()).setLineBreaksDisabled( true);
      getSaveAction().setEnabled( false);
      getRevertAction().setEnabled( false);
    }
  }

  private Action revertDBAction = new RevertDBAction();
  private Action saveDBAction = new SaveDBAction();

  protected Action getSaveAction() {return saveDBAction;}
  protected Action getRevertAction() {return revertDBAction;}


  /**
   * Fetch the list of actions supported by this
   * editor.  It is implemented to return the list
   * of actions supported by the embedded JTextComponent
   * augmented with the actions defined locally.
   *
   * @return actions suported by this editor
   */
  public Action[] getActions() {
    Action[] defaultActions = {new ExitAction(),
			       contextAction,
			       getSaveAction(),
			       getRevertAction()};
    return javax.swing.text.TextAction.augmentList( super.getActions(),
						    defaultActions);
  }


  // Command-line invocation

  /**
   * Returns the language id given the name
   * @param  ksdb         KANTOO Sentence Database object
   * @param  name         Name of language
   * @return              ID of language
   * @throws SQLException if language doesn't exist in KSDB
   */
  private static int getLanguage(Ksdb ksdb, String name) throws SQLException {
    int id = ksdb.selectInt("id FROM Source_Language WHERE name="
			    + SQLConnection.quote( name.toLowerCase()));
    if (id == 0)
      throw new SQLException("Language name=" + name
			     + " doesn't exist in KSDB.");
    return id;
  }

  /**
   * Returns the document id given the language and filename
   * @param  ksdb         KANTOO Sentence Database object
   * @param  langauge     Name of language
   * @param  filename     Name of document file
   * @return              ID of document
   * @throws SQLException if document doesn't exist in KSDB
   */
  private static int getDocument(Ksdb ksdb, String language, String filename)
    throws SQLException {
    int languageID = getLanguage( ksdb, language);
    int id = ksdb.selectInt("id FROM Document WHERE language=" + languageID
			    + " AND filename=" +SQLConnection.quote(filename));
    if (id == 0)
      throw new SQLException("Document file=" + filename
			     + " doesn't exist in KSDB.");
    return id;
  }

  /**
   * Joins all the arguments after arg (inclusive) in a space-separated string
   * @param args Args sent to program
   * @param arg  First arg of string
   * @return String joining all args. may be NULL if no args provided
   */
  private static String argsRemainder(String[] args, int arg) {
    StringBuffer result = new StringBuffer();
    boolean space = false;
    for (int i = arg; i < args.length; i++) {
      if (space) result.append(' ');
      else space = true;
      if (args[i] != null) result.append( args[i]);
    }
    return result.length() == 0 ? null : new String(result);
  }

  /**
   * Main procedure
   */
  public static void main(String[] args) {
    PropertyManager.getArgs( args);
    String select = args[0];
    Rewriter rewriter = null;
    try {
      Ksdb ksdb = new Ksdb(System.getProperty("user.name"));
      System.err.println("WARNING: This method is not lock-safe!");

      if (select.equals("select")) {
	String selectCmd = argsRemainder( args, 1);
	rewriter = Rewriter.createFrame( ksdb, 0, selectCmd);
	rewriter.getFrame( rewriter.getText()).setTitle("Rewriting sentences " + selectCmd);
      } else {
	int document = getDocument(ksdb,args[0],args[1]);
	rewriter = Rewriter.createFrame( ksdb, document, null);
	rewriter.getFrame( rewriter.getText()).setTitle("Rewriting document " + args[1]);
      }
    } catch (Exception x) {ReportError.warning(x);}

    rewriter.getFrame( rewriter.getText()).setVisible( true);
  }
}

