/*
 * Alignment.java
 *
 * ...
 * ...
 * ...
 */

package aksis.alignment;

import javax.swing.*;
import java.awt.*;
import java.io.*;
import java.nio.charset.*;

/**
 * Program startup class for alignment project.
 * @author Johan Utne Poppe
 */

/*
// To display an image in the background of a JPanel,
// you'll need to paint it yourself.
// To do this, override paintComponent to draw the image:
class ImagePanel extends JPanel {
	Image image = null;
	//int x, y;
	public ImagePanel(Image image) {
		this.image = image;
		//x = 0;
		//y = 0;
	}
	public void paintComponent(Graphics g) {
		super.paintComponent(g);   //paint background
		if (image != null) {
			System.out.println("there is a picture: draw it");
			//there is a picture: draw it - tiled
			//g.drawImage(image, x, y, this);
			//int w = this.WIDTH; ### feil, men hvordan?
			//int h = this.HEIGHT; ### feil, men hvordan?
			//int imw = image.WIDTH; ### feil, men hvordan?
			//int imh = image.HEIGHT; ### feil, men hvordan?
			int w = 1280;
			int h = 1024;
			//int imw = 50;
			int imw = 432;
			//int imh = 50;
			int imh = 407;
			System.out.println("w="+w);
			System.out.println("h="+h);
			System.out.println("imw="+imw);
			System.out.println("imh="+imh);
			for (int i = 0; i < (h + imh - 1) / imh; i++) {
				for (int j = 0; j < (w + imw - 1) / imw; j++) {
					g.drawImage(image, imh * j, imw * i, this);
				}
			}
		}
	}
	//// ### noen sier man skal gjøre dette, men det var ikke nødv
	//public boolean isOpaque() {
	//	return true;
	//}
}
*/

public class Alignment {

	public static final String VERSION = "2006-10-05";   // 2006-10-05

	// ### alt eller noe av dette hører hjemme i model

	// the number of texts to align

	public static final int NUM_FILES = 2;
	//public static final int NUM_FILES = 3;

    // with the following settings we get 1-1, 0-1, 1-0, 1-2, 2-1 and 2-2 comparisons,

    // but e.g not 0-2, 2-0, 1-3, 3-3.
    // (neither do we get 0-0 comparisons because they make no sense.)
    // when trying to align elements the program
    // must select at least 0 elements from each text
    public static final int MIN_NUM_TRY = 0;
    // when trying to align elements the program
    // must select at most 2 elements from each text
    public static final int MAX_NUM_TRY = 2;
    // when trying to align elements the number of elements
    // selected from each text cannot differ by more than 1
    public static final int MAX_DIFF_TRY = 1;
    // when trying to align elements the total number of elements
    // selected from the texts cannot exceed 3
    public static final int MAX_TOTAL_TRY = 3;

    public static final String DEFAULT__SETTINGS_FILENAME = ".\\tca2.cfg";   // 2006-09-21

	public static final String DEFAULT__RELEVANT_ELEMENT_NAMES = "s head";
	public static final String DEFAULT__RELEVANT_ANCESTOR_ELEMENT_NAMES = "p div";

    public static final String DEFAULT__SPECIAL_CHARACTERS = ".,;:?!&^(){}[]'" + '"';   // characters that will be stripped off words (unless they are in the middle of words)
    public static final String DEFAULT__SCORING_CHARACTERS = "?!%";   // characters that add to the score if they occur in both tetxt

    public static final float DEFAULT__LENGTH_RATIO = 1.1f;   // ###
	public static final float MIN__LENGTH_RATIO = 0.5f;   // least allowable value of the above
	public static final float MAX__LENGTH_RATIO = 2.f;   // greatest allowable value of the above
	public static final float STEP__LENGTH_RATIO = 0.01f;   // step in the spinner component of the above

