package morfologik.fsa.builders;

import static morfologik.fsa.FSAFlags.*;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import morfologik.fsa.FSA;
import morfologik.fsa.FSA5;
import morfologik.fsa.FSAFlags;

import org.junit.Test;
import static org.junit.Assert.*;

/**
 * Additional tests for {@link FSA5}.
 */
public final class FSA5Test extends TestBase {
  public List<String> expected = Arrays.asList("a", "aba", "ac", "b", "ba", "c");

  @Test
  public void testVersion5() throws IOException {
    final FSA fsa = FSA.read(this.getClass().getResourceAsStream("abc.fsa"));
    assertFalse(fsa.getFlags().contains(FSAFlags.NUMBERS));
    verifyContent(expected, fsa);
  }

  @Test
  public void testVersion5WithNumbers() throws IOException {
    final FSA fsa = FSA.read(this.getClass().getResourceAsStream("abc-numbers.fsa"));

    verifyContent(expected, fsa);
    assertTrue(fsa.getFlags().contains(FSAFlags.NUMBERS));
  }

  @Test
  public void testArcsAndNodes() throws IOException {
    final FSA fsa1 = FSA.read(this.getClass().getResourceAsStream("abc.fsa"));
    final FSA fsa2 = FSA.read(this.getClass().getResourceAsStream("abc-numbers.fsa"));

    FSAInfo info1 = new FSAInfo(fsa1);
    FSAInfo info2 = new FSAInfo(fsa2);

    assertEquals(info1.arcsCount, info2.arcsCount);
    assertEquals(info1.nodeCount, info2.nodeCount);

    assertEquals(4, info2.nodeCount);
    assertEquals(7, info2.arcsCount);
  }

  @Test
  public void testNumbers() throws IOException {
    final FSA fsa = FSA.read(this.getClass().getResourceAsStream("abc-numbers.fsa"));

    assertTrue(fsa.getFlags().contains(NEXTBIT));

    // Get all numbers for nodes.
    byte[] buffer = new byte[128];
    final ArrayList<String> result = new ArrayList<String>();
    walkNode(buffer, 0, fsa, fsa.getRootNode(), 0, result);

    Collections.sort(result);
    assertEquals(Arrays.asList("0 c", "1 b", "2 ba", "3 a", "4 ac", "5 aba"), result);
  }

  public static void walkNode(byte[] buffer, int depth, FSA fsa, int node, int cnt, List<String> result)
      throws IOException {
    for (int arc = fsa.getFirstArc(node); arc != 0; arc = fsa.getNextArc(arc)) {
      buffer[depth] = fsa.getArcLabel(arc);

      if (fsa.isArcFinal(arc) || fsa.isArcTerminal(arc)) {
        result.add(cnt + " " + new String(buffer, 0, depth + 1, "UTF-8"));
      }

      if (fsa.isArcFinal(arc)) {
        cnt++;
      }

      if (!fsa.isArcTerminal(arc)) {
        walkNode(buffer, depth + 1, fsa, fsa.getEndNode(arc), cnt, result);
        cnt += fsa.getRightLanguageCount(fsa.getEndNode(arc));
      }
    }
  }

  private static void verifyContent(List<String> expected, FSA fsa) throws IOException {
    final ArrayList<String> actual = new ArrayList<String>();

    int count = 0;
    for (ByteBuffer bb : fsa.getSequences()) {
      assertEquals(0, bb.arrayOffset());
      assertEquals(0, bb.position());
      actual.add(new String(bb.array(), 0, bb.remaining(), "UTF-8"));
      count++;
    }
    assertEquals(expected.size(), count);
    Collections.sort(actual);
    assertEquals(expected, actual);
  }
}
