package bluej.stride.framedjava.slots;

import java.util.ArrayList;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;

import bluej.stride.framedjava.slots.Operator.Precedence;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import static bluej.stride.framedjava.slots.Operator.Precedence.DOT;
import static bluej.stride.framedjava.slots.Operator.Precedence.HIGH;
import static bluej.stride.framedjava.slots.Operator.Precedence.LOW;
import static bluej.stride.framedjava.slots.Operator.Precedence.MEDIUM;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

public class TestTypeSlot
{       

   @Rule
   
   public TestRule runOnFXThreadRule = new TestRule() {        
      boolean initialised = false;
       
      @Override public Statement apply(Statement base, Description d)
{         
if (!initialised)
           {                   
            
            new JFXPanel();
               
            initialised = true;             
            }
           
         return new Statement() {                
            @Override public void evaluate() throws Throwable
{                                

            CompletableFuture<Throwable> thrown = new CompletableFuture<>();
            Platform.runLater(() -> {
                     
            try {
            base.evaluate();
            thrown.complete(null);                       
            } catch( Throwable throwable ) {
            thrown.complete(throwable);                       
            }                     
         });
      Throwable t = thrown.get();
      if (t != null)
                       
      throw t;                 
      }             
   };
}              
};


private static class CompareWrapper
{           
private String s;
       
   public CompareWrapper(String s)
{
this.s = s; 
      }
       
   @Override public boolean equals(Object o)
{
if (o instanceof CompareWrapper) return s.equals(((CompareWrapper)o).s); else{ return false;
         } 
      }
       
   @Override public String toString()
{
return s; 
      }     
   }

   
private void testInsert(String insertion, String result)
   {        
   InfixType e = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
   CaretPos p = e.testingInsert(insertion, '\0');
       
   assertEquals(insertion + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));
        
       
   
   for (int i = 0; i <= insertion.length(); i++)
       {
      CaretPos caretPos = e.stringPosToCaretPos(i, false);
      if (caretPos != null)
      assertEquals("String pos -> caret pos -> string pos", i, e.caretPosToStringPos(caretPos, false));         
      }
        
       
   
   String noPos = result.replace("$", "");
       
   for (int split = 1; split < insertion.length(); split++)
       {            
      e = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
      e.testingInsert(e.testingInsert(insertion.substring(0, split), '\0'),  insertion.substring(split));
           
      assertEquals(insertion + " -> " + noPos, new CompareWrapper(noPos), new CompareWrapper(e.testingGetState(null)));         
      }                       
   }
    
   
private void testMultiInsert(String multiInsertion, String firstResult, String secondResult)
   {        
   InfixType e = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
   int startNest = multiInsertion.indexOf('{');
       
   int endNest = multiInsertion.indexOf('}', startNest);
   if (startNest == -1 || endNest == -1)
           
   throw new IllegalStateException();
   String before = multiInsertion.substring(0, startNest);
   String nest = multiInsertion.substring(startNest + 1, endNest);
   String after = multiInsertion.substring(endNest + 1);
        
   CaretPos p = e.testingInsert(before + "$" + after, '$');
       
   assertEquals(multiInsertion + " -> " + firstResult, new CompareWrapper(firstResult), new CompareWrapper(e.testingGetState(p)));
   p = e.testingInsert(p,  nest);
       
   assertEquals(multiInsertion + " -> " + secondResult, new CompareWrapper(secondResult), new CompareWrapper(e.testingGetState(p)));     
   }
    
   
