/* ===================================================================== */
/** Interactive aircraft performance chart.
    @author Emmanuel Gustin, gustin@uia.ua.ac.be
    @version 1.1 of 17 October 1997
*/
/* ===================================================================== */

import java.io.*;
import java.util.*;
import java.awt.*;
import java.applet.Applet;
import java.net.*;
import PlotCanvas;
import AuxMath;
import DrawHelper;

/* ===================================================================== */
/** Interactive chart of top speeds
*/
/* ===================================================================== */
public class FighterGun extends Applet {

  // --- status variables ---
  private boolean initialised = false;
  private boolean dataread = false;
  // --- GUI members ---
  private PlotCanvas can = null;
  private TextArea   cmt = null;
  // --- data members ---
  private Vector guns = null;
  private double minyear  =    34.0;
  private double maxyear  =    99.0;
  private double minwof   =     0.0;
  private double maxwof   =    30.0;
  private int plotmode    =     0;
 
  // --- init -------------------------------------------------------
  // Is used to set up the screen elements and read the data file. 
  public void init() {
    int i;
    String plt;
    // --- prepare screen elements ---
    if (! initialised) {
      SetUpScreen();
      initialised = true;
    }
    // --- Now read data ---
    if (! dataread) {
      guns = new Vector();
      i = ReadData(guns, true);
      showStatus("Read " + i + " records");
      dataread = true;
    }
    // --- determine what to plot ---
    plt = getParameter("Plot");
    if (plt.equals("rounds")) {
      plotmode = 2;      
      minwof   =     0.0;
      maxwof   =   165.0;
    } else if (plt.equals("power")) {
      plotmode = 1;      
      minwof   =     0.0;
      maxwof   =  7500.0;
    } else {
      plotmode = 0;      
    }
  }

  // --- set up screen ---------------------------------------------
  // Puts GUI elements in the applet
  public void SetUpScreen() {
    GridBagLayout gbl;
    GridBagConstraints cns;
    Insets ins;
    int i;
    // --- background ---
    setBackground(Color.lightGray);
    // --- set Layout ---
    ins = new Insets(4, 4, 4, 4);
    gbl = new GridBagLayout();
    cns = new GridBagConstraints();
    setLayout(gbl);
    // --- set up windows ---
    // 0. init
    cns.insets    = ins;
    cns.fill      = cns.BOTH;
    // 1. canvas
    cns.weightx   = 0.9;
    cns.weighty   = 1.0;
    cns.gridx     = 0;
    cns.gridy     = 0;
    cns.gridwidth = cns.REMAINDER;
    can = new PlotCanvas();
    can.resize(500, 400);
    gbl.setConstraints(can, cns);
    add(can);
    // 3. Comment field
    cmt = new TextArea(2, 35);
    cmt.setEditable(false);
    cns.gridx     = 0;
    cns.gridy     = 2;
    cns.weightx   = 0.6;
    cns.gridwidth = 1;
    cns.gridheight = cns.REMAINDER;
    gbl.setConstraints(cmt, cns);
    add(cmt);    
    // --- show ---
    resize(600, 480);
    validate();
  }

  // --- Read Data -----------------------------------------------------
  // Reads data from a data file. Stores all of them in the vec Vector. 
  public int ReadData(Vector vec, boolean FromNet) {
    Gun s;
    FileInputStream fin;
    DataInputStream din;
    URL uin;
    String fname;
    boolean ok;
    int count;
    // read from net. Source is Applet paramater
    if (FromNet) {
      fname = getParameter("Source");
      try {
        uin = new URL(getDocumentBase(), fname);
        din = new DataInputStream(uin.openStream());
      }
      catch(MalformedURLException e) {return 0;}
      catch(IOException e) {return 0;}
    // read from local disk. 
    } else {
      try {
        fin = new FileInputStream("FighterGun.data");
        din = new DataInputStream(fin);
      }
      catch(FileNotFoundException e) {return 0;}
      catch(IOException e) {return 0;}
    }
    // --- read records ---
    ok = true;
    while (ok) {
      s = new Gun();
      ok = s.read(din);
      if (ok) {
        vec.addElement(s);
      }
    }
    return vec.size();
  }

  // --- start -----------------------------------------------------
  public void start() {
  }

