package ca.ucalgary.seahawk.services.test;

import ca.ucalgary.seahawk.services.RegexParser;

import junit.framework.*;

import java.util.regex.*;

public class RegexParserTestCase extends TestCase{
    public static final String TEST_REGEX1 = "\\A\\s*(\\S+(\\x20{2,}|\t+|))+\n";
    public static final String TEST_CAPGRP1 = "\\S+(\\x20{2,}|\t+|)";
    public static final String TEST_CAPGRP2 = "\\x20{2,}|\t+|";
    public static final String TEST_DATA1 = "1\t2\t3\t4\n1'\t2'\t3'\4'\n";

    public static final String TEST_REGEX2 = "((?:(\\d+)\\s+-?\\((\\d+\\.\\d+)\\)\n)+)";
    public static final String TEST_CAPGRP2_1 = "(?:(\\d+)\\s+-?\\((\\d+\\.\\d+)\\)\n)+";
    public static final String TEST_CAPGRP2_2 = "\\d+";
    public static final String TEST_CAPGRP2_3 = "\\d+\\.\\d+";
    public static final String TEST_DATA2 = "100  (101.3)\n";

    public static final String TEST_REGEX3 = "foo\\s+(ba(r|z)\\s+){2}?qux";
    public static final String TEST_REGEX4 = "foo\\s+(ba(r|z)\\s+)+qux";
    public static final String TEST_REGEX5 = "foo\\s+(ba(r|z)\\s+)qux";
    public static final int TEST_CAPGRP3_RANGE_START = 6;
    public static final int TEST_CAPGRP3_RANGE_END = 21;
    public static final int TEST_CAPGRP4_RANGE_START = 6;
    public static final int TEST_CAPGRP4_RANGE_END = 18;
    public static final int TEST_CAPGRP5_RANGE_START = 6;
    public static final int TEST_CAPGRP5_RANGE_END = 17;

    public RegexParserTestCase(String name){
	super(name);
    }

    public void testRegexAssumptions(){
	Pattern pattern = null;
	try{
	    pattern = Pattern.compile(TEST_REGEX1);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not compile the regex \"" + TEST_REGEX1 + "\": " + e);
	}
	assertNotNull("Pattern.compile() returned null pattern for \""+TEST_REGEX1+"\"", pattern);

