package ch.unibe.scg.regex;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;

import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.regex.MatchResult;

import org.junit.Test;
import org.mockito.Mockito;

import ch.unibe.scg.regex.ParserProvider.Node.Simple;
import ch.unibe.scg.regex.RealMatchResult.NoMatchResult;



class NFAInterpreter {
  public static final class NFAInterpreterTest {

    @Test
    public void group2() {
      final RegexToNFA r = new RegexToNFA();
      final Simple s = (Simple) new ParserProvider().regexp().parse("(\\.)*");
      final TNFA tnfa = r.convert(s);
      final NFAInterpreter n = new NFAInterpreter(tnfa);
      final RealMatchResult match = (RealMatchResult) n.match("...");
      assertThat(match.captureGroups.toString(), is(""));
    }

    @Test
    public void testMocked() {
      final TNFA tnfa = mock(TNFA.class);
      final State a = mock(State.class);
      final State b = mock(State.class);
      final State c = mock(State.class);
      final Tag tag = mock(Tag.class);

      when(tag.getGroup()).thenReturn(0);
      when(tnfa.availableTransitionsFor(Mockito.eq(a), eq('a'))).thenReturn(
          Arrays.asList(new Pair<State, Tag>(a, tag)));
      when(tnfa.availableTransitionsFor(Mockito.eq(a), eq('b'))).thenReturn(
          Arrays.asList(new Pair<State, Tag>(b, tag)));
      when(tnfa.availableTransitionsFor(Mockito.eq(b), Mockito.any(Character.class))).thenReturn(
          (Collection) Collections.emptyList());
      when(tnfa.getInitialState()).thenReturn(a);
      when(tnfa.isAccepting(eq(a))).thenReturn(false);
      when(tnfa.isAccepting(eq(b))).thenReturn(true);

      final NFAInterpreter n = new NFAInterpreter(tnfa);
      final MatchResult match = n.match("aaab");

      assertThat(match.start(), is(0));
      assertThat(match.end(), is(3));
    }
  }

  final TNFA nfa;

  final Pair<State, Tag> SCAN;

  NFAInterpreter(final TNFA nfa) {
    this.nfa = nfa;

    SCAN = new Pair<>(new State(), null);

  }

  Character charAt(final int j, final String input) {
    if (j >= input.length()) {
      return null;
    }
    return input.charAt(j);
  }

  public MatchResult match(int j, final String input) {
    final Deque<Pair<State, Tag>> q = new ArrayDeque<>();
    q.add(SCAN);
    Pair<State, Tag> transition = new Pair<>(nfa.getInitialState(), Tag.ENTIRE_MATCH);
    final RealMatchResult r = new RealMatchResult(this);
    do {
      if (transition.equals(SCAN)) {
        j++;
        q.add(SCAN);
      } else {
        final Collection<Pair<State, Tag>> transitions =
            nfa.availableTransitionsFor(transition.getFirst(), charAt(j, input));
        assert transitions != null;
        for (final Pair<State, Tag> t : transitions) {
          q.add(t);
        }
      }
      transition = q.pop();
      if (!transition.equals(SCAN)) {
        r.takeCaptureGroup(transition.getSecond(), j - 1);
      }
    } while (j <= input.length() && !nfa.isAccepting(transition.getFirst()) && !q.isEmpty());

    if (nfa.isAccepting(transition.getFirst())) {
      r.takeCaptureGroup(transition.getSecond(), j - 1);
      r.takeCaptureGroup(Tag.ENTIRE_MATCH, j - 1);
      return r; // TODO: make immutable.
    } else {
      return NoMatchResult.SINGLETON;
    }
  }

  public MatchResult match(final String input) {
    return match(0, input);
  }

}

@@ -1376,26 +1376,32 @@
 new 
-Pair%3CState, Tag%3E(a
+TransitionTriple(a, null
 , ta
@@ -1515,26 +1515,32 @@
 new 
-Pair%3CState, Tag%3E(b
+TransitionTriple(b, null
 , ta
@@ -2075,32 +2075,32 @@
   final 
-Pair%3CState, Tag%3E
+TransitionTriple
  SCAN;%0A%0A
@@ -2174,26 +2174,35 @@
 new 
-Pair%3C%3E(new State()
+TransitionTriple(null, null
 , nu
@@ -2427,32 +2427,32 @@
 l Deque%3C
-Pair%3CState, Tag%3E
+TransitionTriple
 %3E q = ne
@@ -2494,48 +2494,66 @@
     
-Pair%3CState, Tag%3E transition = new Pair%3C%3E
+TransitionTriple transition =%0A        new TransitionTriple
 (nfa
@@ -2571,16 +2571,22 @@
 State(),
+ null,
  Tag.ENT
@@ -2647,20 +2647,16 @@
 hResult(
-this
 );%0A    d
@@ -2770,32 +2770,32 @@
 lection%3C
-Pair%3CState, Tag%3E
+TransitionTriple
 %3E transi
@@ -2856,21 +2856,21 @@
 tion.get
-First
+State
 (), char
@@ -2943,24 +2943,24 @@
 nal 
-Pair%3CState, Tag%3E
+TransitionTriple
  t :
@@ -3112,38 +3112,35 @@
 p(transition.get
-Second
+Tag
 (), j - 1);%0A    
@@ -3210,21 +3210,21 @@
 tion.get
-First
+State
 ()) && !
@@ -3280,13 +3280,13 @@
 .get
-First
+State
 ()))
@@ -3331,14 +3331,11 @@
 .get
-Second
+Tag
 (), 

Make NFAInterpreter compile.