// NNfile class: parses neural network input files
// storing network parameters and optional training
// data in memory.

// Modified 9/11/97: removed packing all data in a 
// single array (now use multiple arrays so the code
// is simpler).

package mwa.ai.neural;

import java.io.*;
import java.net.URL;
import java.util.*;
//import java.lang.*;

class FileFormatException extends Exception {
  public FileFormatException(String str) {
    super(str);
  }
}

public class NNfile {
  public int NumLayers;
  public int NumInput, NumHidden, NumOutput, NumTraining;
  public int WeightFlag;
  public int SpecialFlag;
  private float W1[];
  private float W2[];
  private float Special[];
//  private float Training[];  private Vector TrainingIn = new Vector();
  private Vector TrainingOut = new Vector();


  public NNfile() {
    NumLayers=NumInput=NumHidden=NumOutput=0;
  }

  public NNfile(String input_file) {
    FileInputStream is = null;
    try {
      is = new FileInputStream(input_file);
    } catch (Exception E) {
        System.out.println("can not open file " + input_file);
    }
    try {
      if (is != null)  ReadFile(is);  is.close();
    } catch (Exception E) {
        System.out.println("can not process file");
    }
    OutputStream f = null;
  }

  // To get weights:
  public float GetW1(int input, int hidden) {
     return W1[input * NumHidden + hidden];
  }
  public float GetW2(int hidden, int output) {
     return W2[hidden * NumOutput + output];
  }

  // To set weights:
  public void SetW1(int input, int hidden, float x) {
     WeightFlag=1; // set this so save() will save weights
     W1[input * NumHidden + hidden] = x;
  }
  public void SetW2(int hidden, int output, float x) {
     WeightFlag=1; // set this so save() will save weights
     W2[hidden * NumOutput + output] = x;
  }

  // To get any application specific data:
  public float GetSpecial(int i) {
     if (SpecialFlag==0) return 0.0f;
     return Special[i];
  }

  // To add application specific data:
  public void AddSpecial(float x) {
	  Special[SpecialFlag++] = x;
  }

  // To get training cases:
  public float GetInput(int training_case, int neuron_index) {     return ((float [])TrainingIn.elementAt(training_case))[neuron_index];
  }


  public float GetOutput(int training_case, int neuron_index) {     return ((float [])TrainingOut.elementAt(training_case))[neuron_index];
  }


  public void RemoveTraining(int num) {
     if (num < 0 || num >= NumTraining) {
        System.out.println("Error in RemoveTraining(" + num + ")");
        return;
     }
     TrainingIn.removeElementAt(num);     TrainingOut.removeElementAt(num);
     NumTraining--;

  }
  public void AddTraining(float inputs[], float outputs[]) {     TrainingIn.addElement(inputs);
     TrainingOut.addElement(outputs);
     NumTraining++;

  }


  void ReadFile(InputStream inp)
          throws IOException, FileFormatException {
    System.out.println("Entered ReadFile");
	float data[] = new float[32000]; // temporary storage
    StreamTokenizer st = new StreamTokenizer(inp);
    st.commentChar('#');
    st.eolIsSignificant(false);
    st.parseNumbers();
    System.out.println("Before while" );
	int count = 0;
    process:
    while (true) {
      switch(st.nextToken()) {
        case StreamTokenizer.TT_EOL:
          System.out.println("EOF found");
        break process;
        case StreamTokenizer.TT_NUMBER:            float x = (float)st.nval;
            data[count++] = x;
        break;
        case StreamTokenizer.TT_WORD:          String s = st.sval.substring(0, 2).toLowerCase();          if (s.equals("e-")) {
            data[count-1] = 0.0f;
          }
          else {
             System.out.println("Token (default):" + st.sval);
             break process;
          }
          break;        default:           System.out.println("Token (default):" + st.sval);
        break process;
      }

    }
    inp.close();

    int k = 0;
    NumLayers = (int)data[k++];
    NumInput    = (int)data[k++];
    NumHidden   = (int)data[k++];
    NumOutput   = (int)data[k++];
    WeightFlag  = (int)data[k++];
    SpecialFlag = (int)data[k++];
    int TempNumTraining = (int)data[k++];
    // allocate storage:
    W1 = new float[NumInput * NumHidden];
    W2 = new float[NumHidden * NumOutput];
    Special = new float[SpecialFlag + 10]; // room to grow..
//    Training = new float[NumTraining * (NumInput + NumOutput)];

    if (WeightFlag!=0) {
      for (int i=0; i<NumInput*NumHidden; i++)
	W1[i] = data[k++];
      for (int i=0; i<NumHidden*NumOutput; i++)
	W2[i] = data[k++];
    } else {
      for (int i=0; i<NumInput*NumHidden; i++)
        W1[i] = (float)java.lang.Math.random()*0.2f - 0.1f;
      for (int i=0; i<NumHidden*NumOutput; i++)
        W2[i] = (float)java.lang.Math.random()*0.2f - 0.1f;
    }

    for (int i=0; i<SpecialFlag; i++) Special[i] = data[k++];
    float TempIn[];    float TempOut[];    for (int i=0; i<TempNumTraining; i++) {
      TempIn = new float[NumInput];
      TempOut = new float[NumOutput];
      for (int j=0; j<NumInput; j++) {
         TempIn[j] = data[k++];
      }
      for (int j=0; j<NumOutput; j++) {
         TempOut[j] = data[k++];
      }
      AddTraining(TempIn, TempOut);
    }


    if (st.ttype!=StreamTokenizer.TT_EOF)
      throw new FileFormatException(st.toString());
  }

  public void Save(String save_file_name) {
    try {
      FileOutputStream f = new FileOutputStream(save_file_name);
      PrintStream ps = new PrintStream(f);
      ps.println("#  Neural network data written by NNfile\n");
      ps.println(NumLayers + "  # number of neuron layers");

	  ps.println(NumInput + "  # neurons in layer 0");
	  ps.println(NumHidden + "  # neurons in layer 1");
	  ps.println(NumOutput + "  # neurons in layer 2");

      ps.println("1  # weight flag"); // always write out weights
      ps.println(SpecialFlag + "  # special data flag");
      ps.println(NumTraining + "  # number of training cases in file");

      ps.println("\n# Input layer to hidden layer weights:\n");
      for (int i=0; i<NumInput; i++) {
        for (int h=0; h<NumHidden; h++) {
          ps.print(GetW1(i, h) + " ");
        }
        ps.print("\n");
      }
      ps.println("\n# Hidden layer to output layer weights:\n");
      for (int h=0; h<NumHidden; h++) {
        for (int o=0; o<NumOutput; o++) {
          ps.print(GetW2(h, o) + " ");
        }
        ps.print("\n");
      }
      if (SpecialFlag > 0) {
        ps.println("\n# Special network data:\n");
        for (int i=0; i<SpecialFlag; i++) {
          ps.println(GetSpecial(i) + " ");
        }
        ps.println("\n");
      }
      ps.println("\n# Training data:\n");
      for (int i=0; i<NumTraining; i++) {
         for (int j=0; j<NumInput; j++) {
            ps.print(GetInput(i,j) + " ");
         }
         ps.print("   ");
         for (int j=0; j<NumOutput; j++) {
            ps.print(GetOutput(i,j) + " ");
         }
         ps.println("");
      }
      System.out.println("Done writing to output file.");
      ps.close();
      f.close();
    } catch (Exception E) {
        System.out.println("can not process the file " + save_file_name);
    }
  }
  public static void main(String argv[]) {
    NNfile test = new NNfile("test.dat");
  }
}