	Matcher matcher = pattern.matcher(TEST_DATA1);
	assertTrue("Regex \""+TEST_REGEX1+"\" did not match string \""+TEST_DATA1+"\" as expected", 
		   matcher.find());
	assertTrue("Regex did not match 8 chars expected, but rather " + matcher.group().length(), 
		   matcher.group().length() == 8);
	assertTrue("Regex did not have 2 capture groups as expected, but rather " + matcher.groupCount(), 
		   matcher.groupCount() == 2);
	assertTrue("Regex capture group 1 did not have length 1 as expected, but rather " + 
		   matcher.group(1).length(), 
		   matcher.group(1).length() == 1);
    }

    public void testCaptureGroupExtraction(){
	Pattern pattern = null;
	try{
	    pattern = Pattern.compile(TEST_REGEX1);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not compile the regex \"" + TEST_REGEX1 + "\": " + e);
	}
	assertNotNull("Pattern.compile() returned null pattern for \""+TEST_REGEX1+"\"", pattern);

	Matcher matcher = pattern.matcher(TEST_DATA1);	
	String captureGroup = null;
	try{
	    captureGroup = RegexParser.getCaptureGroupRegex(pattern, 1);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not extract capture group 1 from regex, encountered exception:" +e);
	}
	assertNotNull("The capture group returned was null", captureGroup);
	assertTrue("Capture group extraction failed.  Expected \"" +
		   TEST_CAPGRP1+"\", but got \""+captureGroup+"\"",
		   TEST_CAPGRP1.equals(captureGroup));

	try{
	    captureGroup = RegexParser.getCaptureGroupRegex(pattern, 2);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not extract capture group 2 from regex, encountered exception:" +e);
	}
	assertNotNull("The capture group returned was null", captureGroup);
	assertTrue("Capture group extraction failed.  Expected \"" +
		   TEST_CAPGRP2+"\", but got \""+captureGroup+"\"",
		   TEST_CAPGRP2.equals(captureGroup));

	// Should fail
	boolean failed = false;
	try{
	   captureGroup = RegexParser.getCaptureGroupRegex(pattern, 3);
	} catch(Exception e){
	    failed = true;
	}
	assertTrue("getCaptureGroupRegex() didn't fail when it was supposed to: " +
		   " capture group index 3 should be out of bounds", failed);

	try{
	    pattern = Pattern.compile(TEST_REGEX2);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not compile the regex \"" + TEST_REGEX2 + "\": " + e);
	}
	assertNotNull("Pattern.compile() returned null pattern for \""+TEST_REGEX2+"\"", pattern);

	matcher = pattern.matcher(TEST_DATA2);
	try{
	    captureGroup = RegexParser.getCaptureGroupRegex(pattern, 1);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not extract capture group 1 from regex, encountered exception:" +e);
	}
	assertNotNull("The capture group returned was null", captureGroup);
	assertTrue("Capture group extraction failed.  Expected \"" +
		   TEST_CAPGRP2_1+"\", but got \""+captureGroup+"\"",
		   TEST_CAPGRP2_1.equals(captureGroup));

	try{
	    captureGroup = RegexParser.getCaptureGroupRegex(pattern, 2);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not extract capture group 2 from regex, encountered exception:" +e);
	}
	assertNotNull("The capture group returned was null", captureGroup);
	assertTrue("Capture group extraction failed.  Expected \"" +
		   TEST_CAPGRP2_2+"\", but got \""+captureGroup+"\"",
		   TEST_CAPGRP2_2.equals(captureGroup));

	try{
	    captureGroup = RegexParser.getCaptureGroupRegex(pattern, 3);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not extract capture group 3 from regex, encountered exception:" +e);
	}
	assertNotNull("The capture group returned was null", captureGroup);
	assertTrue("Capture group extraction failed.  Expected \"" +
		   TEST_CAPGRP2_3+"\", but got \""+captureGroup+"\"",
		   TEST_CAPGRP2_3.equals(captureGroup));
    }

    /**
     * Ensure that extraction of capture groups, including their quantifier, works.
     */
    public void testCaptureGroupQuantifier(){
	Pattern pattern = null;
	try{
	    pattern = Pattern.compile(TEST_REGEX3);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not compile the regex \"" + TEST_REGEX3 + "\": " + e);
	}
	assertNotNull("Pattern.compile() returned null pattern for \""+TEST_REGEX3+"\"", pattern);
	
	int range[] = null;
	try{
	    range = RegexParser.getCaptureGroupRange(pattern, 1, true);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not extract capture group 1 from regex, encountered exception:" +e);
	}
	assertNotNull("getCaptureGroupRange() returned null range for \""+TEST_REGEX3+"\" capture group 1", pattern);
	assertTrue("getCaptureGroupRange() failed on \""+ TEST_REGEX3+"\", expected " + 
		   "start of capture at index " + TEST_CAPGRP3_RANGE_START + ". but found " + range[0], 
		   range[0] == TEST_CAPGRP3_RANGE_START);
	assertTrue("getCaptureGroupRange() failed on \""+ TEST_REGEX3+"\", expected " + 
		   "end of capture at index " + TEST_CAPGRP3_RANGE_END + ". but found " + range[1], 
		   range[1] == TEST_CAPGRP3_RANGE_END);
	
	try{
	    pattern = Pattern.compile(TEST_REGEX4);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not compile the regex \"" + TEST_REGEX4 + "\": " + e);
	}
	assertNotNull("Pattern.compile() returned null pattern for \""+TEST_REGEX4+"\"", pattern);
	
	try{
	    range = RegexParser.getCaptureGroupRange(pattern, 1, true);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not extract capture group 1 from regex, encountered exception:" +e);
	}
	assertNotNull("getCaptureGroupRange() returned null range for \""+TEST_REGEX4+"\" capture group 1", pattern);
	assertTrue("getCaptureGroupRange() failed on \""+ TEST_REGEX4+"\", expected " + 
		   "start of capture at index " + TEST_CAPGRP4_RANGE_START + ". but found " + range[0], 
		   range[0] == TEST_CAPGRP4_RANGE_START);
	assertTrue("getCaptureGroupRange() failed on \""+ TEST_REGEX4+"\", expected " + 
		   "end of capture at index " + TEST_CAPGRP4_RANGE_END + ". but found " + range[1], 
		   range[1] == TEST_CAPGRP4_RANGE_END);
	
	try{
	    pattern = Pattern.compile(TEST_REGEX5);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not compile the regex \"" + TEST_REGEX5 + "\": " + e);
	}
	assertNotNull("Pattern.compile() returned null pattern for \""+TEST_REGEX5+"\"", pattern);
	
	try{
	    range = RegexParser.getCaptureGroupRange(pattern, 1, true);
	} catch(Exception e){
	    e.printStackTrace();
	    fail("Could not extract capture group 1 from regex, encountered exception:" +e);
	}
	assertNotNull("getCaptureGroupRange() returned null range for \""+TEST_REGEX5+"\" capture group 1", pattern);
	assertTrue("getCaptureGroupRange() failed on \""+ TEST_REGEX5+"\", expected " + 
		   "start of capture at index " + TEST_CAPGRP5_RANGE_START + ". but found " + range[0], 
		   range[0] == TEST_CAPGRP5_RANGE_START);
	assertTrue("getCaptureGroupRange() failed on \""+ TEST_REGEX5+"\", expected " + 
		   "end of capture at index " + TEST_CAPGRP5_RANGE_END + ". but found " + range[1], 
		   range[1] == TEST_CAPGRP5_RANGE_END);
	
    }

    /**
     * @return a test suite for all the test methods of this test case.
     */
    public static Test suite() {

	TestSuite suite = new TestSuite();
 	suite.addTest(new RegexParserTestCase("testRegexAssumptions"));
 	suite.addTest(new RegexParserTestCase("testCaptureGroupExtraction"));
 	suite.addTest(new RegexParserTestCase("testCaptureGroupQuantifier"));
        return suite;
    }

    public static void main(String[] args){
	junit.textui.TestRunner.run(suite());
    }
}
