<commit_before>/**
 * Copyright 2013, <NAME>. See LICENSE for licensing details.
 */
package us.stangl.crostex.gui;

import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

import us.stangl.crostex.Cell;
import us.stangl.crostex.CellChangeListener;
import us.stangl.crostex.Clue;
import us.stangl.crostex.FullGridChangeListener;
import us.stangl.crostex.Grid;
import us.stangl.crostex.util.Message;
import us.stangl.crostex.util.RowColumnPair;

/**
 * Panel to display clues on a side tab.
 * @author <NAME>
 */
public final class CluesPanel extends JPanel implements FullGridChangeListener, CellChangeListener {
	private static final long serialVersionUID = 1L;
	
	// length to use for text fields
	private static final int TEXT_FIELD_LENGTH = 40;
	
	// associated grid
	private final Grid grid;
	
	private JScrollPane scrollPane;

	private SquareClueAssociation[][] squaresToClues;
	
	public CluesPanel(Grid grid) {
		this.grid = grid;
		
		grid.addFullGridChangeListener(this);
		grid.addCellChangeListener(this);
		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

		regenerate();
	}
	
	public void regenerate() {
		// setup 2-dimensional array for looking up by grid coordinate later
		int nRows = grid.getHeight();
		int nCols = grid.getWidth();
		squaresToClues = new SquareClueAssociation[nRows][];
		for (int gridRow = 0; gridRow < nRows; ++gridRow) {
			squaresToClues[gridRow] = new SquareClueAssociation[nCols];
			for (int gridCol = 0; gridCol < nCols; ++gridCol) {
				squaresToClues[gridRow][gridCol] = new SquareClueAssociation();
			}
		}
		
		removeAll();
		GridBagLayout gbl = new GridBagLayout();
		//Border border = BorderFactory.createLineBorder(Color.RED);
		JPanel topPanel = new JPanel();
		//topPanel.setBorder(border);
		topPanel.setLayout(gbl);
		scrollPane = new JScrollPane(topPanel);
		
		int row = 0;
		topPanel.add(new JLabel(Message.LABEL_ACROSS.toString()), new GBC(0, row).anchor(GBC.NORTHWEST));
		topPanel.add(new JLabel(), new GBC(1, row).weightx(1.0).gridwidth(GBC.REMAINDER));
		++row;
		List<Clue> acrossClues = grid.validateAndGetAcrossClues();
		for (final Clue clue : acrossClues) {
			JLabel label = new JLabel(buildLabelString(clue));
			topPanel.add(label, new GBC(0, row).anchor(GBC.NORTHWEST));
			final JTextField textField = new JTextField(clue.getClueText(), TEXT_FIELD_LENGTH);
			boolean editable = clue.isWordComplete();
			if (editable) {
				textField.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						clue.setClueText(textField.getText());
					}
				});
				GuiUtils.addListenerToCommitOnFocusLost(textField);
			}
			textField.setEnabled(editable);
			textField.setEditable(editable);
			topPanel.add(textField, new GBC(1, row).anchor(GBC.NORTHWEST).weightx(1.0).gridwidth(GBC.REMAINDER));
			++row;
			
			RowColumnPair startOfWord = clue.getStartOfWord();
			RowColumnPair endOfWord = clue.getEndOfWord();
			int gridRow = startOfWord.row;
			for (int gridCol = startOfWord.column; gridCol <= endOfWord.column; ++gridCol) {
				SquareClueAssociation sca = squaresToClues[gridRow][gridCol];
				sca.acrossLabel = label;
				sca.acrossClueField = textField;
				sca.acrossCells = clue.getCells();
				sca.acrossClueNumber = clue.getNumber();
			}
		}
		//topPanel.add();
		topPanel.add(new JLabel(Message.LABEL_DOWN.toString()), new GBC(0, row).anchor(GBC.NORTHWEST));
		++row;
		List<Clue> downClues = grid.validateAndGetDownClues();
		for (final Clue clue : downClues) {
			JLabel label = new JLabel(buildLabelString(clue));
			topPanel.add(label, new GBC(0, row).anchor(GBC.NORTHWEST));
			final JTextField textField = new JTextField(clue.getClueText(), TEXT_FIELD_LENGTH);
			boolean editable = clue.isWordComplete();
			if (editable) {
				textField.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						clue.setClueText(textField.getText());
					}
				});
				GuiUtils.addListenerToCommitOnFocusLost(textField);
			}
			textField.setEnabled(editable);
			textField.setEditable(editable);
			topPanel.add(textField, new GBC(1, row).anchor(GBC.NORTHWEST).weightx(1.0).gridwidth(GBC.REMAINDER));
			++row;
			
			RowColumnPair startOfWord = clue.getStartOfWord();
			RowColumnPair endOfWord = clue.getEndOfWord();
			int gridCol = startOfWord.column;
			for (int gridRow = startOfWord.row; gridRow <= endOfWord.row; ++gridRow) {
				SquareClueAssociation sca = squaresToClues[gridRow][gridCol];
				sca.downLabel = label;
				sca.downClueField = textField;
				sca.downCells = clue.getCells();
				sca.downClueNumber = clue.getNumber();
			}
		}
		