private void testInsertExisting(String start, String insertion, String result)
   {        
   InfixType e = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
   CaretPos p = e.testingInsert(start, '$');
   p = e.testingInsert(p, insertion);
        
       
   assertEquals(start + " then \"" + insertion + "\" -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));     
   }
    
    
   
private void testBackspace(String insertionInclBackspace, String result)
   {
   testBackspace(insertionInclBackspace, result, true, true);     
   }
    
   
private void testBackspace(String insertionInclBackspace, String result, boolean testBackspace, boolean testDelete)
   {
   if (testBackspace)
       {            
      InfixType e = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
      CaretPos p = e.testingInsert(insertionInclBackspace, '\b');
      e.positionCaret(p);
            
      p = e.testingBackspace(p);
           
      assertEquals(insertionInclBackspace.replace("\b", "\\b") + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));         
      }
        
       
   
   int index = insertionInclBackspace.indexOf('\b');
   if (index > 0 && testDelete)
       {
      String before = insertionInclBackspace.substring(0, index);
      String after = insertionInclBackspace.substring(index + 1);
            
           
      
      after = before.substring(before.length() - 1) + after;
      before = before.substring(0, before.length() - 1);
            
           
      
      String joined = before + "\b" + after;
           
      InfixType e = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
      CaretPos p = e.testingInsert(joined, '\b');
      e.positionCaret(p);
      p = e.testingDelete(p);
           
      assertEquals(joined.replace("\b", "\\DEL") + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));         
      }     
   }
    
   
private void testDeleteSelection(String src, String result)
   {        
   InfixType e = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
   int startNest = src.indexOf('{');
       
   int endNest = src.indexOf('}', startNest);
   if (startNest == -1 || endNest == -1)
           
   throw new IllegalStateException();
   String before = src.substring(0, startNest);
   String nest = src.substring(startNest + 1, endNest);
   String after = src.substring(endNest + 1);
        
   CaretPos start = e.testingInsert(before, '\0');
   CaretPos end = e.testingInsert(start, nest);
   e.testingInsert(end, after);
   CaretPos p = e.testingDeleteSelection(start, end);
        
       
   assertEquals(src + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));     
   }
    
   
private void testSelectionInsert(char c, String src, String result)
   {        
   InfixType e = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
   int startNest = src.indexOf('{');
       
   int endNest = src.indexOf('}', startNest);
   if (startNest == -1 || endNest == -1)
           
   throw new IllegalStateException();
   String before = src.substring(0, startNest);
   String nest = src.substring(startNest + 1, endNest);
   String after = src.substring(endNest + 1);
        
   CaretPos start = e.testingInsert(before, '\0');
   CaretPos end = e.testingInsert(start, nest);
   e.testingInsert(end, after);
   CaretPos p = e.testingInsertWithSelection(start, end, c);
        
       
   assertEquals(src + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p)));     
   }
    
   
private static class CPM
{           
private int stringPos;
       
   private CaretPos caretPos;
       
   public CPM(int stringPos, CaretPos caretPos)
       {            
      this.stringPos = stringPos;
           
      this.caretPos = caretPos;         
      }
       
   @Override
       
   public String toString()
       {            
      return "CPM [stringPos=" + stringPos + ", caretPos=" + caretPos                    
      + "]";         
      }     
   }
    
   
private void testCaretPosMap(String src, String result, CPM... cpms)
   {
   testCaretPosMap(src, result, null, cpms);     
   }
    
   
private void testCaretPosMap(String src, String result, String java, CPM... cpms)
   {        
   InfixType e = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
   CaretPos p = e.testingInsert(src, '\0');
       
   assertEquals(src + " -> " + result, new CompareWrapper(result), new CompareWrapper(e.testingGetState(p).replace("$", "")));
       
   for (CPM cpm : cpms)
       {            
      assertEquals("Caret to String " + cpm.toString() + " for " + src, cpm.stringPos, e.caretPosToStringPos(cpm.caretPos, java != null));
           
      assertEquals("String to Caret " + cpm.toString() + " for " + src, cpm.caretPos, e.stringPosToCaretPos(cpm.stringPos, java != null));         
      }
        
   if (java != null)
       {
      assertEquals("To Java, from: " + src, java, e.getJavaCode());         
      }     
   }
    
   





    
   
@Test
   
