import java.applet.Applet;
import java.awt.image.*;
import java.awt.*;
import java.net.*;
import java.io.*;

class NN 
{
  int numUnits, numInputs, numHiddens, numOutputs, numConnections;
  float activation[];
  int numInputsToUnit[];
  float connectionWeight[][];
  int connectionUnit[][];
  int from[], to[];
  float weights[];
  int outputWidth;
  int outputHeight;
  int firstOutput;
  int firstHidden;

public NN() 
  {
  }

public void readNetwork(InputStream in)
  {
    StreamTokenizer tokens=new StreamTokenizer(in);
    tokens.eolIsSignificant(false);
    tokens.parseNumbers();
    tokens.wordChars('a','z');
    try {
      tokens.nextToken();
      numUnits=(int)tokens.nval;
      tokens.nextToken();
      tokens.nextToken();
      numInputs=(int)tokens.nval;
      tokens.nextToken();
      for (int i=0; i<numInputs*4; i++) 
	tokens.nextToken();
      tokens.nextToken();
      numHiddens=(int)tokens.nval;
      tokens.nextToken();
      for (int i=0; i<numHiddens*4; i++) 
	tokens.nextToken();
      tokens.nextToken();
      numOutputs=(int)tokens.nval;
      tokens.nextToken();
      outputWidth=0;
      outputHeight=0;
      for (int i=0; i<numOutputs; i++) {
	tokens.nextToken();
      	tokens.nextToken();
      	tokens.nextToken();
	if (tokens.nval+1>outputHeight) outputHeight=(int)tokens.nval+1;
      	tokens.nextToken();
	if (tokens.nval+1>outputWidth) outputWidth=(int)tokens.nval+1;
      }
      firstOutput=numInputs+numHiddens;
      firstHidden=numInputs;
//      System.err.println(numInputs + " inputs");
//      System.err.println(numHiddens + " hiddens");
//      System.err.println(numOutputs + " outputs");
//      System.err.println(outputWidth + " width");
//      System.err.println(outputHeight + " height");

      activation=new float[numUnits];
      numInputsToUnit=new int[numUnits];
      for (int i=0; i<numUnits; i++) {
	activation[i]=(float)0.0;
	numInputsToUnit[i]=0;
      }
      activation[0]=(float)1.0;
      tokens.nextToken();
      numConnections=(int)tokens.nval;
      from=new int[numConnections];
      to=new int[numConnections];
      tokens.nextToken();
      for (int i=0; i<numConnections; i++) {
	tokens.nextToken();
	from[i]=(int)tokens.nval;
	tokens.nextToken();
	to[i]=(int)tokens.nval;
	numInputsToUnit[to[i]]++;
	tokens.nextToken();
      }

      weights=new float[numConnections];

    } catch (Exception e) { System.err.println("network: " + e); }
  }

public void readWeights(InputStream in)
  {
    StreamTokenizer tokens=new StreamTokenizer(in);
    tokens.eolIsSignificant(false);
    tokens.parseNumbers();
    tokens.wordChars('a','z');
    try {
      tokens.nextToken();
      tokens.nextToken();
      tokens.nextToken();
      tokens.nextToken();
      for (int i=0; i<numConnections; i++) {
	tokens.nextToken();
	weights[i]=(float)tokens.nval;
      }
      connectionUnit=new int[numUnits][];
      connectionWeight=new float[numUnits][];
      for (int i=0; i<numUnits; i++) {
        if (numInputsToUnit[i]>0) {
          connectionUnit[i]=new int[numInputsToUnit[i]];
          connectionWeight[i]=new float[numInputsToUnit[i]];
        }
	numInputsToUnit[i]=0;
      }
      for (int i=0; i<numConnections; i++) {
	int unit=to[i];
	int conn=numInputsToUnit[unit];
	connectionWeight[unit][conn]=weights[i];
	connectionUnit[unit][conn]=from[i];
	numInputsToUnit[unit]++;
      }
    } catch (Exception e) { System.err.println("weights: " + e); }
  }

public MemoryImageSource getImageSource(IndexColorModel cm, double x, double y)
{
  for (int i=0; i<50; i++)
    activation[i+1]=(float)(-1.0+2.0*Math.exp(-(i-x)*(i-x)/2.0/10.0));
  for (int i=0; i<50; i++)
    activation[i+51]=(float)(-1.0+2.0*Math.exp(-(i-y)*(i-y)/2.0/10.0));
  for (int i=firstHidden; i<numUnits; i++) {
    float tot=(float)0.0;
    for (int j=0; j<numInputsToUnit[i]; j++) 
      tot+=connectionWeight[i][j]*activation[connectionUnit[i][j]];
    activation[i]=(float)((Math.exp(tot)-Math.exp(-tot))/
                          (Math.exp(tot)+Math.exp(-tot)));
  }
  byte buf[]=new byte[outputWidth*outputHeight];
  int ptr=0;
  for (int i=firstOutput; i<numUnits; i++)
    buf[ptr++]=(byte)((activation[i]+1)*127.5);
  return new MemoryImageSource(outputWidth,outputHeight,cm,buf,0,
                               outputWidth);
}
}

public class Eye extends Applet
{
  NN nn;
  Image img;
  static IndexColorModel cm;

  static {
    byte red[]=new byte[256];
    byte green[]=new byte[256];
    byte blue[]=new byte[256];
    byte alpha[]=new byte[256];
    for (int i=0; i<256; i++) {
      red[i]=(byte)i;
      green[i]=(byte)i;
      blue[i]=(byte)i;
      alpha[i]=(byte)255;
    }
    cm=new IndexColorModel(8,256,red,green,blue,alpha);
  }

public void init() 
  {
    try {
      nn=new NN();
      showStatus("Reading network...");
      URL url=new URL(getCodeBase(),getParameter("network"));
      nn.readNetwork(url.openStream());
      showStatus("Reading weights...");
      url=new URL(getCodeBase(),getParameter("weights"));
      nn.readWeights(url.openStream());
      showStatus("Ready.");
    } catch (Exception e) { System.err.println(e); }
  }

public void update(Graphics g)
{
  paint(g);
}

public void paint(Graphics g)
{
  if (img==null) img=createImage(nn.getImageSource(cm,25,25));
  Rectangle rect=bounds();
  g.drawImage(img,rect.x,rect.y,rect.width,rect.height,0,0,width,height,null);
//  g.drawImage(img,rect.x,rect.y,rect.width,rect.height,null);
}

public void pointEye(int x, int y)
{
  Rectangle rect=bounds();
  double xx=45.0-(x-rect.x-rect.width*0.5)*40.0/(rect.width*0.5);
  double yy=(y-rect.y-rect.height*0.5)*40.0/(rect.height*0.5)+5.0;
  
  if (xx<5) xx=5;
  if (yy<5) yy=5;
  if (xx>45) xx=45;
  if (yy>45) yy=45;
  img=createImage(nn.getImageSource(cm,xx,yy));
  repaint();
}

public boolean mouseDown(Event evt, int x, int y) 
{
  pointEye(x,y);
  return true;
}

public boolean mouseMove(Event evt, int x, int y) 
{
  pointEye(x,y);
  return true;
}

public boolean mouseDrag(Event evt, int x, int y) 
{
  pointEye(x,y);
  return true;
}

public boolean mouseUp(Event evt, int x, int y) 
{
  pointEye(x,y);
  return true;
}

public void start()
  {
  }
  
public void stop()
  {
  }
}


