<?php

namespace Doctrine\Tests\ORM\Functional\Ticket;

require_once __DIR__ . '/../../../TestInit.php';

class NewIssueTest extends \Doctrine\Tests\OrmFunctionalTestCase
{
    protected function setUp()
    {
        parent::setUp();
        try {
            $this->_schemaTool->createSchema(array(
                    $this->_em->getClassMetadata(__NAMESPACE__.'\SomeOrder'),
                    $this->_em->getClassMetadata(__NAMESPACE__.'\SomeOrderContainer')
            ));
        } catch (\Exception $e) {
            // Swallow all exceptions. We do not test the schema tool here.
        }
    }

    public function testIssue()
    {

        $em = $this->_em;
        $ordersRepo = $em->getRepository(__NAMESPACE__.'\SomeOrder');

        $order = $ordersRepo->find(1);
        if (!$order) {
        	$order = new SomeOrder();
        	$order->setNotes('Some note');
        	$em->persist($order);

        	$orderContainer = new SomeOrderContainer($order);
        	$em->persist($orderContainer);

        	$em->flush();
        }

        // Detach to reload entities from scratch
        $em->detach($order);

        // This works and sets 'Note from order'
        $ordersRepo->find(1);
        $order->setNotes('Note from order');
        $this->assertEquals('Note from order', $order->getNotes());
        $em->flush();
        $em->detach($order);

        //This doesnt set 'Note from container'
        $orderContainer = $em->getRepository(__NAMESPACE__.'\SomeOrderContainer')->find(1);
        $order = $orderContainer->getSomeOrder();
        $order->setNotes('Note from container');
        $em->flush();
        $this->assertEquals('Note from container', $order->getNotes());
        $em->detach($order);

        //This is the final persisted value (should be 'Note from container' but is 'Note from order'
        $order = $em->getRepository(__NAMESPACE__.'\SomeOrder')->find(1);
        $this->assertEquals('Note from container', $order->getNotes());

    }
}

abstract class AbstractNotifier implements \Doctrine\Common\NotifyPropertyChanged
{
    private $_listeners = array();

    protected function _setPropertyValue($propertyName, $newValue)
    {
        $refl = new \ReflectionClass($this);
        if (!$refl->hasProperty($propertyName)) {
            throw new \Exception('Class '.get_class($this).' does not have a property named "'.$propertyName.'"');
        }

        $currentValue = $this->$propertyName;

        // Check if new value is different from the current value.
        // Note: objects and scalar variables are compared here
        $change = $currentValue !== $newValue ? true : false;

        if ($change) {
            $this->$propertyName = $newValue;

            // Notify listeners
            $this->_notifyPropertyChanged($propertyName, $currentValue, $newValue);
        }
    }

    public function addPropertyChangedListener(\Doctrine\Common\PropertyChangedListener $listener)
    {
        $this->_listeners[] = $listener;
    }

    protected function _notifyPropertyChanged($propName, $oldValue, $newValue)
    {
        // Used internally to know which properties have been edited
        $this->_changedProperties[] = $propName;

        // Notify registered listeners
        if ($this->_listeners) {
            foreach ($this->_listeners as $listener) {
                $listener->propertyChanged($this, $propName, $oldValue, $newValue);
            }
        }
    }
}

/**
 * @Entity
 * @ChangeTrackingPolicy("NOTIFY")
 */
class SomeOrder extends AbstractNotifier
{
    /** @Id @GeneratedValue @Column(type="integer") */
    protected $id;
    /** @Column(type="string") */
    protected $notes;

    public function setNotes($notes)
    {
        $this->_setPropertyValue('notes', $notes);
    }

    public function getNotes()
    {
        return $this->notes;
    }
}

/**
 * @Entity
 */
class SomeOrderContainer extends AbstractNotifier
{
    /** @Id @GeneratedValue @Column(type="integer") */
    protected $id;
    /** @ManyToOne(targetEntity="SomeOrder", cascade={"all"}) */
    private $someorder;

    public function __construct(SomeOrder $order)
    {
	    $this->someorder = $order;
    }

    public function getSomeOrder()
    {
        return $this->someorder;
    }
}