public void testOperators()
   {        
   testInsert("aa", "{aa$}");
       
   testInsert("a+b", "{ab$}");
       
   testInsert("a+b-c", "{abc$}");
       
   testInsert("1++1", "{11$}");
       
   testInsert("5==-6", "{56$}");
       
   testInsert("a.b", "{a}.{b$}");
       
   testInsert("a..b", "{a}.{}.{b$}");
       
   testInsert("getY()*1", "{getY1$}");
       
   testInsert("getY[]-1", "{getY}_[{}]_{1$}");     
   }
    
   
@Test
   
public void testNew()
   {        
   testInsert("newton", "{newton$}");
       
   testInsert("new ton", "{newton$}");     
   }
    
   
@Test
   
public void testStrings()
   {        
   
   testInsert("\"hello\"", "{hello$}");
   
   testInsert("\"hello", "{hello$}");
       
   
   testInsert("\"\\\"\"", "{$}");     
   }
    
   
@Test
   
public void testBrackets()
   {        
   testInsert("a+(b-c)", "{abc$}");
       
   testInsert("a+(b-(c*d))", "{abcd$}");

       
   testInsert("a<b>", "{a}_<{b}>_{$}");
       
   testInsert("a<b<c>>", "{a}_<{b}_<{c}>_{}>_{$}");
        
       
   
   testInsert("<a.b", "{}_<{a}.{b$}>_{}");
        
       
   testInsert("<<<", "{}_<{}_<{}_<{$}>_{}>_{}>_{}");
       
   testInsert("[[[", "{}_[{}_[{}_[{$}]_{}]_{}]_{}");
       
   testInsert("(((", "{$}");
       
   testInsert("[[[]", "{}_[{}_[{}_[{}]_{$}]_{}]_{}");
       
   testInsert("[[[]]", "{}_[{}_[{}_[{}]_{}]_{$}]_{}");
       
   testInsert("[[[]]]", "{}_[{}_[{}_[{}]_{}]_{}]_{$}");     
   }
    
   
@Test
   
public void testBackspace()
   {        
   testBackspace("\bxyz", "{$xyz}");
       
   testBackspace("x\byz", "{$yz}");
       
   testBackspace("xy\bz", "{x$z}");
       
   testBackspace("xyz\b", "{xy$}");
        
       
   testBackspace("xy\b.ab", "{x$}.{ab}");
       
   testBackspace("xy.\bab", "{xy$ab}");
       
   testBackspace("xy.a\bb", "{xy}.{$b}");
        
       
   testBackspace("move[\b]", "{move$}");
       
   testBackspace("[\b]", "{$}");
       
   testBackspace("<\b>", "{$}");     
   }
    
   
@Test
   
public void testFloating()
   {        
   testInsert("1.0", "{1}.{0$}");
       
   testInsert("10.20", "{10}.{20$}");
       
   testInsert("a.0", "{a}.{0$}");
       
   testInsert("1.a", "{1}.{a$}");
       
   testInsert("x1.a", "{x1}.{a$}");
       
   testInsert("+1", "{1$}");
       
   testInsert("+1.0", "{1}.{0$}");
       
   testInsert("+1.0e5", "{1}.{0e5$}");
       
   testInsert("+1.0e", "{1}.{0e$}");
       
   testInsert("+1.0e+5", "{1}.{0e5$}");
       
   testInsert("+1.0p+5", "{1}.{0p5$}");
       
   testInsert("3+1", "{31$}");
       
   testInsert("3+1.0", "{31}.{0$}");
       
   testInsert("3+1.0e5", "{31}.{0e5$}");
       
   testInsert("3+1.0e+5", "{31}.{0e5$}");
       
   testInsert("3+1.0p+5", "{31}.{0p5$}");
        
       
   testInsert("1e6", "{1e6$}");
       
   testInsert("1e-6", "{1e6$}");
       
   testInsert("10e20", "{10e20$}");
       
   testInsert("10e+20", "{10e20$}");
       
   testInsert("10e-20", "{10e20$}");
        
       
   testInsert("1.0.3", "{1}.{0}.{3$}");
       
   testInsert("1.0.3.4", "{1}.{0}.{3}.{4$}");
       
   testInsert("1.0.x3.4", "{1}.{0}.{x3}.{4$}");
        
        
       
   testInsert("1.0", "{1}.{0$}");
       
   testInsert("1..0", "{1}.{}.{0$}");
       
   testBackspace("1..\b0", "{1}.{$0}", true, false); 
   testBackspace("1.\b.0", "{1$}.{0}", false, true); 
   testBackspace("a..\bc", "{a}.{$c}", true, false); 
   testBackspace("a.\b.c", "{a$}.{c}", false, true);      
   }
    
   
