// GridAreaFricative.java -- Richard W. DeVaul -- DATE

/* $Id$ */

/*
$Log$
*/

import java.awt.*;
import java.io.*;
import java.util.*;
import java.net.*;

public class GridAreaNasal extends GridAreaTwo {

				// ***************
				// ** Constants **
				// ***************

  public static final int stop = 0x0100;
  public static final int fricative = 0x0200;
  public static final int affricate = 0x0400;
  public static final int nasal = 0x0800;
  public static final int liquid = 0x1000;
  public static final int glide = 0x2000;
  
  public static final int moved = 0x4000;


  public static final int stopC      = Color.HSBtoRGB(0.0f,1.0f,1.0f);
  public static final int fricativeC = Color.HSBtoRGB(4.0f/6.0f,1.0f,1.0f);
  public static final int affricateC = Color.HSBtoRGB(1.0f/6.0f,1.0f,1.0f);
  public static final int nasalC     = Color.HSBtoRGB(3.0f/6.0f,1.0f,1.0f);
  public static final int liquidC    = Color.HSBtoRGB(2.0f/6.0f,1.0f,1.0f);
  public static final int glideC     = Color.HSBtoRGB(5.0f/6.0f,1.0f,1.0f);
  

  public static final Color stopColor = new Color(stopC);
  public static final Color fricativeColor = new Color(fricativeC);
  public static final Color affricateColor = new Color(affricateC);
  public static final Color nasalColor = new Color(nasalC);
  public static final Color liquidColor = new Color(liquidC);
  public static final Color glideColor = new Color(glideC);

				// *****************
				// ** Member data **
				// *****************

  public boolean drawColors;

  protected int frequency;
  protected int maxFrequency;
  
  protected int counter;
  char newRaster[][];
				// ********************
				// ** Member methods **
				// ********************

  public GridAreaNasal(Container p, int r, int c, int maxFreq) {
    super(p,r,c);
    frequency = 0;
    maxFrequency = maxFreq;
    drawColors = true;
  }

				// Expose frequency.

  public void setFrequency(int freq) {
    frequency=freq;
  }

  public int getFrequency() {
    return frequency;
  }

 public int getMaxFrequency() {
   return maxFrequency;
 }
				// Process a key press, overrides
				// processKey in gridArea.java

  protected void processKey(char key) {
    counter++;
    
    doCA();
    super.processKey(key);
  }

				// All subsequent methods used
				// internally for CA operation.

				// Is character a stop.

  protected boolean isVowel(char c) {
    c = Character.toLowerCase(c);

    switch(c) {
    case 'a':
    case 'e':
    case 'i':
    case 'o':
    case 'u':
      return true;
    }
    return false;
  }

  protected char getSanatizedChar(int i,
				  int j) {
    int c = (int)raster[wrapRows(i)][wrapColumns(j)];
    c &= 0x00ff;
    return Character.toLowerCase((char)c);
  }

  protected boolean isStop(int i,
			   int j) {
    char b = getSanatizedChar(i,j-1);
    char c = getSanatizedChar(i,j);
    char d = getSanatizedChar(i,j+1);

    switch (c) {
    case 'p':
    case 'b':
    case 'd':
    case 'k':
    case 'q':
      return true;
    case 't':			
      if (d != 'h') {		// 'th' is a fricative
	return true;
      }
      break;
    case 'g':
      if (b != 'n') {		// 'ng' is a nasal
	return true;
      }
      break;
    }
    return false;
  }
  
  protected boolean isFricative(int i,
				int j) {

    char a = getSanatizedChar(i,j-2);
    char b = getSanatizedChar(i,j-1);
    char c = getSanatizedChar(i,j);
    char d = getSanatizedChar(i,j+1);
    
    switch (c) {
    case 'f':			// Do the one character cases
    case 'v':
    case 's':
    case 'z':
      return true;
				// 'h' is tricky because it can also
				// be part of an affricate or a glide.
    case 'h':
      switch (b) {
      case 'c':			// 'ch' is an affricate
	return false;
      case 'w':			// 'wh' can be a glide -- check for vowels
	if (isVowel(a) || isVowel(d)) {
	  return false;		// The vowel is there -- it is a glide.
	}
				// No vowel -- it is a fricative.
      }	
      return true;
    case 't':			// Do the two character cases
      if (d == 'h') {
	return true;
      }
      break;  
    }
    return false;
  }
  