//		add(topPanel);
		add(scrollPane);
	}
	
	public void handleChange(Grid grid, Cell cell, int row, int column) {
		SquareClueAssociation sca = squaresToClues[row][column];
		sca.acrossLabel.setText(buildLabelString(sca.acrossClueNumber, sca.acrossCells));
		sca.downLabel.setText(buildLabelString(sca.downClueNumber, sca.downCells));
	}
	
	@Override
	public void handleFullGridChange(Grid grid) {
		regenerate();
	}
	
	// build label string for clue based upon clue number and pattern
	private String buildLabelString(Clue clue) {
		//return Integer.toString(clue.getNumber()) + "  " + clue.getGridWord();
		return buildLabelString(clue.getNumber(), clue.getCells());
	}
	
	// build label string for clue based upon clue number and pattern from specified cells
	private String buildLabelString(int clueNumber, Cell[] cells) {
		return Integer.toString(clueNumber) + "  " + buildPatternString(cells);
	}
	
	// build pattern string from cells
	private String buildPatternString(Cell[] cells) {
		StringBuilder builder = new StringBuilder(28);
		for (Cell cell : cells)
			builder.append(cell.isEligibleForAutofill() ? "_" : cell.getContents());
		return builder.toString();
	}
	
	// private object to keep track of association between a grid square and its corresponding clues/text fields
	private static class SquareClueAssociation {

		public JLabel acrossLabel;
		public JTextField acrossClueField;
		public Cell[] acrossCells;
		public int acrossClueNumber;
		public JLabel downLabel;
		public JTextField downClueField;
		public Cell[] downCells;
		public int downClueNumber;
		
		/*
		public SquareClueAssociation(JLabel acrossLabel, JTextField acrossClueField,
				JLabel downLabel, JTextField downClueField)
		{
			this.acrossLabel = acrossLabel;
			this.acrossClueField = acrossClueField;
			this.downLabel = downLabel;
			this.downClueField = downClueField;
		}
		*/<commit_msg>Cleanup.<commit_after>/**
 * Copyright 2013, <NAME>. See LICENSE for licensing details.
 */
package us.stangl.crostex.gui;

import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

import us.stangl.crostex.Cell;
import us.stangl.crostex.CellChangeListener;
import us.stangl.crostex.Clue;
import us.stangl.crostex.FullGridChangeListener;
import us.stangl.crostex.Grid;
import us.stangl.crostex.util.Message;
import us.stangl.crostex.util.RowColumnPair;

/**
 * Panel to display clues on a side tab.
 * TODO: invalidate clue field when its corresponding grid word changes -- if necessary
 * @author <NAME>
 */
public final class CluesPanel extends JPanel implements FullGridChangeListener, CellChangeListener {
	private static final long serialVersionUID = 1L;
	
	// length to use for text fields
	private static final int TEXT_FIELD_LENGTH = 40;
	
	// associated grid
	private final Grid grid;
	
	private JScrollPane scrollPane;

	private SquareClueAssociation[][] squaresToClues;
	
	public CluesPanel(Grid grid) {
		this.grid = grid;
		
		grid.addFullGridChangeListener(this);
		grid.addCellChangeListener(this);
		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

		regenerate();
	}
	
	public void regenerate() {
		// setup 2-dimensional array for looking up by grid coordinate later
		int nRows = grid.getHeight();
		int nCols = grid.getWidth();
		squaresToClues = new SquareClueAssociation[nRows][];
		for (int gridRow = 0; gridRow < nRows; ++gridRow) {
			squaresToClues[gridRow] = new SquareClueAssociation[nCols];
			for (int gridCol = 0; gridCol < nCols; ++gridCol) {
				squaresToClues[gridRow][gridCol] = new SquareClueAssociation();
			}
		}
		
		removeAll();
		GridBagLayout gbl = new GridBagLayout();
		JPanel topPanel = new JPanel();
		topPanel.setLayout(gbl);
		scrollPane = new JScrollPane(topPanel);
		
		int row = 0;
		topPanel.add(new JLabel(Message.LABEL_ACROSS.toString()), new GBC(0, row).anchor(GBC.NORTHWEST));
		topPanel.add(new JLabel(), new GBC(1, row).weightx(1.0).gridwidth(GBC.REMAINDER));
		++row;
		List<Clue> acrossClues = grid.validateAndGetAcrossClues();
		for (final Clue clue : acrossClues) {
			JLabel label = new JLabel(buildLabelString(clue));
			topPanel.add(label, new GBC(0, row).anchor(GBC.NORTHWEST));
			final JTextField textField = new JTextField(clue.getClueText(), TEXT_FIELD_LENGTH);
			boolean editable = clue.isWordComplete();
			if (editable) {
				textField.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						clue.setClueText(textField.getText());
					}
				});
				GuiUtils.addListenerToCommitOnFocusLost(textField);
			}
			textField.setEnabled(editable);
			textField.setEditable(editable);
			topPanel.add(textField, new GBC(1, row).anchor(GBC.NORTHWEST).weightx(1.0).gridwidth(GBC.REMAINDER));
			++row;
			
			RowColumnPair startOfWord = clue.getStartOfWord();
			RowColumnPair endOfWord = clue.getEndOfWord();
			int gridRow = startOfWord.row;
			for (int gridCol = startOfWord.column; gridCol <= endOfWord.column; ++gridCol) {
				SquareClueAssociation sca = squaresToClues[gridRow][gridCol];
				sca.acrossLabel = label;
				sca.acrossClueField = textField;
				sca.acrossCells = clue.getCells();
				sca.acrossClueNumber = clue.getNumber();
			}
		}
		//topPanel.add();
		topPanel.add(new JLabel(Message.LABEL_DOWN.toString()), new GBC(0, row).anchor(GBC.NORTHWEST));
		++row;
		List<Clue> downClues = grid.validateAndGetDownClues();
		for (final Clue clue : downClues) {
			JLabel label = new JLabel(buildLabelString(clue));
			topPanel.add(label, new GBC(0, row).anchor(GBC.NORTHWEST));
			final JTextField textField = new JTextField(clue.getClueText(), TEXT_FIELD_LENGTH);
			boolean editable = clue.isWordComplete();
			if (editable) {
				textField.addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent evt) {
						clue.setClueText(textField.getText());
					}
				});
				GuiUtils.addListenerToCommitOnFocusLost(textField);
			}
			textField.setEnabled(editable);
			textField.setEditable(editable);
			topPanel.add(textField, new GBC(1, row).anchor(GBC.NORTHWEST).weightx(1.0).gridwidth(GBC.REMAINDER));
			++row;
			
			RowColumnPair startOfWord = clue.getStartOfWord();
			RowColumnPair endOfWord = clue.getEndOfWord();
			int gridCol = startOfWord.column;
			for (int gridRow = startOfWord.row; gridRow <= endOfWord.row; ++gridRow) {
				SquareClueAssociation sca = squaresToClues[gridRow][gridCol];
				sca.downLabel = label;
				sca.downClueField = textField;
				sca.downCells = clue.getCells();
				sca.downClueNumber = clue.getNumber();
			}
		}
		