@Test
   
public void testDeleteBracket()
   {        
   testInsert("a<b.c>", "{a}_<{b}.{c}>_{$}");
       
   testBackspace("a<b.c>\b", "{ab}.{c$}");
       
   testBackspace("a<\bb.c>", "{a$b}.{c}");     
   }
    
   
@Test
   
public void testDeleteSelection()
   {        
   testDeleteSelection("a{bc}d", "{a$d}");
       
   testDeleteSelection("a{bc}", "{a$}");
       
   testDeleteSelection("a<{b}.c>", "{a}_<{$}.{c}>_{}");
       
   testDeleteSelection("a<{b.}c>", "{a}_<{$c}>_{}");
       
   testDeleteSelection("a<{b.c}>", "{a}_<{$}>_{}");
       
   testDeleteSelection("a{<b.c>}", "{a$}");
       
   testDeleteSelection("a{<b.c>.}d.e", "{a$d}.{e}");
       
   testDeleteSelection("a{<b.c>.d}.e", "{a$}.{e}");
       
   testDeleteSelection("a{<b.c>.d.}e", "{a$e}");     
   }
    
   
@Test
   
public void testSelectionOperation()
   {        
   testSelectionInsert('<', "a{bc}d", "{a}_<{bc}>_{$d}");
       
   testSelectionInsert('<', "a{b.c}d", "{a}_<{b}.{c}>_{$d}");
       
   testSelectionInsert('<', "a{b.}d", "{a}_<{b}.{}>_{$d}");
       
   testSelectionInsert('<', "a{b.}", "{a}_<{b}.{}>_{$}");
       
   testSelectionInsert('<', "a{b..}", "{a}_<{b}.{}.{}>_{$}");
       
   testSelectionInsert('<', "a{b.}.", "{a}_<{b}.{}>_{$}.{}");     
   }

   
@Test
   
public void testNoString()
   {        
   testSelectionInsert('\"', "a{bc}d", "{abc$d}");
       
   testSelectionInsert('\'', "a{bc}d", "{abc$d}");
       
   testInsert("ab\"", "{ab$}");
       
   testInsert("a\"b", "{ab$}");     
   }
    
   
private CaretPos makeCaretPos(int... xs)
   {        
   CaretPos p = null;
       
   for (int i = xs.length - 1; i >= 0; i--)
       {            
      p = new CaretPos(xs[i], p);         
      }
       
   return p;     
   }

   
@Test
   
public void testCaretPosMap()
   {        
   testCaretPosMap("abc", "{abc}",
               
   new CPM(0, makeCaretPos(0, 0)),
               
   new CPM(1, makeCaretPos(0, 1)),
               
   new CPM(2, makeCaretPos(0, 2))
       
   );
        
       
   testCaretPosMap("a.b.c", "{a}.{b}.{c}",
           
   new CPM(0, makeCaretPos(0, 0)),
           
   new CPM(1, makeCaretPos(0, 1)),
           
   new CPM(2, makeCaretPos(1, 0)),
           
   new CPM(3, makeCaretPos(1, 1)),
           
   new CPM(4, makeCaretPos(2, 0)),
           
   new CPM(5, makeCaretPos(2, 1))
       
   );

       
   testCaretPosMap("a<b.c>", "{a}_<{b}.{c}>_{}",
               
   new CPM(0, makeCaretPos(0, 0)),
               
   new CPM(1, makeCaretPos(0, 1)),
               
   new CPM(2, makeCaretPos(1, 0, 0)),
               
   new CPM(3, makeCaretPos(1, 0, 1)),
               
   new CPM(4, makeCaretPos(1, 1, 0)),
               
   new CPM(5, makeCaretPos(1, 1, 1)),
               
   new CPM(6, makeCaretPos(2, 0))
       
   );

       
   testCaretPosMap("a<b.c,d>", "{a}_<{b}.{c},{d}>_{}",
               
   new CPM(0, makeCaretPos(0, 0)),
               
   new CPM(1, makeCaretPos(0, 1)),
               
   new CPM(2, makeCaretPos(1, 0, 0)),
               
   new CPM(3, makeCaretPos(1, 0, 1)),
               
   new CPM(4, makeCaretPos(1, 1, 0)),
               
   new CPM(5, makeCaretPos(1, 1, 1)),
               
   new CPM(6, makeCaretPos(1, 2, 0)),
               
   new CPM(7, makeCaretPos(1, 2, 1)),
               
   new CPM(8, makeCaretPos(2, 0))
       
   );     
   }
    
   
