<?php

/**
 * @package framework
 * @subpackage tests
 */
class GridFieldDetailFormTest extends FunctionalTest {

	protected static $fixture_file = 'GridFieldDetailFormTest.yml';

	protected $extraDataObjects = array(
		'GridFieldDetailFormTest_Person',
		'GridFieldDetailFormTest_PeopleGroup',
		'GridFieldDetailFormTest_Category',
	);

	public function testValidator() {
		$this->logInWithPermission('ADMIN');

		$response = $this->get('GridFieldDetailFormTest_Controller');
		$this->assertFalse($response->isError());
		$parser = new CSSContentParser($response->getBody());
		$addlinkitem = $parser->getBySelector('.ss-gridfield .new-link');
		$addlink = (string) $addlinkitem[0]['href'];

		$response = $this->get($addlink);
		$this->assertFalse($response->isError());

		$parser = new CSSContentParser($response->getBody());
		$addform = $parser->getBySelector('#Form_ItemEditForm');
		$addformurl = (string) $addform[0]['action'];

		$response = $this->post(
			$addformurl,
			array(
				'FirstName' => 'Jeremiah',
				'ajax' => 1,
				'action_doSave' => 1
			)
		);

		$parser = new CSSContentParser($response->getBody());
		$errors = $parser->getBySelector('span.required');
		$this->assertEquals(1, count($errors));

		$response = $this->post(
			$addformurl,
			array(
				'ajax' => 1,
				'action_doSave' => 1
			)
		);

		$parser = new CSSContentParser($response->getBody());
		$errors = $parser->getBySelector('span.required');
		$this->assertEquals(2, count($errors));
	}

	public function testAddForm() {
		$this->logInWithPermission('ADMIN');
		$group = GridFieldDetailFormTest_PeopleGroup::get()
			->filter('Name', 'My Group')
			->sort('Name')
			->First();
		$count = $group->People()->Count();

		$response = $this->get('GridFieldDetailFormTest_Controller');
		$this->assertFalse($response->isError());
		$parser = new CSSContentParser($response->getBody());
		$addlinkitem = $parser->getBySelector('.ss-gridfield .new-link');
		$addlink = (string) $addlinkitem[0]['href'];

		$response = $this->get($addlink);
		$this->assertFalse($response->isError());

		$parser = new CSSContentParser($response->getBody());
		$addform = $parser->getBySelector('#Form_ItemEditForm');
		$addformurl = (string) $addform[0]['action'];

		$response = $this->post(
			$addformurl,
			array(
				'FirstName' => 'Jeremiah',
				'Surname' => 'BullFrog',
				'action_doSave' => 1
			)
		);
		$this->assertFalse($response->isError());

		$group = GridFieldDetailFormTest_PeopleGroup::get()
			->filter('Name', 'My Group')
			->sort('Name')
			->First();
		$this->assertEquals($count + 1, $group->People()->Count());
	}

	public function testViewForm() {
		$this->logInWithPermission('ADMIN');

		$response = $this->get('GridFieldDetailFormTest_Controller');
		$parser   = new CSSContentParser($response->getBody());

		$viewLink = $parser->getBySelector('.ss-gridfield-items .first .view-link');
		$viewLink = (string) $viewLink[0]['href'];

		$response = $this->get($viewLink);
		$parser   = new CSSContentParser($response->getBody());

		$firstName = $parser->getBySelector('#Form_ItemEditForm_FirstName');
		$surname   = $parser->getBySelector('#Form_ItemEditForm_Surname');

		$this->assertFalse($response->isError());
		$this->assertEquals('Jane', (string) $firstName[0]);
		$this->assertEquals('Doe', (string) $surname[0]);
	}