    public static final int DEFAULT__DICE_MIN_WORD_LENGTH = 5;   // default. user settable. words with length below this value will not be compared
    public static final int MIN__DICE_MIN_WORD_LENGTH = 1;   // least possible value of the above

	public static final float DEFAULT__DICE_MIN_COUNTING_SCORE = 0.7f;   // word pairs with raw dice score below this value will not get a score
	public static final float MIN__DICE_MIN_COUNTING_SCORE = 0.5f;   // least allowable value of the above
	public static final float MAX__DICE_MIN_COUNTING_SCORE = 0.9f;   // greatest allowable value of the above
	public static final float STEP__DICE_MIN_COUNTING_SCORE = 0.01f;   // step in the spinner component of the above
    //public static final int DEFAULT__CLUSTER_SCORE_METHOD = 2;
    //public static final int DEFAULT__CLUSTER_SCORE_METHOD = 1;   // 2006-04-07
    public static final int MIN__LARGE_CLUSTER_SCORE_PERCENTAGE = 0;
    public static final int MAX__LARGE_CLUSTER_SCORE_PERCENTAGE = 100;
    public static final int DEFAULT__LARGE_CLUSTER_SCORE_PERCENTAGE = 25;
    public static final int STEP__LARGE_CLUSTER_SCORE_PERCENTAGE = 5;

    public static final int DEFAULT__MAX_PATH_LENGTH = 10;
    public static final int MAX__MAX_PATH_LENGTH = 20;

	public static final float MIN__ANCHORWORD_MATCH_WEIGHT       = 0.5f;
	public static final float MIN__ANCHORPHRASE_MATCH_WEIGHT     = 0.5f;
	public static final float MIN__PROPERNAME_MATCH_WEIGHT       = 0.5f;
	public static final float MIN__DICE_MATCH_WEIGHT             = 0.5f;
	public static final float MIN__DICEPHRASE_MATCH_WEIGHT       = 0.5f;
	public static final float MIN__NUMBER_MATCH_WEIGHT           = 0.5f;
	public static final float MIN__SCORINGCHARACTER_MATCH_WEIGHT = 0.5f;
	public static final float MAX__ANCHORWORD_MATCH_WEIGHT       = 3.0f;
	public static final float MAX__ANCHORPHRASE_MATCH_WEIGHT     = 3.0f;
	public static final float MAX__PROPERNAME_MATCH_WEIGHT       = 3.0f;
	public static final float MAX__DICE_MATCH_WEIGHT             = 3.0f;
	public static final float MAX__DICEPHRASE_MATCH_WEIGHT       = 3.0f;
	public static final float MAX__NUMBER_MATCH_WEIGHT           = 3.0f;
	public static final float MAX__SCORINGCHARACTER_MATCH_WEIGHT = 3.0f;
	public static final float STEP__MATCH_WEIGHT = 0.1f;
	public static final float DEFAULT__ANCHORWORD_MATCH_WEIGHT       = 1.0f;
	//public static final float DEFAULT__ANCHORPHRASE_MATCH_WEIGHT     = 2.0f;
	public static final float DEFAULT__ANCHORPHRASE_MATCH_WEIGHT     = 1.6f;   // 2006-09-21
	//public static final float DEFAULT__PROPERNAME_MATCH_WEIGHT       = 1.5f;
	public static final float DEFAULT__PROPERNAME_MATCH_WEIGHT       = 1.3f;   // 2006-09-21
	//public static final float DEFAULT__DICE_MATCH_WEIGHT             = 0.8f;
	public static final float DEFAULT__DICE_MATCH_WEIGHT             = 1.3f;   // 2006-09-21
	public static final float DEFAULT__DICEPHRASE_MATCH_WEIGHT       = 1.6f;
	//public static final float DEFAULT__NUMBER_MATCH_WEIGHT           = 1.0f;
	public static final float DEFAULT__NUMBER_MATCH_WEIGHT           = 1.3f;   // 2006-09-21
	//public static final float DEFAULT__SCORINGCHARACTER_MATCH_WEIGHT = 1.2f;
	public static final float DEFAULT__SCORINGCHARACTER_MATCH_WEIGHT = 1.3f;   // 2006-09-21

