<?php

class WikiParser_PluginMatcherTest extends TikiTestCase
{
	function toArray( $matcher )
	{
		$ret = array();
		foreach( $matcher as $match ) {
			$ret[] = $match;
		}

		return $ret;
	}

	function doMatch( $string, $expecting )
	{
		$matches = WikiParser_PluginMatcher::match( $string );

		$ret = $this->toArray( $matches );

		$this->assertEquals( $expecting, count($matches) );
		$this->assertEquals( $expecting, count($ret) );

		return $ret;
	}
	function testShortMatch()
	{
		$matches = $this->doMatch( ' {img src=foo.png} ', 1 );

		$match = $matches[0];
		$this->assertEquals( 'img', $match->getName() );
		$this->assertEquals( 'src=foo.png', $match->getArguments() );
		$this->assertEquals( null, $match->getBody() );
		$this->assertEquals( 1, $match->getStart() );
		$this->assertEquals( 18, $match->getEnd() );
	}

	function testFullMatch()
	{
		$matches = $this->doMatch( '{DIV(hello=>world)} foobar {DIV}', 1 );
		
		$match = $matches[0];
		$this->assertEquals( 'div', $match->getName() );
		$this->assertEquals( 'hello=>world', $match->getArguments() );
		$this->assertEquals( ' foobar ', $match->getBody() );
		$this->assertEquals( 0, $match->getStart() );
		$this->assertEquals( 32, $match->getEnd() );
	}

	function testNestedShortMatch()
	{
		$matches = $this->doMatch( '{A(foo=>bar)} {a hello=world} {A}', 2 );
		
		$match = $matches[0];
		$this->assertEquals( 'a', $match->getName() );
		$this->assertEquals( 'foo=>bar', $match->getArguments() );
		$this->assertEquals( ' {a hello=world} ', $match->getBody() );
		$this->assertEquals( 0, $match->getStart() );
		$this->assertEquals( 33, $match->getEnd() );
		
		$match = $matches[1];
		$this->assertEquals( 'a', $match->getName() );
		$this->assertEquals( 'hello=world', $match->getArguments() );
		$this->assertEquals( null, $match->getBody() );
		$this->assertEquals( 14, $match->getStart() );
		$this->assertEquals( 29, $match->getEnd() );

		$this->assertTrue( $matches[1]->inside( $matches[0] ) );
		$this->assertFalse( $matches[0]->inside( $matches[1] ) );
	}

	function testSideBySideFullMatch()
	{
		$matches = $this->doMatch( '{A(hello=world)} middle {A} between {A(bar=baz)} center {A}', 2 );

		// Make sure the matches found are those we expect
		
		$match = $matches[0];
		$this->assertEquals( 'a', $match->getName() );
		$this->assertEquals( 'hello=world', $match->getArguments() );
		$this->assertEquals( ' middle ', $match->getBody() );

		$match = $matches[1];
		$this->assertEquals( 'a', $match->getName() );
		$this->assertEquals( 'bar=baz', $match->getArguments() );
		$this->assertEquals( ' center ', $match->getBody() );

		// Corrolary of the above
		$this->assertFalse( $matches[0]->inside( $matches[1] ) );
		$this->assertFalse( $matches[1]->inside( $matches[0] ) );
	}

	function testNestedFullMatch()
	{
		$matches = $this->doMatch( '{A(foo=>bar)} {A(hello=world)} middle {A} between {A(bar=baz)} center {A} {A}', 3 );

		// Make sure the matches found are those we expect
		
		$match = $matches[0];
		$this->assertEquals( 'a', $match->getName() );
		$this->assertEquals( 'foo=>bar', $match->getArguments() );
		$this->assertEquals( ' {A(hello=world)} middle {A} between {A(bar=baz)} center {A} ', $match->getBody() );
		
		$match = $matches[1];
		$this->assertEquals( 'a', $match->getName() );
		$this->assertEquals( 'hello=world', $match->getArguments() );
		$this->assertEquals( ' middle ', $match->getBody() );

		$match = $matches[2];
		$this->assertEquals( 'a', $match->getName() );
		$this->assertEquals( 'bar=baz', $match->getArguments() );
		$this->assertEquals( ' center ', $match->getBody() );

		// The two inner matches are inside the outer
		$this->assertTrue( $matches[1]->inside( $matches[0] ) );
		$this->assertTrue( $matches[2]->inside( $matches[0] ) );

		// Corrolary of the above
		$this->assertFalse( $matches[0]->inside( $matches[1] ) );
		$this->assertFalse( $matches[0]->inside( $matches[2] ) );

		// The two inner matches are not within each other
		$this->assertFalse( $matches[1]->inside( $matches[2] ) );
		$this->assertFalse( $matches[2]->inside( $matches[1] ) );
	}