	public function testEditForm() {
		$this->logInWithPermission('ADMIN');
		$group = GridFieldDetailFormTest_PeopleGroup::get()
			->filter('Name', 'My Group')
			->sort('Name')
			->First();
		$firstperson = $group->People()->First();
		$this->assertTrue($firstperson->Surname != 'Baggins');

		$response = $this->get('GridFieldDetailFormTest_Controller');
		$this->assertFalse($response->isError());
		$parser = new CSSContentParser($response->getBody());
		$editlinkitem = $parser->getBySelector('.ss-gridfield-items .first .edit-link');
		$editlink = (string) $editlinkitem[0]['href'];

		$response = $this->get($editlink);
		$this->assertFalse($response->isError());

		$parser = new CSSContentParser($response->getBody());
		$editform = $parser->getBySelector('#Form_ItemEditForm');
		$editformurl = (string) $editform[0]['action'];
		
		$response = $this->post(
			$editformurl,
			array(
				'FirstName' => 'Bilbo',
				'Surname' => 'Baggins',
				'action_doSave' => 1
			)
		);
		$this->assertFalse($response->isError());

		$group = GridFieldDetailFormTest_PeopleGroup::get()
			->filter('Name', 'My Group')
			->sort('Name')
			->First();
		$this->assertDOSContains(array(array('Surname' => 'Baggins')), $group->People());
	}

	public function testEditFormWithManyManyExtraData() {
		$this->logInWithPermission('ADMIN');

		// Lists all categories for a person
		$response = $this->get('GridFieldDetailFormTest_CategoryController');
		$this->assertFalse($response->isError());
		$parser = new CSSContentParser($response->getBody());
		$editlinkitem = $parser->getBySelector('.ss-gridfield-items .first .edit-link');
		$editlink = (string) $editlinkitem[0]['href'];

		// Edit a single category, incl. manymany extrafields added manually
		// through GridFieldDetailFormTest_CategoryController
		$response = $this->get($editlink);
		$this->assertFalse($response->isError());
		$parser = new CSSContentParser($response->getBody());
		$editform = $parser->getBySelector('#Form_ItemEditForm');
		$editformurl = (string) $editform[0]['action'];

		$manyManyField = $parser->getByXpath('//*[@id="Form_ItemEditForm"]//input[@name="ManyMany[IsPublished]"]');
		$this->assertTrue((bool)$manyManyField);
		
		$response = $this->post(
			$editformurl,
			array(
				'Name' => 'Updated Category',
				'ManyMany' => array('IsPublished' => 1),
				'action_doSave' => 1
			)
		);
		$this->assertFalse($response->isError());

		$person = GridFieldDetailFormTest_Person::get()->sort('FirstName')->First();
		$category = $person->Categories()->filter(array('Name' => 'Updated Category'))->First();
		$this->assertEquals(
			array('IsPublished' => 1),
			$person->Categories()->getExtraData('', $category->ID)
		);
	}

