package cc.mallet.types;

import java.io.*;
import java.util.regex.*;
import java.util.HashMap;
import gnu.trove.TObjectIntHashMap;
import java.util.Set;
import java.util.Iterator;

// xxx A not very space-efficient version.  I'll compress it later.

public class StringEditFeatureVectorSequence extends FeatureVectorSequence implements Serializable
{
  private int string1Length, string2Length;
  private String string1, string2;
  private String[] string1Blocks, string2Blocks;
  private TObjectIntHashMap string1Present, string2Present;
  private TObjectIntHashMap lexicon;
  private int[] block1Indices, block2Indices;
  private char delim = ':';
  private static final char defaultDelimiter = ':';

  public StringEditFeatureVectorSequence (FeatureVector[] featureVectors, String s1, String s2)
  {
    this (featureVectors, s1, s2, defaultDelimiter);
  }

  public StringEditFeatureVectorSequence(FeatureVector[] featureVectors, String s1, String s2, char delimiter)
  {
    this (featureVectors, s1, s2, delimiter, null);
  }

  public StringEditFeatureVectorSequence(FeatureVector[] featureVectors, String s1, String s2, HashMap lexic)
  {
    this (featureVectors, s1, s2, defaultDelimiter, lexic);
  }

  public StringEditFeatureVectorSequence(FeatureVector[] featureVectors, String s1, String s2, char delimiter, HashMap lexic)
  {
    super (featureVectors);
    this.delim = delimiter;
    
    this.lexicon = new TObjectIntHashMap();
    if (lexic != null) {
      Set keys = lexic.keySet();
      java.util.Iterator iter = keys.iterator();
      while (iter.hasNext())
        this.lexicon.put((String) iter.next(), 1);
    }

    this.string1 = s1;
    this.string2 = s2;
    this.string1Length = s1.length() + 2;
    this.string2Length = s2.length() + 2;
    string1Blocks = string1.split("" + delim);
    string2Blocks = string2.split("" + delim);
    string1Present = new TObjectIntHashMap();
    string2Present = new TObjectIntHashMap();
    block1Indices = new int[string1Length];
    if (string1Blocks.length > 0) {
      int whichBlock = 0;
      block1Indices[0] = whichBlock++;
      for (int i = 0; i < string1Blocks.length; i++)
        string1Present.put(string1Blocks[i], 1);
      for (int i = 1; i < string1Length-1; i++)
        block1Indices[i] = ((string1.charAt(i-1) == delim) ? whichBlock++ : -1);
      block1Indices[string1Length-1] = -1;
    }
    block2Indices = new int[string2Length];
    if (string2Blocks.length > 0) {
      int whichBlock = 0;
      block2Indices[0] = whichBlock++;
      for (int i = 0; i < string2Blocks.length; i++)
        string2Present.put(string2Blocks[i], 1);
      for (int i = 1; i < string2Length - 1; i++)
        block2Indices[i] = ((string2.charAt(i-1) == delim) ? whichBlock++ : -1);
      block2Indices[string2Length-1] = -1;
    }
  }
 
  public String getString1() {
    return string1;
  }

  public String getString2() {
    return string2;
  }

  public int getString1Length () {
    return string1Length;
  }

  public int getString2Length () {
    return string2Length;
  }

  // End of Block
  public int getString1EOBIndex(String delimiter) {
    return getString1EOBIndex(delimiter, 0);
  }

  public int getString1EOBIndex(String delimiter, int start) {
    return getString1IndexOf(delimiter, start);
  }

  public String getString1BlockAtIndex(int idx) {
    if (idx < 0 || idx >= block1Indices.length || block1Indices[idx] < 0 || block1Indices[idx] >= string1Blocks.length) return null;
    else return string1Blocks[block1Indices[idx]];
  }

  public int getString1IndexOf(String str, int start) {
    int toret = string1.indexOf(str, start);
  
    if (toret == -1)
      toret = string1.length() - 1 - start;
    else
      toret = toret - start;

    if (toret < 1)
      return -1;

    return toret;
  }

  public boolean isPresent1(String patternStr) {
    Pattern p = Pattern.compile(patternStr);
    Matcher m = p.matcher(string1);
    boolean b = m.matches();

    return b;
  }

  public boolean isPresentInString1(String str) {
    return string1Present.containsKey(str);
  }

  public char getString1Char(int index) {
    index = index - 1;
    if (index < 0 || index >= string1.length()) return (char) 0;
    else return string1.charAt(index);
  }

  public int getString2EOBIndex(String delimiter) {
    return getString2EOBIndex(delimiter, 0);
  }