@Test
   
public void testOvertype()
   {        
   
   testInsertExisting("$[]", "int", "{int$}_[{}]_{}");
       
   testInsertExisting("int$[]", "[",  "{int}_[{$}]_{}");

       
   
   testInsertExisting("a$.z", ".", "{a}.{$}.{z}");
       
   testInsertExisting("a$.z", ".b", "{a}.{b$}.{z}");

       
   
   testInsertExisting("a$,", ",", "{a},{$}");
       
   testInsertExisting("a<b$,>", ",", "{a}_<{b},{$}>_{}");
       
   testInsertExisting("a$,b", ",", "{a},{$},{b}");
       
   testInsertExisting("a<$,b>", ",", "{a}_<{},{$},{b}>_{}");     
   }
    
   
@Test
   
public void testBracket()
   {        
   testInsert("(", "{$}");
       
   testInsert("foo()", "{foo$}");     
   }
    
   
private void testNoComma(String str)
   {        
   InfixType type = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
   type.testingInsert(str, '\0');
       
   AtomicBoolean run = new AtomicBoolean(false);
   type.runIfCommaDirect((a, b) -> run.set(true));
   assertFalse("Checking comma did not run", run.get());     
   }

   
private void testComma(String src, String before, String after)
   {        
   InfixType type = StructuredSlot.testingModification(token -> new InfixType(null, null, token));
   type.testingInsert(src, '\0');
       
   String[] beforeAfter = new String[] {null, null
   };
type.runIfCommaDirect((a, b) -> {beforeAfter[0] = a; beforeAfter[1] = b;
});
assertEquals("Checking before comma: \"" + src + "\"", before, beforeAfter[0]);
assertEquals("Checking after comma: \"" + src + "\"", after, beforeAfter[1]);     
}
    
   
@Test
   
public void testComma()
   {
   testNoComma("aaa");
   testNoComma("a.b");
   testNoComma("a<b,c>");
   testComma(",", "", "");
   testComma("a,", "a", "");
   testComma(",b", "", "b");
   testComma("a,b", "a", "b");
   testComma("a<x>,b<y>", "a<x>", "b<y>");
   testComma("a<x,x2>,b<y,c<z1,z2>>", "a<x,x2>", "b<y,c<z1,z2>>");     
   } 
}
top, use, map, class TestTypeSlot

.   apply
.   evaluate

top, use, map, class CompareWrapper

.   CompareWrapper
.   equals
.   toString
.   testInsert
.   testMultiInsert
.   testInsertExisting
.   testBackspace
.   testBackspace
.   testDeleteSelection
.   testSelectionInsert

top, use, map, class CPM

.   CPM
.   toString
.   testCaretPosMap
.   testCaretPosMap
.   testOperators
.   testNew
.   testStrings
.   testBrackets
.   testBackspace
.   testFloating
.   testDeleteBracket
.   testDeleteSelection
.   testSelectionOperation
.   testNoString
.   makeCaretPos
.   testCaretPosMap
.   testOvertype
.   testBracket
.   testNoComma
.   testComma
.   testComma




738 neLoCode + 0 LoComm