	public function testNestedEditForm() {
		$this->logInWithPermission('ADMIN');

		$group = $this->objFromFixture('GridFieldDetailFormTest_PeopleGroup', 'group');
		$person = $group->People()->First();
		$category = $person->Categories()->First();

		// Get first form (GridField managing PeopleGroup)
		$response = $this->get('GridFieldDetailFormTest_GroupController');
		$this->assertFalse($response->isError());
		$parser = new CSSContentParser($response->getBody());

		$groupEditLink = $parser->getByXpath('//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "'
			. $group->ID . '")]//a');
		$this->assertEquals(
			'GridFieldDetailFormTest_GroupController/Form/field/testfield/item/' . $group->ID . '/edit',
			(string)$groupEditLink[0]['href']
		);

		// Get second level form (GridField managing Person)
		$response = $this->get((string)$groupEditLink[0]['href']);
		$this->assertFalse($response->isError());
		$parser = new CSSContentParser($response->getBody());
		$personEditLink = $parser->getByXpath('//fieldset[@id="Form_ItemEditForm_People"]' .
			'//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $person->ID . '")]//a');		
		$this->assertEquals(
			sprintf('GridFieldDetailFormTest_GroupController/Form/field/testfield/item/%d/ItemEditForm/field/People'
				. '/item/%d/edit', $group->ID, $person->ID),
			(string)$personEditLink[0]['href']
		);

		// Get third level form (GridField managing Category)
		$response = $this->get((string)$personEditLink[0]['href']);
		$this->assertFalse($response->isError());
		$parser = new CSSContentParser($response->getBody());
		$categoryEditLink = $parser->getByXpath('//fieldset[@id="Form_ItemEditForm_Categories"]'
			. '//tr[contains(@class, "ss-gridfield-item") and contains(@data-id, "' . $category->ID . '")]//a');	
		$this->assertEquals(
			sprintf('GridFieldDetailFormTest_GroupController/Form/field/testfield/item/%d/ItemEditForm/field/People'
				. '/item/%d/ItemEditForm/field/Categories/item/%d/edit', $group->ID, $person->ID, $category->ID),
			(string)$categoryEditLink[0]['href']
		);

		// Fourth level form would be a Category detail view
	}

	public function testCustomItemRequestClass() {
		$this->logInWithPermission('ADMIN');

		$component = new GridFieldDetailForm();
		$this->assertEquals('GridFieldDetailForm_ItemRequest', $component->getItemRequestClass());
		$component->setItemRequestClass('GridFieldDetailFormTest_ItemRequest');
		$this->assertEquals('GridFieldDetailFormTest_ItemRequest', $component->getItemRequestClass());
	}

	public function testItemEditFormCallback() {
		$this->logInWithPermission('ADMIN');
		
		$category = new GridFieldDetailFormTest_Category();
		$component = new GridFieldDetailForm();
		$component->setItemEditFormCallback(function($form, $component) {
			$form->Fields()->push(new HiddenField('Callback'));
		});
		// Note: A lot of scaffolding to execute the tested logic,
		// due to the coupling of form creation with request handling (and its context)
		$request = new GridFieldDetailForm_ItemRequest(
			GridField::create('Categories', 'Categories'),
			$component,
			$category,
			new Controller(),
			'Form'
		);
		$form = $request->ItemEditForm();
		$this->assertNotNull($form->Fields()->fieldByName('Callback'));
	}

	/**
	 * Tests that a has-many detail form is pre-populated with the parent ID.
	 */
	public function testHasManyFormPrePopulated() {
		$group = $this->objFromFixture(
			'GridFieldDetailFormTest_PeopleGroup', 'group'
		);

		$this->logInWithPermission('ADMIN');

		$response = $this->get('GridFieldDetailFormTest_Controller');
		$parser = new CSSContentParser($response->getBody());
		$addLink = $parser->getBySelector('.ss-gridfield .new-link');
		$addLink = (string) $addLink[0]['href'];

		$response = $this->get($addLink);
		$parser = new CSSContentParser($response->getBody());
		$title = $parser->getBySelector('#GroupID span');
		$id = $parser->getBySelector('#GroupID input');

		$this->assertEquals($group->Name, (string) $title[0]);
		$this->assertEquals($group->ID, (string) $id[0]['value']);
	}

}

/**
 * @package framework
 * @subpackage tests
 */

class GridFieldDetailFormTest_Person extends DataObject implements TestOnly {

	private static $db = array(
		'FirstName' => 'Varchar',
		'Surname' => 'Varchar'
	);

	private static $has_one = array(
		'Group' => 'GridFieldDetailFormTest_PeopleGroup'
	);

	private static $many_many = array(
		'Categories' => 'GridFieldDetailFormTest_Category'
	);

	private static $many_many_extraFields = array(
		'Categories' => array(
			'IsPublished' => 'Boolean'
		)
	);

	private static $default_sort = '"FirstName"';

	public function getCMSFields() {
		$fields = parent::getCMSFields();
		// TODO No longer necessary once FormScaffolder uses GridField
		$fields->replaceField('Categories',
			GridField::create('Categories', 'Categories',
				$this->Categories(),
				GridFieldConfig_RelationEditor::create()
			)
		);
		return $fields;
	}

	public function getCMSValidator() {
		return new RequiredFields(array(
			'FirstName', 'Surname'
		));
	}
}

/**
 * @package framework
 * @subpackage tests
 */

class GridFieldDetailFormTest_PeopleGroup extends DataObject implements TestOnly {
	private static $db = array(
		'Name' => 'Varchar'
	);

	private static $has_many = array(
		'People' => 'GridFieldDetailFormTest_Person'
	);

	private static $default_sort = '"Name"';

