package org.noritan.randomword;

/**
 * Title: Random Word Generator Model
 * File: RandomWordGenerator
 * Copyright: Copyright (c) 2005 noritan
 * Organization: noritan.org
 */

import java.util.Random;

import javax.swing.event.EventListenerList;

/**
 * generates the specified numbers of random words with specified characters
 * in specified length.
 * The number of random words to be generated is specified by the
 * wordCount property,
 * the characters to be used to generate the random words is specified
 * by the seedString property,
 * and the  length of the random words to be generated is specified
 * by the wordLnegth propert.
 * The random words are generated in the words property
 * by invoking the generate method.
 *
 * @author noritan
 * @version 1.0
 */
public class RandomWordGenerator
{
  /**
   * Default value of the seedString property.
   *
   * @see #seedString
   * @see #setSeedString
   * @see #getSeedString
   */
  private static final String DEFAULT_SEED_STRING =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";

  /**
   * The value of the seedString property.
   *
   * @ see #DEFAULT_SEED_STRING
   * @see #setSeedString
   * @see #getSeedString
   */
  private String seedString = DEFAULT_SEED_STRING;

  /**
   * Set the characters to be used to generate random words.
   * The random words generated by this object is made of
   * the characters specified by this property.
   *
   * @param seedString a set of characters to be used to construct
   * the random words. If null is provided,
   * the default value is used instead. As the result,
   * the seedString property never becomes a null.
   * @see #getSeedString
   */
  public void setSeedString(final String seedString) {
    final String newSeedString;
    if (seedString == null) {
      newSeedString = DEFAULT_SEED_STRING;
    } else {
      newSeedString = seedString;
    }
    this.seedString = newSeedString;
  }

  /**
   * Returns the characters to be used to generate random words.
   *
   * @return the characters to be used to generate random words.
   * @see #setSeedSrting
   */
  public String getSeedString() {
    return seedString;
  }

  /**
   * Default value of the wordLength property.
   *
   * @see #wordLength
   * @see #setWordLength
   * @see #getWordLength
   */
  private static final int DEFAULT_WORD_LENGTH = 1;

  /**
   * The value of the wordLength property.
   *
   * @see #DEFAULT_WORD_LENGTH
   * @see #setWordLength
   * @see #getWordLength
   */
  private int wordLength = DEFAULT_WORD_LENGTH;

  /**
   * Set the length of a random word to be generated.
   * This object generates the random words which length are specified
   * by this property.
   * This property must be greater than ZERO.
   * An IllegalArgumentException is thrown if the word length
   * specified by the parameter is not grater than ZERO.
   *
   * @param wordLength the length of a random word to be generated.
   * @throws IllegalArgumentException if the word length specified
   * by the parameter is not grater than ZERO.
   * @see #getWordLength
   */
  public void setWordLength(final int wordLength) throws IllegalArgumentException {
    if (wordLength <= 0) {
      throw new IllegalArgumentException("Word length must be > 0");
    }
    this.wordLength = wordLength;
  }

  /**
   * Returns the length of a random word to be generated.
   *
   * @return the length of a random word to be generated.
   * @see #setWordLength
   */
  public int getWordLength() {
    return wordLength;
  }

  /**
   * Default value of the wordCount property.
   *
   * @see #wordCount
   * @see #setWordCount
   * @see #getWordCount
   */
  private static final int DEFAULT_WORD_COUNT = 1;

  /**
   * The value of the wordCount property.
   *
   * @see #DEFAULT_WORD_COUNT
   * @see #setWordCount
   * @see #getWordCount
   */
  private int wordCount = DEFAULT_WORD_COUNT;

  /**
   * Set the number of random words to be generated.
   * This object generates any numbers of the random words speicfied
   * by this property.
   * This property must be greater or equal to ZERO.
   * An IllegalArgumentException is thrown if the word count
   * specified by the parameter is less than ZERO.
   *
   * @param wordCount the number of random words to be generated.
   * @throws IllegalArgumentException if the word count
   * specified by the parameter is less than ZERO.
   * @see #getWordCount
   */
  public void setWordCount(final int wordCount) throws IllegalArgumentException {
    if (wordCount < 0) {
      throw new IllegalArgumentException("Word count must be >= 0");
    }
    this.wordCount = wordCount;
  }

  /**
   * Returns the number of random words to be generated.
   *
   * @return the number of random words to be generated.
   * @see #setWordCount
   */
  public int getWordCount() {
    return wordCount;
  }

  /**
   * The value of words property.
   *
   * @see #setWords
   * @see #getWords
   */
  private String[] words;

  /**
   * Set the value of words property.
   * This method is implemented as private because
   * the property is a Read-only property.
   *
   * @param words an array of generated random words.
   * @see #words
   * @see #getWords
   */
  private void setWords(String[] words) {
    this.words = words;
  }

  /**
   * Returns the array of generated random words.
   *
   * @return the array of generated random words or null if
   * the object has never generated any random words.
   */
  public String[] getWords() {
    return words;
  }

  /**
   * Construct a random word generator object.
   */
  public RandomWordGenerator() {
    super();
  }

  /**
   * generate the random words.
   * A RandomWordGenerationEvent is issued to the event listeners
   * after the generation is completed.
   *
   * @see #addRandomWordGenerationListener
   * @see RandomWordGenerationEvent
   * @see RandomWordGenerationListener
   */
  public synchronized void generate() {
    Random random = new Random();
    String[] words = new String[wordCount];
    for (int i = 0; i < words.length; i++) {
      words[i] = generateOne(random);
    }
    setWords(words);
    fireGenerated();
  }

  /**
   * Generate a random word.
   *
   * @param random random number object to be used to generate a
   * random word.
   *
   * @return a generated random word.
   * @see #wordLength
   * @see #seedString
   */
  private String generateOne(Random random) {
    char[] buf = new char[wordLength];
    for (int j = 0; j < buf.length; j++) {
      int index = random.nextInt(seedString.length());
      buf[j] = seedString.charAt(index);
    }
    return new String(buf);
  }

  /**
   * A list of listeners.
   *
   * @see #addRandomWordGenerationListener
   * @see #removeRandomWordGenerationListener
   */
  private final EventListenerList listenerList = new EventListenerList();

  /**
   * Adds a RandomWordGenerationLstener.
   *
   * @param listener the RandomWordGenerationLstener to be added.
   */
  public synchronized void addRandomWordGenerationListener(
    RandomWordGenerationListener listener) {
    listenerList.add(RandomWordGenerationListener.class, listener);
  }

  /**
   * Removes a RandomWordGenerationLstener.
   *
   * @param listener the RandomWordGenerationLstener to be removed.
   */
  public synchronized void removeRandomWordGenerationListener(
    RandomWordGenerationListener listener) {
    listenerList.remove(RandomWordGenerationListener.class, listener);
  }

  /**
   * Notifies the RandomWordGenerationEvent to the listeners.
   *
   * @see #listenerList
   * @see RandomWordGenerationEvent
   * @see RandomWordGenerationListener
   */
  private void fireGenerated() {
    RandomWordGenerationEvent event = new RandomWordGenerationEvent(this);
    RandomWordGenerationListener[] listeners = (RandomWordGenerationListener[])
      listenerList.getListeners(RandomWordGenerationListener.class);
    for (int i = 0; i < listeners.length; i++) {
      listeners[i].generated(event);
    }
  }
}