//		add(topPanel);
		add(scrollPane);
	}
	
	public void handleChange(Grid grid, Cell cell, int row, int column) {
		SquareClueAssociation sca = squaresToClues[row][column];
		sca.acrossLabel.setText(buildLabelString(sca.acrossClueNumber, sca.acrossCells));
		sca.downLabel.setText(buildLabelString(sca.downClueNumber, sca.downCells));
	}
	
	@Override
	public void handleFullGridChange(Grid grid) {
		regenerate();
	}
	
	// build label string for clue based upon clue number and pattern
	private String buildLabelString(Clue clue) {
		return buildLabelString(clue.getNumber(), clue.getCells());
	}
	
	// build label string for clue based upon clue number and pattern from specified cells
	private String buildLabelString(int clueNumber, Cell[] cells) {
		return Integer.toString(clueNumber) + "  " + buildPatternString(cells);
	}
	
	// build pattern string from cells
	private String buildPatternString(Cell[] cells) {
		StringBuilder builder = new StringBuilder(28);
		for (Cell cell : cells)
			builder.append(cell.isEligibleForAutofill() ? "_" : cell.getContents());
		return builder.toString();
	}
	
	// private object to keep track of association between a grid square and its corresponding clues/text fields
	private static class SquareClueAssociation {

		public JLabel acrossLabel;
		public JTextField acrossClueField;
		public Cell[] acrossCells;
		public int acrossClueNumber;
		public JLabel downLabel;
		public JTextField downClueField;
		public Cell[] downCells;
		public int downClueNumber;
{'fraction_non_alphanumeric': 0.11312945504713727, 'fraction_numerical': 0.003985590557216218, 'mean_word_length': 4.614457831325301, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3421470', 'n_tokens_mistral': 4832, 'n_tokens_neox': 4251, 'n_words': 932}