    /*
    public static final int DEFAULT__FILE_NAMING_METHOD = 2;
    public static final String DEFAULT__CORRESP_EXTENSION = "cor";
    public static final String DEFAULT__NEWLINE_EXTENSION = "new";
	public static final String DEFAULT__CORRESP_SUFFIX = "-cor";
	public static final String DEFAULT__NEWLINE_SUFFIX = "-new";
	*/

	//public static final String DEFAULT__LOG_FILENAME = "." + File.separatorChar + "logfile.txt";

	public static final int MODE_ONE = 1;
	public static final int MODE_SKIP11 = 2;
	public static final int MODE_AUTO = 3;


	//AlignmentModel model = new AlignmentModel();

    // €€€In many circumstances, you need a chance to do some clean-up when the user shuts down your application...
    // <http://www.onjava.com/pub/a/onjava/2003/03/26/shutdownhook.html#listing1>
	public void start(AlignmentModel model) {

		//System.out.println("Alignment sin start(). Skal ta new ShutdownHook(...)");
		ShutdownHook shutdownHook = new ShutdownHook(model);
		Runtime.getRuntime().addShutdownHook(shutdownHook);

	}

    public static void main(String[] args) throws Exception {

		//System.out.println("Alignment sin main()");
        //System.out.println("skal be om å få laget model");
        AlignmentModel model = new AlignmentModel();

        // <http://www.onjava.com/pub/a/onjava/2003/03/26/shutdownhook.html#listing1>
        // model must exist before start() is run, because the latter needs it
        Alignment alignment = new Alignment();
        alignment.start(model);

        AlignGui gui = new AlignGui(model);
        //model.addView(gui);

        //JFrame frame = new JFrame("TCA2");
        //JFrame frame = new JFrame("TCA2 - Girlie Edition");
        JFrame frame = new JFrame("TCA2");

		/*
		// prøver å få til bakgrunnsbilde. fikk det til til slutt
		//Image background = java.awt.Toolkit.getDefaultToolkit().getImage("/images/bg.gif");
		//Image background = java.awt.Toolkit.getDefaultToolkit().getImage("c:/bg.gif");   // ### absolutt path funker! hvorfor er getImage() forskjellig fra ImageIcon()?
		Image background = java.awt.Toolkit.getDefaultToolkit().getImage("c:/a5.jpg");
		//Image background = java.awt.Toolkit.getDefaultToolkit().getImage("http://www.aksis.uib.no/people/images/16.jpg");
		ImagePanel contentPane = new ImagePanel(background);
		//contentPane.setOpaque(false); // ### hjalp ikke. trodde noen skrev at det hjalp. men skjønner ikke hvorfor det _skulle_ hjelpe
		contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS));   // uten denne kommer gui-komponentene rart plassert
		//contentPane.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); funker, hvis vi vil ha litt luft rundt alt sammen
		//contentPane.setBackground(new Color(0xFFFF00)); // ### dette funker (gul bakgrunnsfarge, men hvorfor funker ikke bilde)
		frame.setContentPane(contentPane);
		*/

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(gui);   // ### får ikke til bilde i bg. prøvde å la være å adde gui her, men det ble fortsatt bare grått. så det er vel ikke gui som skygger
        //contentPane.add(gui);   // ### dette alternativet hjalp ikke

        // fikk ikke til å sette menyen i gui-konstruktoren
        ////System.err.println(gui.menuBar);
        frame.setJMenuBar(gui.menuBar);
		// get the size of the default screen (€€€in principle there can be more than one screen)
		Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();

		//screenDim.setSize(800, 600);
		//screenDim.setSize(1024, 768);

		//
        //frame.setSize(1000,750);
        frame.setSize(screenDim);
        frame.setVisible(true);
        frame.setLocationRelativeTo(null);   // relative to screen

		// ### dette er vel helt feil sted å gjøre det, men jeg fikk ikke tak i den i AlignGui. var vel for tidlig
		// ### bør vel lage en eller annen initialiseringsmetode for gui, og gjøre det i den
		gui.compareInfoGraphics = gui.compareInfoPanel.getGraphics();
		//System.out.println("gui.compareInfoGraphics = " + gui.compareInfoGraphics);

		/*
		System.out.println("gui.alignedListBox[0].getWidth() = " + gui.alignedListBox[0].getWidth());
		System.out.println("gui.alignedListBox[1].getWidth() = " + gui.alignedListBox[1].getWidth());
		System.out.println("gui.toAlignListBox[0].getWidth() = " + gui.toAlignListBox[0].getWidth());
		System.out.println("gui.toAlignListBox[1].getWidth() = " + gui.toAlignListBox[1].getWidth());
		System.out.println("gui.unalignedListBox[0].getWidth() = " + gui.unalignedListBox[0].getWidth());
		System.out.println("gui.unalignedListBox[1].getWidth() = " + gui.unalignedListBox[1].getWidth());
		*/


		/*
		// 2006-02-23 match info log file
		// log file
		// ### riktig sted å gjøre dette???

		//System.out.println("Skal åpne loggfil:");
		File f = new File(model.logFilename);

        try {

			OutputStream fOut = new FileOutputStream(f);
			OutputStream bOut = new BufferedOutputStream(fOut);
			Charset cs = Charset.forName("UTF-8");
			model.logFileOut = new OutputStreamWriter(bOut, cs);

        } catch (Exception e) {

			// €€€ PLAIN_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, ERROR_MESSAGE?
			//€€€ advarselsdialog her eller i kallende kode?
			JOptionPane.showMessageDialog(
				null,
				"Can't open log file " + f.getName(),
				"€€€Title",
				JOptionPane.ERROR_MESSAGE
			);
            System.err.println("Exception when opening " + f.getName() + ": ");
            System.err.println(e.toString());
            //e.printStackTrace();

            //€€€€€€€€€€€€men hva så?

        }

		//System.out.println("Skal skrive header til loggfil:");
        try {

			String text = ">>> Start log <<<\n\n";
			//System.out.println(text);
			model.logFileOut.write(text, 0, text.length());

        } catch (Exception e) {

            System.err.println("Exception when writing to " + f.getName() + ": ");
            System.err.println(e.toString());

        }
        */

        // 2006-09-21, 2006-09-22

        // command line parameters

        // <http://www.javaworld.com/javaworld/jw-08-2004/jw-0816-command-p4.html>

		Options opt = new Options(args);

		opt.getSet().addOption("cfg", Options.Separator.EQUALS, Options.Multiplicity.ZERO_OR_ONE);   // name of file with settings
		//opt.getSet().addOption("log", Options.Multiplicity.ZERO_OR_ONE);   // ###name of log file. perhaps implement this later
		//in1 in2 out etc

		// ignoreUnmatched false
		// requireDataLast false
		if (!opt.check(false, false)) {
			ErrorMessage.error("Error(s) in command line options:\n" + opt.getCheckErrors());   // ############
			// ###print usage hints
			//..................
		}

		// normal processing

		// settings file

		boolean settingsFilenameOption = false;
		String settingsFilename = DEFAULT__SETTINGS_FILENAME;
		if (opt.getSet().isSet("cfg")) {
			// react to option -cfg
			settingsFilename = opt.getSet().getOption("cfg").getResultValue(0);
			settingsFilenameOption = true;
		}

		/*

		// input file 1

		if (opt.getSet().isSet("in1")) {
			// react to option -in1
			String logFilename = opt.getSet().getOption("in1").getResultValue(0);
		}

		// input file 2

		if (opt.getSet().isSet("in2")) {
			// react to option -in2
			String logFilename = opt.getSet().getOption("in2").getResultValue(0);
		}

		// output directory

		if (opt.getSet().isSet("out")) {
			// react to option -out
			String logFilename = opt.getSet().getOption("out").getResultValue(0);
		}

		// log file

		if (opt.getSet().isSet("log")) {
			// react to option -log
			String logFilename = opt.getSet().getOption("log").getResultValue(0);
		}
		*/

		/*
		...
		String inpfile = opt.getSet().getData().get(0);
		String outfile = opt.getSet().getData().get(1);
		...
		*/

		// ..............

		// load settings from file

		/*
		model.setSettingsFilename(settingsFilename);
		try {
			model.loadSettingsFile();
		} catch (Exception e) {   // ##########################
			// ...
			model.setSettingsFilename("");   // ###
		}
		*/

		// read settings from file and store in model
		File f = new File(settingsFilename);
		if (f.exists()) {
			SettingsDialog sD = new SettingsDialog(null, gui);   // #############instansierer for å få lov til å kalle loadFile-metoden. er dette dumt eller suspekt?
			if (!sD.loadFile(f, "model")) {
				// error messages have been shown ########vil komme feilmeldinger også i lovlig tilfelle, når default fil ikke er der
				//...
			}
		} else {
			// the settings file does not exist...
			if (settingsFilenameOption) {
				// ...and it was specified in the command line arguments
				ErrorMessage.error("Can't find the settings file specified in the -cfg option: " + settingsFilename);
			} // else the settings file was not specified in the command line arguments. the file that does not exist is the default settings file, and that is legal
		}

        // end 2006-09-21, 2006-09-22

    }

} // end class Alignment