  protected boolean isAffricate(int i,
				int j) {
    char b = getSanatizedChar(i,j-1);
    char c = getSanatizedChar(i,j);
    char d = getSanatizedChar(i,j+1);
    
    switch (c) {
    case 'j':			// Do the one character cases
      return true;
    case 'c':			// Do the two character cases
      if (d == 'h') {
	return true;		// 'ch' is an affricate
      }
      break;
    case 'h':
      if (b == 'c') {
	return true;
      }
      break;
    }
    return false;
  }
  

  protected boolean isNasal(int i,
			    int j) {
    char b = getSanatizedChar(i,j-1);
    char c = getSanatizedChar(i,j);
    char d = getSanatizedChar(i,j+1);
    
    switch (c) {
    case 'm':			// Do the one character cases
    case 'n':
      return true;
    case 'g':			// Do the two character cases
      if (b == 'n') {
	return true;		// 'ng' is a nasal
      }
      break;
    }
    return false;
  }
  
  protected boolean isLiquid(int i,
			     int j) {
    char c = getSanatizedChar(i,j);

    switch (c) {
    case 'l':			// Do the one character cases
    case 'r':
      return true;
    }
    return false;
  }
  
  
  protected boolean isGlide(int i,
			    int j) {

    char a = getSanatizedChar(i,j-2);
    char b = getSanatizedChar(i,j-1);
    char c = getSanatizedChar(i,j);
    char d = getSanatizedChar(i,j+1);
    char e = getSanatizedChar(i,j+2);
    
    switch (c) {
    case 'y':			// Do the one character cases
      if (isVowel(b) || isVowel(d)) {
	return true;
      }
      return false;
    case 'w':
      if (isVowel(b)) {
	return true;
      }
      if (isVowel(d) || (d =='h' && isVowel(e))) {
	return true;
      }
      return false;
    case 'h':
      if (b != 'w') {
	return false;
      }
      if (isVowel(a) || isVowel(d)) {
	return true;
      }
    }
    return false;
  }

				// Does periodic boundary for rows.

  protected int wrapRows(int r) {
    if (r < 0) {
      return r+rows;
    }
    return r%rows;
  }

				// Does periodic boundary for columns.

  protected int wrapColumns(int c) {
    if (c < 0) {
      return c+columns;
    }
    return c%columns;
  }

				// Top level CA routine.