  // --- paint ------------------------------------------------------
  public void paint(Graphics g) {
    Graphics gc;
    Gun ts;
    double xtick, ytick;
    int xsub, ysub;
    int i, xba, xbb, yba;
    
    if (initialised) {
      // --- set up PlotCanvas ---
      gc = can.getGraphics();
      can.clear();
      can.SetBorder(can.DEFAULT | can.GRID, can.DEFAULT | can.GRID);
      can.SetViewport(0.10, 0.95, 0.05, 0.90);
      can.SetWindow(minyear, maxyear, minwof, maxwof);
      can.SetTicks(4.0, 3.0);
      can.SetColor(Color.black);
      can.SetWidth(3);
      can.PlotBorder();
      // --- draw lines ---
      if (plotmode == 0) {
        xba = can.toPhysX(minyear);
        xbb = can.toPhysX(maxyear);
        // four Browning .303
        if ((minwof <= 1.72) && (1.72 <= maxwof)) {
          can.SetColor(Color.blue);
          yba = can.toPhysY(1.72);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
        // six Browning .50
        if ((minwof <= 3.64) && (3.64 <= maxwof)) {
          can.SetColor(Color.red);
          yba = can.toPhysY(3.64);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
        // four Hispano Mk.II
        if ((minwof <= 5.20) && (5.20 <= maxwof)) {
          can.SetColor(Color.green);
          yba = can.toPhysY(5.20);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
        // two MK 108
        if ((minwof <= 6.24) && (6.24 <= maxwof)) {
          can.SetColor(Color.yellow);
          yba = can.toPhysY(6.24);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
      } else if (plotmode == 1) {
        xba = can.toPhysX(minyear);
        xbb = can.toPhysX(maxyear);
        // four Browning .303
        if ((minwof <= 480.0) && (480.0 <= maxwof)) {
          can.SetColor(Color.blue);
          yba = can.toPhysY(480.0);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
        // six Browning .50
        if ((minwof <= 1370.0) && (1370.0 <= maxwof)) {
          can.SetColor(Color.red);
          yba = can.toPhysY(1370.0);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
        // four Hispano Mk.II
        if ((minwof <= 2010.0) && (2010.0 <= maxwof)) {
          can.SetColor(Color.green);
          yba = can.toPhysY(2010.0);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
        // two MK 108
        if ((minwof <= 795.0) && (795.0 <= maxwof)) {
          can.SetColor(Color.yellow);
          yba = can.toPhysY(795.0);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
      } else if (plotmode == 2) {
        xba = can.toPhysX(minyear);
        xbb = can.toPhysX(maxyear);
        // four Browning .303
        if ((minwof <= 152.0) && (152.0 <= maxwof)) {
          can.SetColor(Color.blue);
          yba = can.toPhysY(152.0);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
        // six Browning .50
        if ((minwof <= 78.0) && (78.0 <= maxwof)) {
          can.SetColor(Color.red);
          yba = can.toPhysY(78.0);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
        // four Hispano Mk.II
        if ((minwof <= 40.0) && (40.0 <= maxwof)) {
          can.SetColor(Color.green);
          yba = can.toPhysY(40.0);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
        // two MK 108
        if ((minwof <= 20.0) && (20.0 <= maxwof)) {
          can.SetColor(Color.yellow);
          yba = can.toPhysY(20.0);
          can.DrawFatLine(gc, xba, yba, xbb, yba, 2);
        }
      }
      // --- plot data ---
      if (dataread) {
        can.SetColor(Color.red);
        can.SetStyle(can.SYMBOLS);
        can.SetSymbol('\u2756', new Font("sansserif", Font.PLAIN, 10));
        can.SetWidth(1);
        if (plotmode == 0) {
          for (i = 0; i < guns.size(); i++) {
            ts = ((Gun) guns.elementAt(i));
            if ((ts.year >= minyear) && (ts.year <= maxyear) &&
               (ts.wof >= minwof) && (ts.wof <= maxwof)) {
              ts.PlotCanvasWof(can, gc);
            }
          }
        } else if (plotmode == 1) {
          for (i = 0; i < guns.size(); i++) {
            ts = ((Gun) guns.elementAt(i));
            if ((ts.year >= minyear) && (ts.year <= maxyear) &&
               (ts.mpw >= minwof) && (ts.mpw <= maxwof)) {
              ts.PlotCanvasMpw(can, gc);
            }
          }
        } else if (plotmode == 2) {
          for (i = 0; i < guns.size(); i++) {
            ts = ((Gun) guns.elementAt(i));
            if ((ts.year >= minyear) && (ts.year <= maxyear) &&
               (ts.rps >= minwof) && (ts.rps <= maxwof)) {
              ts.PlotCanvasRps(can, gc);
            }
          }
        }
      }
    }
    // --- validate ---
    validate();
  }

  // --- Event Handler ----------------------------------------------
  // Handled events:
  //  WINDOW_EXPOSE and WINDOW_DESTROY, of course.
  //  ACTION_EVENT returned by TwoSliders ruler.
  //  ACTION_EVENT returend by PlotCanvas in ZOOM, UNZOOM
  //  MOUSE_DOWN in PlotCanvas
  //  KEYPRESS '\n' to replot with matching
  // These events modify the list of elements that are to be
  // plotted by calling the filters.
  //
  public boolean handleEvent(Event evt) {
    Gun ref;
    switch (evt.id) {
    case Event.WINDOW_EXPOSE:
      repaint(250);
      return(true);
    case Event.WINDOW_DESTROY:
      System.exit(0);
      return(true);
    case Event.ACTION_EVENT:
      if (evt.target == can) {
        if (((String) evt.arg).equals("ZOOM")) {
          minyear  = can.wx0;
          maxyear  = can.wx1;
          minwof   = can.wy0;
          maxwof   = can.wy1;
          repaint();
          return(true);
        } else if (((String) evt.arg).equals("UNZOOM")) {
          minyear = 34.0;
          maxyear = 99.0;
          if (plotmode == 0) {
            minwof  =    0.0;
            maxwof  =   30.0;
          } else if (plotmode == 1) {
            minwof  =    0.0;
            maxwof  = 6500.0;
          } else if (plotmode == 2) {
            minwof  =    0.0;
            maxwof  =  165.0;
          }
          repaint();
          return(true);
        }
      }
    case Event.MOUSE_DOWN:
      if (evt.target == can) {
        ref = Closest(evt.x, evt.y, plotmode);
        cmt.setText(ref.getText());
      }
    }
    return(false);
  }

  // --- Seek closest element on the canvas -------------------------
  public Gun Closest(int x, int y, int mode) {
    Gun ref, ts;
    double sp, le;
    double r, s, dist, t;
    int i;
    // --- first convert ---
    sp = can.toWorldX(x);
    le = can.toWorldY(y);
    dist = 0.0;
    ref = null;
    // --- next seek closest ---
    if (mode == 0) {
      for (i = 0; i < guns.size(); i++) {
        ts = ((Gun) guns.elementAt(i));
        r = (sp - ts.year) / 12.0;
        s = (le - ts.wof)  / 15.0;
        t = r * r + s * s;
        if ((i == 0) || (t < dist)) {
          dist = t;
          ref = ts;
        }
      }
    } else if (mode == 1)  {
      for (i = 0; i < guns.size(); i++) {
        ts = ((Gun) guns.elementAt(i));
        r = (sp - ts.year) / 12.0;
        s = (le - ts.mpw)  / 3250.0;
        t = r * r + s * s;
        if ((i == 0) || (t < dist)) {
          dist = t;
          ref = ts;
        }
      }
    } else if (mode == 2)  {
      for (i = 0; i < guns.size(); i++) {
        ts = ((Gun) guns.elementAt(i));
        r = (sp - ts.year) / 12.0;
        s = (le - ts.rps)  / 32.5;
        t = r * r + s * s;
        if ((i == 0) || (t < dist)) {
          dist = t;
          ref = ts;
        }
      }
    }
    // --- return ---
    return(ref);
  }

}

/* ===================================================================== */
/** Represents an aircraft with its top speed at a given level. Has 
    reading and plotting procedures, and a few helper methods.
*/
/* ===================================================================== */
class Gun {

  // --- country flag constants ---
  public final int COUNTRY_INTERNATIONAL =  1;
  public final int COUNTRY_BRITAIN       =  2;
  public final int COUNTRY_CZECH         =  3;
  public final int COUNTRY_FRANCE        =  4;
  public final int COUNTRY_GERMANY       =  5;
  public final int COUNTRY_ITALY         =  6;
  public final int COUNTRY_JAPAN         =  7;
  public final int COUNTRY_NETHERLANDS   =  8;
  public final int COUNTRY_POLAND        =  9;
  public final int COUNTRY_SWEDEN        = 10;
  public final int COUNTRY_USA           = 11;
  public final int COUNTRY_USSR          = 12;
  public final int COUNTRY_AUSTRIA       = 13;
  public final int COUNTRY_CANADA        = 14;
  public final int COUNTRY_AUSTRALIA     = 15;
  public final int COUNTRY_ISRAEL        = 16;
  public final int COUNTRY_FINLAND       = 17;
  
  // --- data members -----------------------------------------------
  protected String name;
  protected String arms;
  protected double year;
  protected int country;
  protected double wof;
  protected double mpw;
  protected double rps;

  // --- read a member from a stream --------------------------------
  public boolean read(DataInputStream di) {
    String str;
    StringTokenizer stk;
    int k, i, j;
    double month;
    char c;
    // --- try to read ---
    try {
      // first loop to skip over comment lines
      do  {
        str = di.readLine();
        if (str.length() == 0) {return(false);}
        c = str.charAt(0);
      } while (c == '#');
      // If ok, assign it as name of this record
      name = str;
      // Read descriptive line
      str = di.readLine();
      if (str == null) {return(false);}
      stk = new StringTokenizer(str);
      if (stk.countTokens() < 2) {return(false);}
      year    = Double.valueOf(stk.nextToken()).doubleValue();
      month   = Month2Double(stk.nextToken());
      year    = year + month;
      country = Country2Flag(stk.nextToken());
      // read firepower line
      str = di.readLine();
      if (str == null) {return(false);}
      stk = new StringTokenizer(str);
      if (stk.countTokens() < 2) {return(false);}
      wof     = Double.valueOf(stk.nextToken()).doubleValue();
      mpw     = Double.valueOf(stk.nextToken()).doubleValue();
      rps     = Double.valueOf(stk.nextToken()).doubleValue();
      // Read guns line
      str = di.readLine();
      if (str == null) {return(false);}
      arms = str.trim();
      // done
      return(true);
    }
    // --- catch blocks ---
    // for the moment, just stop reading the record. No clean up.
    // Caller should not try to store the results, and they will
    // go away... 
    catch(IOException e) {
      System.out.println("  ! IO " + e);
      return(false);
    }
    catch(NumberFormatException e) {
      System.out.println("  ! NumberFormat " + e);
      return(false);
    }
    catch(StringIndexOutOfBoundsException e) {
      System.out.println("  ! StringIndexOutOfBounds " + e);
      return(false);
    }
  }

  // --- plot on canvas -----------------------------
  public void PlotCanvasWof(PlotCanvas can, Graphics g) {
    int ix, iy;
    double si = 12.0;
    // --- get point ---
    ix = can.toPhysX(year);
    iy = can.toPhysY(wof);
    // --- draw ---
    switch (country) {
    case COUNTRY_BRITAIN :     DrawHelper.drawBritain(g, ix, iy, si); break;
    case COUNTRY_FRANCE :      DrawHelper.drawFrance(g, ix, iy, si);  break;
    case COUNTRY_GERMANY :     DrawHelper.drawGermany(g, ix, iy, si); break;
    case COUNTRY_ITALY :       DrawHelper.drawItaly(g, ix, iy, si);   break;
    case COUNTRY_JAPAN :       DrawHelper.drawJapan(g, ix, iy, si);   break;
    case COUNTRY_POLAND :      DrawHelper.drawPoland(g, ix, iy, si);  break;
    case COUNTRY_USSR :        DrawHelper.drawUSSR(g, ix, iy, si);    break;
    case COUNTRY_USA :         DrawHelper.drawUSA(g, ix, iy, si);     break;
    case COUNTRY_NETHERLANDS : DrawHelper.drawNetherlands(g, ix, iy, si); break;
    case COUNTRY_CZECH :       DrawHelper.drawCzech(g, ix, iy, si);   break;  
    case COUNTRY_SWEDEN :      DrawHelper.drawSweden(g, ix, iy, si);  break;  
    case COUNTRY_AUSTRIA :     DrawHelper.drawAustria(g, ix, iy, si); break;  
    case COUNTRY_INTERNATIONAL :
    case COUNTRY_CANADA :
    case COUNTRY_AUSTRALIA :
    case COUNTRY_ISRAEL :
    case COUNTRY_FINLAND :
    default :
      DrawHelper.drawUnknown(g, ix, iy, si);
      break;
    }
  }

  // --- plot on canvas -----------------------------
  public void PlotCanvasMpw(PlotCanvas can, Graphics g) {
    int ix, iy;
    double si = 12.0;
    // --- get point ---
    ix = can.toPhysX(year);
    iy = can.toPhysY(mpw);
    // --- draw ---
    switch (country) {
    case COUNTRY_BRITAIN :     DrawHelper.drawBritain(g, ix, iy, si); break;
    case COUNTRY_FRANCE :      DrawHelper.drawFrance(g, ix, iy, si);  break;
    case COUNTRY_GERMANY :     DrawHelper.drawGermany(g, ix, iy, si); break;
    case COUNTRY_ITALY :       DrawHelper.drawItaly(g, ix, iy, si);   break;
    case COUNTRY_JAPAN :       DrawHelper.drawJapan(g, ix, iy, si);   break;
    case COUNTRY_POLAND :      DrawHelper.drawPoland(g, ix, iy, si);  break;
    case COUNTRY_USSR :        DrawHelper.drawUSSR(g, ix, iy, si);    break;
    case COUNTRY_USA :         DrawHelper.drawUSA(g, ix, iy, si);     break;
    case COUNTRY_NETHERLANDS : DrawHelper.drawNetherlands(g, ix, iy, si); break;
    case COUNTRY_CZECH :       DrawHelper.drawCzech(g, ix, iy, si);   break;  
    case COUNTRY_SWEDEN :      DrawHelper.drawSweden(g, ix, iy, si);  break;  
    case COUNTRY_AUSTRIA :     DrawHelper.drawAustria(g, ix, iy, si); break;  
    case COUNTRY_INTERNATIONAL :
    case COUNTRY_CANADA :
    case COUNTRY_AUSTRALIA :
    case COUNTRY_ISRAEL :
    case COUNTRY_FINLAND :
    default :
      DrawHelper.drawUnknown(g, ix, iy, si);
      break;
    }
  }

  // --- plot on canvas -----------------------------
  public void PlotCanvasRps(PlotCanvas can, Graphics g) {
    int ix, iy;
    double si = 12.0;
    // --- get point ---
    ix = can.toPhysX(year);
    iy = can.toPhysY(rps);
    // --- draw ---
    switch (country) {
    case COUNTRY_BRITAIN :     DrawHelper.drawBritain(g, ix, iy, si); break;
    case COUNTRY_FRANCE :      DrawHelper.drawFrance(g, ix, iy, si);  break;
    case COUNTRY_GERMANY :     DrawHelper.drawGermany(g, ix, iy, si); break;
    case COUNTRY_ITALY :       DrawHelper.drawItaly(g, ix, iy, si);   break;
    case COUNTRY_JAPAN :       DrawHelper.drawJapan(g, ix, iy, si);   break;
    case COUNTRY_POLAND :      DrawHelper.drawPoland(g, ix, iy, si);  break;
    case COUNTRY_USSR :        DrawHelper.drawUSSR(g, ix, iy, si);    break;
    case COUNTRY_USA :         DrawHelper.drawUSA(g, ix, iy, si);     break;
    case COUNTRY_NETHERLANDS : DrawHelper.drawNetherlands(g, ix, iy, si); break;
    case COUNTRY_CZECH :       DrawHelper.drawCzech(g, ix, iy, si);   break;  
    case COUNTRY_SWEDEN :      DrawHelper.drawSweden(g, ix, iy, si);  break;  
    case COUNTRY_AUSTRIA :     DrawHelper.drawAustria(g, ix, iy, si); break;  
    case COUNTRY_INTERNATIONAL :
    case COUNTRY_CANADA :
    case COUNTRY_AUSTRALIA :
    case COUNTRY_ISRAEL :
    case COUNTRY_FINLAND :
    default :
      DrawHelper.drawUnknown(g, ix, iy, si);
      break;
    }
  }

  // --- gettext ---------------------------------------------
  public String getText() {
    String str =   name + "    ("+ getCountry() + ")\n"
                 + arms;
    return(str);                 
  }                    

  // --- country to flag -----------------------------------------
  public int Country2Flag(String str) {
    int flag;
    if (str.equals("International")) {         flag = COUNTRY_INTERNATIONAL;
    } else if (str.equals("Austria")) {        flag = COUNTRY_AUSTRIA;
    } else if (str.equals("Britain")) {        flag = COUNTRY_BRITAIN;
    } else if (str.equals("Czechoslovakia")) { flag = COUNTRY_CZECH;
    } else if (str.equals("France")) {         flag = COUNTRY_FRANCE;
    } else if (str.equals("Germany")) {        flag = COUNTRY_GERMANY;
    } else if (str.equals("Italy")) {          flag = COUNTRY_ITALY;
    } else if (str.equals("Japan")) {          flag = COUNTRY_JAPAN;
    } else if (str.equals("Netherlands")) {    flag = COUNTRY_NETHERLANDS;
    } else if (str.equals("Poland")) {         flag = COUNTRY_POLAND;
    } else if (str.equals("Sweden")) {         flag = COUNTRY_SWEDEN;
    } else if (str.equals("USA")) {            flag = COUNTRY_USA;
    } else if (str.equals("USSR")) {           flag = COUNTRY_USSR;
    } else if (str.equals("Israel")) {         flag = COUNTRY_ISRAEL;
    } else if (str.equals("Australia")) {      flag = COUNTRY_AUSTRALIA;
    } else if (str.equals("Canada")) {         flag = COUNTRY_CANADA;
    } else if (str.equals("Finland")) {        flag = COUNTRY_FINLAND;
    } else {
      flag = 0;
      System.out.println(str);
    }
    return(flag);
  }

  // --- month to value ------------------------------------------
  public double Month2Double(String str) {
    double v;
    if (str.equals("Jan"))          {  v = 0.04;
    } else if (str.equals("Feb"))   {  v = 0.13;
    } else if (str.equals("Mar"))   {  v = 0.21;
    } else if (str.equals("Apr"))   {  v = 0.29;
    } else if (str.equals("May"))   {  v = 0.38;
    } else if (str.equals("Jun"))   {  v = 0.46;
    } else if (str.equals("Jul"))   {  v = 0.54;
    } else if (str.equals("Aug"))   {  v = 0.63;
    } else if (str.equals("Sep"))   {  v = 0.71;
    } else if (str.equals("Oct"))   {  v = 0.79;
    } else if (str.equals("Nov"))   {  v = 0.88;
    } else if (str.equals("Dec"))   {  v = 0.96;
    } else if (str.equals("Early")) {  v = 0.2;
    } else if (str.equals("Mid"))   {  v = 0.5;
    } else if (str.equals("Late"))  {  v = 0.8;
    } else {                           v = 0.5;
    }
    return v;
  }

  // --- country to flag -----------------------------------------
  public String getCountry() {
    switch (country) {
    case COUNTRY_INTERNATIONAL : return("International");
    case COUNTRY_AUSTRIA :       return("Austria");
    case COUNTRY_BRITAIN :       return("Britain");
    case COUNTRY_CZECH :         return("Czechoslovakia");
    case COUNTRY_FRANCE :        return("France");
    case COUNTRY_GERMANY :       return("Germany");
    case COUNTRY_ITALY :         return("Italy");
    case COUNTRY_JAPAN :         return("Japan");
    case COUNTRY_NETHERLANDS :   return("The Netherlands");
    case COUNTRY_POLAND :        return("Poland");
    case COUNTRY_SWEDEN :        return("Sweden");
    case COUNTRY_USA :           return("USA");
    case COUNTRY_USSR :          return("USSR");
    case COUNTRY_CANADA :        return("Canada");
    case COUNTRY_AUSTRALIA :     return("Australia");
    case COUNTRY_ISRAEL :        return("Israel");
    case COUNTRY_FINLAND :       return("Finland");
    default :                    break;
    }
    return("Unknown");
  }

}

/* ===================================================================== */
/** Class to separate tokens in a string and put them in an array,
    then compare with a string to see whether the String contains
    one of the tokens.
*/
/* ===================================================================== */

class TokenMatcher {

  String tokens[];
  int countTokens;

  // --- constructor ---------------------------------------------------
  public TokenMatcher(String s) {
    StringTokenizer st = new StringTokenizer(s);
    int i;
    // --- init ---
    countTokens = st.countTokens();
    // --- loop ---
    if (countTokens != 0) {
      tokens = new String[countTokens];
      for (i = 0; i < countTokens; i++) {
        tokens[i] = st.nextToken();
      }
    }
  }

  // --- matcher -------------------------------------------------------
  public boolean matches(String s) {
    int i = 0;
    boolean b = false;
    if (countTokens == 0) return(true);
    while ((! b) && (i < countTokens)){
      b = (s.indexOf(tokens[i]) != -1);
      i++;
    }
    return(b);
  }

}