  public int getString2EOBIndex(String delimiter, int start) {
    return getString2IndexOf(delimiter, start);
  }

  public String getString2BlockAtIndex(int idx) {
    if (idx < 0 || idx >= block2Indices.length || block2Indices[idx] < 0 || block2Indices[idx] >= string2Blocks.length) return null;
    else return string2Blocks[block2Indices[idx]];
  }

  public boolean isPresentInString2(String str) {
    return string2Present.containsKey(str);
  }

  public int getString2IndexOf(String str, int start) {
    int toret = string2.indexOf(str, start);
  
    if (toret == -1)
      toret = string2.length() - 1 - start;
    else
      toret = toret - start;

    if (toret < 1)
      return -1;

    return toret;
  }

  public boolean isPresent2(String patternStr) {
    Pattern p = Pattern.compile(patternStr);
    Matcher m = p.matcher(string2);
    boolean b = m.matches();

    return b;
  }

  public char getString2Char(int index) {
    index = index - 1;
    if (index < 0 || index >= string2.length()) return (char) 0;
    else return string2.charAt(index);
  }

  public boolean isInLexicon(String str) {
    if (lexicon == null || str == null) return false;

    return lexicon.containsKey(str);
  }

  public String toString ()
  {
    StringBuffer sb = new StringBuffer ();
    sb.append (super.toString());
    sb.append ('\n');
    sb.append ("String 1: " + string1Length + " String 2: " + string2Length);

    return sb.toString();
  }

  // Serialization of Instance

  private static final long serialVersionUID = 1;
  private static final int CURRENT_SERIAL_VERSION = 0;
  private static final int NULL_INTEGER = -1;

  private void writeObject (ObjectOutputStream out) throws IOException {
    out.writeInt (CURRENT_SERIAL_VERSION);
    out.writeInt (string1Length);
    out.writeInt (string2Length);
    out.writeObject (string1);
    out.writeObject (string2);

    if (string1Blocks == null) {
      out.writeInt(NULL_INTEGER);
    }
    else {
      int size = string1Blocks.length;
      out.writeInt(size);
      for(int i=0; i<size; i++) {
        out.writeObject(string1Blocks[i]);
      }
    }

    if (string2Blocks == null) {
      out.writeInt(NULL_INTEGER);
    }
    else {
      int size = string2Blocks.length;
      out.writeInt(size);
      for(int i=0; i<size; i++) {
        out.writeObject(string2Blocks[i]);
      }
    }

    out.writeObject(string1Present); 
    out.writeObject(string2Present); 
    out.writeObject(lexicon); 

    if (block1Indices == null) {
      out.writeInt(NULL_INTEGER);
    }
    else {
      int size = block1Indices.length;
      out.writeInt(size);
      for (int i=0; i<size; i++) {
        out.writeInt(block1Indices[i]);
      }
    }

    if (block2Indices == null) {
      out.writeInt(NULL_INTEGER);
    }
    else {
      int size = block2Indices.length;
      out.writeInt(size);
      for (int i=0; i<size; i++) {
        out.writeInt(block2Indices[i]);
      }
    }

    out.writeChar(delim);
  }

  private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException {
    int version = in.readInt ();
    int string1Length = in.readInt();
    int string2Length = in.readInt();
    String string1 = (String) in.readObject();
    String string2 = (String) in.readObject();
    int size = in.readInt();
    if (size == NULL_INTEGER) {
      string1Blocks = null;
    }
    else {
      string1Blocks = new String[size];
      for (int i = 0; i<size; i++) {
        string1Blocks[i] = (String) in.readObject();
      }
    }

    size = in.readInt();
    if (size == NULL_INTEGER) {
      string2Blocks = null;
    }
    else {
      string2Blocks = new String[size];
      for (int i = 0; i<size; i++) {
        string2Blocks[i] = (String) in.readObject();
      }
    }

    TObjectIntHashMap string1Present = (TObjectIntHashMap) in.readObject();
    TObjectIntHashMap string2Present = (TObjectIntHashMap) in.readObject();
    TObjectIntHashMap lexicon = (TObjectIntHashMap) in.readObject();

    size = in.readInt();
    if (size == NULL_INTEGER) {
      block1Indices = null;
    }
    else {
      block1Indices = new int[size];
      for (int i = 0; i<size; i++) {
        block1Indices[i] = in.readInt();
      }
    }

    size = in.readInt();
    if (size == NULL_INTEGER) {
      block2Indices = null;
    }
    else {
      block2Indices = new int[size];
      for (int i = 0; i<size; i++) {
        block2Indices[i] = in.readInt();
      }
    }

    delim = in.readChar();
  }
}