  protected void doCA() {
    int i,j;
    int k,l;
    int c1 = 1,c2 = 2,c3=3,c4=4,c5=5,c6=6;

    newRaster = new char[rows][columns];
    
    for (i = 0; i < rows; i++) {
      for (j = 0; j < columns; j++) {
	newRaster[i][j] = (char)((int)(raster[i][j])&0x00ff);

	if (isStop(i,j)) {
	  newRaster[i][j] |= stop;
	}
	if (isFricative(i,j)) {
	  newRaster[i][j] |= fricative;
	}
	if (isAffricate(i,j)) {
	  newRaster[i][j] |= affricate;
	}
	if (isNasal(i,j)) {
	  newRaster[i][j] |= nasal;
	}
	if (isLiquid(i,j)) {
	  newRaster[i][j] |= liquid;
	}
	if (isGlide(i,j)) {
	  newRaster[i][j] |= glide;
	}
				// For now, just label.
      }
    }

    if (frequency == 0 || (frequency < maxFrequency
			   && counter%frequency == 0)) {
      int C;
      // update positions
	for (i = 0; i < rows; i++) {
	  for (j = 0; j < columns; j++) {
	    C = (int)newRaster[i][j];
	    
	    if ((C&fricative) != 0 && (C&moved) == 0) {
	      int d = (c1++)%4;
	      int ni = i,nj = j;
	      switch (d) {
	      case 0:
		ni = wrapRows(ni-1);
		break;
	      case 1:
		nj = wrapColumns(nj-1);
		break;
	      case 2:
		ni = wrapRows(ni+1);
		break;
	      default:
		nj = wrapColumns(nj+1);
		break;
	      }
	      int N = (int)newRaster[ni][nj];
	      if ((N&(stop|fricative|affricate|nasal|liquid|glide)) == 0) {
		newRaster[i][j] = (char)N;
		newRaster[ni][nj] = (char)(C|moved);
	      }
	      else {
		newRaster[i][j] = (char)(C|moved);
	      }
	    }
	    else {
	      if ((C&affricate) != 0 && (C&moved) == 0) {
		if ((c2++)%2==0) {
		  int d = (c3++)%4;
		  int ni = i,nj = j;
		  switch (d) {
		  case 0:
		    ni = wrapRows(ni-1);
		    break;
		  case 1:
		    nj = wrapColumns(nj-1);
		    break;
		  case 2:
		    ni = wrapRows(ni+1);
		    break;
		  default:
		    nj = wrapColumns(nj+1);
		    break;
		  }
		  int N = (int)newRaster[ni][nj];
		  if ((N&(stop|fricative|affricate|nasal|liquid|glide)) == 0) {
		    newRaster[i][j] = (char)N;
		    newRaster[ni][nj] = (char)(C|moved);
		  }
		  else {
		    newRaster[i][j] = (char)(C|moved);
		  }
		}
		else {
		  newRaster[i][j] = (char)(C|moved);
		}
	      }
	      else {
		if ((C&nasal) != 0 && (C&moved) == 0) {
		  if ((c4++)%4 == 0) {
		    int ni = wrapRows(i-1),nj = j;
		    int N = (int)newRaster[ni][nj];
		    if ((N&(stop|affricate)) == 0) {
		      newRaster[i][j] = (char)N;
		      newRaster[ni][nj] = (char)(C|moved);
		    }
		    else {
		      newRaster[i][j] = (char)(C|moved);
		    }
		  }
		  else {
		    newRaster[i][j] = (char)(C|moved);
		  }
		}
		else {
		  if ((C&liquid) != 0 && (C&moved) == 0) {
		    if ((c5++)%2 == 0) {
		      int ni = wrapRows(i+1),nj = j;
		      int N = (int)newRaster[ni][nj];
		      if ((N&(stop|affricate)) == 0) {
			newRaster[i][j] = (char)N;
			newRaster[ni][nj] = (char)(C|moved);
		      }
		      else {
			newRaster[i][j] = (char)(C|moved);
		      }
		    }
		    else {
		      newRaster[i][j] = (char)(C|moved);
		    }
		  }
		  else {
		    if ((C&glide) != 0 && (C&moved) == 0) {
		      if ((c6++)%3 == 0) {
			int ni = i,nj = wrapColumns(j+1);
			int N = (int)newRaster[ni][nj];
			if ((N&(stop|affricate)) == 0) {
			  newRaster[i][j] = (char)N;
			  newRaster[ni][nj] = (char)(C|moved);
			}
			else {
			  newRaster[i][j] = (char)(C|moved);
			}
		      }
		      else {
			newRaster[i][j] = (char)(C|moved);
		      }
		      
		    }
		  }
		}
	      }
	    }
	  }
	  
	}
    }
    raster=newRaster;
    update(graphics);
  }

protected void drawCharacter(char c, int col, int row, boolean clear) {
  int X = (int)(col*cWidth);
  int Y = (int)((row+1)*cHeight);
  
  int C = (int)c;
  
  c = (char)(C&0x00ff);
  
  if (drawColors) {
    if ( (C&stop) != 0) {
      graphics.setColor(stopColor);
    }
    else {
      if ((C&fricative) != 0) {
	graphics.setColor(fricativeColor);
      }
      else {
	if ((C&affricate) != 0) {
	  graphics.setColor(affricateColor);
	}
	else {
	  if ((C&nasal) != 0) {
	    graphics.setColor(nasalColor);
	  }
	  else {
	    if ((C&liquid) != 0) {
	      graphics.setColor(liquidColor);
	    }
	    else {
	      if ((C&glide) != 0) {
		graphics.setColor(glideColor);
	      }
	      else {
		if (isVowel(c)) {
		  graphics.setColor(Color.white);
		}
		else {
		  graphics.setColor(Color.gray);
		}
	      }
	    }
	  }
	}
      }
    }
  }
  else {
    if (isVowel(c)) {
      graphics.setColor(Color.white);
    }
    else {
      graphics.setColor(Color.gray);
    }
  }
  
  
  if (clear) {
    graphics.clearRect(X,Y-(int)cHeight,(int)cWidth,(int)cHeight);
  }
  font.drawCharacter(graphics,c,X,Y);
}
}