	public function getCMSFields() {
		$fields = parent::getCMSFields();
		// TODO No longer necessary once FormScaffolder uses GridField
		$fields->replaceField('People',
			GridField::create('People', 'People',
				$this->People(),
				GridFieldConfig_RelationEditor::create()
			)
		);
		return $fields;
	}
}

/**
 * @package framework
 * @subpackage tests
 */

class GridFieldDetailFormTest_Category extends DataObject implements TestOnly {
	
	private static $db = array(
		'Name' => 'Varchar'
	);

	private static $belongs_many_many = array(
		'People' => 'GridFieldDetailFormTest_Person'
	);

	private static $default_sort = '"Name"';

	public function getCMSFields() {
		$fields = parent::getCMSFields();
		// TODO No longer necessary once FormScaffolder uses GridField
		$fields->replaceField('People',
			GridField::create('People', 'People',
				$this->People(),
				GridFieldConfig_RelationEditor::create()
			)
		);
		return $fields;
	}
}

/**
 * @package framework
 * @subpackage tests
 */

class GridFieldDetailFormTest_Controller extends Controller implements TestOnly {
	
	private static $allowed_actions = array('Form');

	protected $template = 'BlankPage';

	public function Form() {
		$group = GridFieldDetailFormTest_PeopleGroup::get()
			->filter('Name', 'My Group')
			->sort('Name')
			->First();

		$field = new GridField('testfield', 'testfield', $group->People());
		$field->getConfig()->addComponent(new GridFieldToolbarHeader());
		$field->getConfig()->addComponent(new GridFieldAddNewButton('toolbar-header-right'));
		$field->getConfig()->addComponent(new GridFieldViewButton());
		$field->getConfig()->addComponent(new GridFieldEditButton());
		$field->getConfig()->addComponent($gridFieldForm = new GridFieldDetailForm($this, 'Form'));
		$field->getConfig()->addComponent(new GridFieldEditButton());
		return new Form($this, 'Form', new FieldList($field), new FieldList());
	}
}

/**
 * @package framework
 * @subpackage tests
 */

class GridFieldDetailFormTest_GroupController extends Controller implements TestOnly {

	private static $allowed_actions = array('Form');

	protected $template = 'BlankPage';

	public function Form() {
		$field = new GridField('testfield', 'testfield', GridFieldDetailFormTest_PeopleGroup::get()->sort('Name'));
		$field->getConfig()->addComponent($gridFieldForm = new GridFieldDetailForm($this, 'Form'));
		$field->getConfig()->addComponent(new GridFieldToolbarHeader());
		$field->getConfig()->addComponent(new GridFieldAddNewButton('toolbar-header-right'));
		$field->getConfig()->addComponent(new GridFieldEditButton());
		return new Form($this, 'Form', new FieldList($field), new FieldList());
	}
}

/**
 * @package framework
 * @subpackage tests
 */

class GridFieldDetailFormTest_CategoryController extends Controller implements TestOnly {

	private static $allowed_actions = array('Form');
	
	protected $template = 'BlankPage';

	public function Form() {
		// GridField lists categories for a specific person
		$person = GridFieldDetailFormTest_Person::get()->sort('FirstName')->First();
		$detailFields = singleton('GridFieldDetailFormTest_Category')->getCMSFields();
		$detailFields->addFieldToTab('Root.Main', new CheckboxField('ManyMany[IsPublished]'));
		$field = new GridField('testfield', 'testfield', $person->Categories());
		$field->getConfig()->addComponent($gridFieldForm = new GridFieldDetailForm($this, 'Form'));
		$gridFieldForm->setFields($detailFields);
		$field->getConfig()->addComponent(new GridFieldToolbarHeader());
		$field->getConfig()->addComponent(new GridFieldAddNewButton('toolbar-header-right'));
		$field->getConfig()->addComponent(new GridFieldEditButton());
		return new Form($this, 'Form', new FieldList($field), new FieldList());
	}
}

/**
 * @package framework
 * @subpackage tests
 */
class GridFieldDetailFormTest_ItemRequest extends GridFieldDetailForm_ItemRequest implements TestOnly { }