	function testUnclosedFullMatch()
	{
		$matches = $this->doMatch( '{A(unclosed=>bar)} {A(unclosed=world)} middle {A}', 1 );

		$match = $matches[0];
		$this->assertEquals( 'a', $match->getName() );
		$this->assertEquals( 'unclosed=world', $match->getArguments() );
		$this->assertEquals( ' middle ', $match->getBody() );
	}

	function testQuotesSkipInArguments()
	{
		$matches = $this->doMatch( '{a foo=>"bar \" } {"}', 1 );
		
		$match = $matches[0];
		$this->assertEquals( 'a', $match->getName() );
		$this->assertEquals( 'foo=>"bar \" } {"', $match->getArguments() );
	}

	function testSkipNoParse()
	{
		$matches = $this->doMatch( '{A()} ~np~ {A} {b} {B()} {B} ~/np~ {A} ~np~ {b} ~/np~', 1 );
		$this->assertEquals( ' ~np~ {A} {b} {B()} {B} ~/np~ ', $matches[0]->getBody() );
	}


	function testVerySimpleMatch()
	{
		$string = '{c}';
		$matches = WikiParser_PluginMatcher::match( $string );
		$this->assertEquals( 1, count($matches) );
	}

	function testSimpleReplacement()
	{
		$string = '{c} {A()} {b} {A} {b}';
		$matches = WikiParser_PluginMatcher::match( $string );
		$this->assertEquals( 4, count($matches) );

		$orig = $this->toArray( $matches );

		$orig[2]->replaceWith( 'Hello' );

		$this->assertEquals( '{c} {A()} Hello {A} {b}', $matches->getText() );
	}

	function testLargerReplacement()
	{
		$string = '{c} {A()} {b} {A} {b}';
		$matches = WikiParser_PluginMatcher::match( $string );
		$this->assertEquals( 4, count($matches) );

		$orig = $this->toArray( $matches );

		$orig[1]->replaceWith( 'Hello' );

		$this->assertEquals( '{c} Hello {b}', $matches->getText() );
	}

	function testMatchReplacementChangesOffsets()
	{
		$matches = $this->doMatch( '{c} {A()} {b} {A} {b}', 4 );
		$this->assertEquals( 4, count($matches) );
		
		$lastMatch = $matches[3];
		$innerMatch = $matches[2];

		// Test initial positions
		$this->assertEquals( 0, $matches[0]->getStart() );
		$this->assertEquals( 3, $matches[0]->getEnd() );

		$this->assertEquals( 18, $lastMatch->getStart() );
		$this->assertEquals( 21, $lastMatch->getEnd() );

		$matches[1]->replaceWith( 'Hello' );

		// First one does not move
		$this->assertEquals( 0, $matches[0]->getStart() );
		$this->assertEquals( 3, $matches[0]->getEnd() );

		// Second one is shifted to the left
		$this->assertEquals( 10, $lastMatch->getStart() );
		$this->assertEquals( 13, $lastMatch->getEnd() );

		// One of them is gone
		$this->assertFalse( $innerMatch->getStart() );
		$this->assertFalse( $innerMatch->getEnd() );
	}

	function testMatchReplacementDecreasesCount()
	{
		$string = '{c} {A()} {b} {A} {b}';
		$matches = WikiParser_PluginMatcher::match( $string );

		$this->assertEquals( 4, count($matches) );

		$orig = $this->toArray( $matches );

		$orig[1]->replaceWith( 'Hello' );

		$this->assertEquals( 2, count($matches) );
	}

	function testIterationSurvivesReplacement()
	{
		$string = '{c} {A()} {b} {A} {d}';
		$matches = WikiParser_PluginMatcher::match( $string );
		$this->assertEquals( 4, count($matches) );

		$expected = array( 'c', 'a', 'd' );
		$iteration = 0;
		foreach( $matches as $match ) {
			$this->assertEquals( $expected[$iteration], $match->getName() );

			if( $iteration == 1 ) {
				$match->replaceWith( 'Hello' );
			}

			++$iteration;
		}

		$this->assertEquals( 3, $iteration );
	}

	function testGeneratedPluginsGetMatched()
	{
		$string = '{c} {A()} {b} {A} {d}';
		$matches = WikiParser_PluginMatcher::match( $string );
		$this->assertEquals( 4, count($matches) );

		$expected = array( 'c', 'a', 'b', 'f', 'd' );
		$iteration = 0;
		foreach( $matches as $match ) {
			$this->assertEquals( $expected[$iteration], $match->getName() );

			if( $iteration == 2 ) {
				$match->replaceWith( '{f}' );
			}

			++$iteration;
		}
	}

/*
	// TODO : Replacement re-find existing
	// TODO : Replacement original vs generated
	*/
}