// <http://www.onjava.com/pub/a/onjava/2003/03/26/shutdownhook.html#listing1>
class ShutdownHook extends Thread {

	AlignmentModel model;

	ShutdownHook(AlignmentModel model) {
		super();
		//System.out.println("ShutdownHook konstruktor kjører");
		this.model = model;
	}

    public void run() {

        //System.out.println("Shutting down - close log file");

		// 2006-02-23. match info log file
		// close log file

		if (model.logFileOut != null) {   // 2006-08-11
			try {
				String text = "\n\n>>> Program closed. Stop logging <<<\n\n";
				model.logFileOut.write(text, 0, text.length());
				//System.out.println("1");
			} catch (Exception ex) {
				//System.out.println("2");
				//System.err.println("Exception when writing to " + model.getLogFilename() + ": ");
				//System.err.println(ex.toString());
				ErrorMessage.error("Exception when writing to " + model.getLogFilename() + ":\n" + ex.toString());   // 2006-08-10
				//ex.printStackTrace();
				//System.out.println("3");
			}

			////if (model.getLogging()) {   // 2006-04-18
			//if (model.logFilename != "") {   // 2006-04-18. if there is a log file, close the file, even if we're not logging right now

				try {
					model.logFileOut.close();
				} catch (Exception e) {
					//System.err.println("Exception when trying to close log file");   // ###(vet ikke om jeg har navnet på loggfilen)
					//System.err.println(e.toString());
					ErrorMessage.error("Exception when trying to close log file:\n" + e.toString());   // ###(vet ikke om jeg har navnet på loggfilen)   // 2006-08-10
				}

				//System.out.println("Shutting down - log file closed");

			//}
		}

    }

}


/*
	public static final int SUBSTITUTION = 1;   // 1-1
    public static final int INSERTION    = 2;   // 0-1
    public static final int DELETION     = 3;   // 1-0
    public static final int CONTRACTION  = 4;   // 2-1
    public static final int EXPANSION    = 5;   // 1-2
    public static final int MERGE        = 6;   // 2-2
    public static final ...[] ... = {null, }
*/