TEST_F( TestCommandFixture, creatMacro )
{
	auto & controller = getReflectionController();

	auto objHandle = klass_->createManagedObject();

	PropertyAccessor counter = klass_->bindProperty("counter", objHandle );
	CHECK(counter.isValid());

	const int TEST_VALUE = 57;
	{
		int value = TEST_VALUE;
		controller.setValue( counter, value );
	}

	{
		// TODO: wait on controller
		controller.getValue( counter );
	}

	{
		auto & commandSystemProvider = getCommandSystemProvider();
		auto & history = commandSystemProvider.getHistory();
		commandSystemProvider.createMacro( history );
		CHECK(commandSystemProvider.getMacros().empty() == false );
	}
}
//==============================================================================
bool CompoundCommand::validateArguments(const ObjectHandle& arguments) const
{
	ICommandManager* cmdSysProvider = getCommandSystemProvider();
	if (cmdSysProvider == nullptr)
	{
		return false;
	}

	MacroEditObject* ccArgs = arguments.getBase<MacroEditObject>();
	if (ccArgs == nullptr)
	{
		return false;
	}
	if (ccArgs->getArgCount() != subCommands_.size())
	{
		return false;
	}

	for (SubCommandCollection::size_type i = 0; i < subCommands_.size(); ++i)
	{
		Command* command = cmdSysProvider->findCommand(subCommands_[i].first.c_str());

		if (command == nullptr)
		{
			return false;
		}
		if (!command->validateArguments(ccArgs->getCommandArgument(i)))
		{
			return false;
		}
	}

	return true;
}
//==============================================================================
void CompoundCommand::initDisplayData(IDefinitionManager& defManager, IReflectionController* controller)
{
	auto cmdSysProvider = getCommandSystemProvider();
	assert(cmdSysProvider != nullptr);
	const auto pDefinition = defManager.getDefinition(getClassIdentifier<MacroObject>());
	assert(pDefinition != nullptr);

	macroObject_ = defManager.create<MacroObject>(false);
	macroObject_->init(*cmdSysProvider, defManager, controller, id_.c_str());
}
TEST_F( TestCommandFixture, undo_redo )
{
	auto & controller = getReflectionController();

	auto objHandle = klass_->createManagedObject();

	PropertyAccessor counter = klass_->bindProperty("counter", objHandle );
	CHECK(counter.isValid());
	int oldValue = -1;
	{
		Variant variant = counter.getValue();
		CHECK(variant.tryCast( oldValue ));
	}
	auto & commandSystemProvider = getCommandSystemProvider();
	const int TEST_VALUE = 57;
	{
		int value = TEST_VALUE;
		
		controller.setValue( counter, value );
	}

	{
		int value = 0;
		Variant variant = controller.getValue( counter );
		CHECK(variant.tryCast( value ));
		CHECK_EQUAL(TEST_VALUE, value);
	}

	{
		commandSystemProvider.undo();
		CHECK(!commandSystemProvider.canUndo());
		CHECK(commandSystemProvider.canRedo());

		int value = 0;
		Variant variant = controller.getValue( counter );
		CHECK(variant.tryCast( value ));
		CHECK_EQUAL(oldValue, value);
	}

	{
		commandSystemProvider.redo();
		CHECK(commandSystemProvider.canUndo());
		CHECK(!commandSystemProvider.canRedo());

		int value = 0;
		Variant variant = controller.getValue( counter );
		CHECK(variant.tryCast( value ));
		CHECK_EQUAL(TEST_VALUE, value);
	}
}
TEST_F( TestCommandFixture, threadCommands )
{
	// This test attempts to verify commands do not deadlock.
	// TODO: waitForInstance need to take a timeout to properly handle when this test fails
	auto & commandManager = getCommandSystemProvider();
	
	auto command = commandManager.queueCommand( TestThreadCommand::generateId( CommandThreadAffinity::UI_THREAD ).c_str() );
	commandManager.waitForInstance( command );

	command = commandManager.queueCommand( TestThreadCommand::generateId( CommandThreadAffinity::COMMAND_THREAD ).c_str() );
	commandManager.waitForInstance( command );

	command = commandManager.queueCommand( TestThreadCommand::generateId( CommandThreadAffinity::ANY_THREAD ).c_str() );
	commandManager.waitForInstance( command );
}
TEST_F( TestCommandFixture, executeMacro )
{
	auto & controller = getReflectionController();

	auto objHandle = klass_->createManagedObject();

	PropertyAccessor counter = klass_->bindProperty("counter", objHandle );
	CHECK(counter.isValid());

	const int TEST_VALUE = 57;
	{
		int value = TEST_VALUE;
		controller.setValue( counter, value );
	}

	{
		// TODO: wait on controller
		controller.getValue( counter );
	}

	{
		auto & commandSystemProvider = getCommandSystemProvider();
		commandSystemProvider.undo();
		auto & history = commandSystemProvider.getHistory();
		commandSystemProvider.createMacro( history, "Macro1" );
		CHECK(commandSystemProvider.getMacros().empty() == false );
		auto macroObj = static_cast<CompoundCommand*>( commandSystemProvider.findCommand( "Macro1" ) )->getMacroObject();
		auto instObj = macroObj.getBase<MacroObject>()->executeMacro();
		CommandInstancePtr inst = instObj.getBase<CommandInstance>();
		commandSystemProvider.waitForInstance( inst );
		{
			PropertyAccessor counter = klass_->bindProperty("counter", objHandle );
			int value = 0;
			Variant variant = controller.getValue( counter );
			CHECK(variant.tryCast( value ));
			CHECK_EQUAL(TEST_VALUE, value);
		}
	}
}
//==============================================================================
ObjectHandle CompoundCommand::execute(const ObjectHandle& arguments) const
{
	auto cmdSysProvider = getCommandSystemProvider();
	assert(cmdSysProvider != nullptr);
	MacroEditObject* ccArgs = arguments.getBase<MacroEditObject>();
	assert(ccArgs);

	std::vector<CommandInstance*> subInstances;
	subInstances.reserve(subCommands_.size());
	CommandInstancePtr instance;

	for (SubCommandCollection::size_type i = 0; i < subCommands_.size(); ++i)
	{
		ccArgs->resolveDependecy(i, subInstances);
		instance = cmdSysProvider->queueCommand(subCommands_[i].first.c_str(), ccArgs->getCommandArgument(i));
		assert(instance != nullptr);
		cmdSysProvider->waitForInstance(instance);
		subInstances.push_back(instance.get());
	}

	return instance->getReturnValue();
}
TEST_F( TestCommandFixture, runBatchCommand )
{
	auto & controller = getReflectionController();

	auto objHandle = klass_->createManagedObject();

	const int TEST_VALUE = 57;
	const char * TEST_TEXT = "HelloCommand";

	PropertyAccessor counter = klass_->bindProperty("counter", objHandle );
	CHECK(counter.isValid());
	PropertyAccessor text = klass_->bindProperty( "text", objHandle );
	CHECK(text.isValid());
	PropertyAccessor incrementCounter = klass_->bindProperty("incrementCounter", objHandle );
	CHECK(incrementCounter.isValid());

	{
		int value = 0;
		Variant variantValue = controller.getValue( counter );
		CHECK( variantValue.tryCast( value ) );
		CHECK( TEST_VALUE != value );

		std::string text_value;
		Variant variantText = controller.getValue( text );
		CHECK( variantText.tryCast( text_value ) );
		CHECK( TEST_TEXT != text_value );
	}

	{
		int value = TEST_VALUE;
		auto & commandSystemProvider = getCommandSystemProvider();
		commandSystemProvider.beginBatchCommand();
		controller.setValue( counter, value );
		std::string text_value = TEST_TEXT;
		controller.setValue( text, text_value );
		commandSystemProvider.abortBatchCommand();
	}

	{
		int value = 0;
		Variant variantValue = controller.getValue( counter );
		CHECK(variantValue.tryCast( value ));
		CHECK(TEST_VALUE != value);

		std::string text_value;
		Variant variantText = controller.getValue( text );
		CHECK(variantText.tryCast( text_value ));
		CHECK(TEST_TEXT != text_value);
	}

	{
		int value = TEST_VALUE;
		auto & commandSystemProvider = getCommandSystemProvider();
		commandSystemProvider.beginBatchCommand();
		controller.setValue( counter, value );
		std::string text_value = TEST_TEXT;
		controller.setValue( text, text_value );
		commandSystemProvider.endBatchCommand();
	}

	{
		int value = 0;
		Variant variantValue = controller.getValue( counter );
		CHECK(variantValue.tryCast( value ));
		CHECK_EQUAL(TEST_VALUE, value);

		std::string text_value;
		Variant variantText = controller.getValue( text );
		CHECK(variantText.tryCast( text_value ));
		CHECK_EQUAL(TEST_TEXT, text_value);
	}

	{
		Variant variantValue;
		int value = 0;
		auto & commandSystemProvider = getCommandSystemProvider();
		commandSystemProvider.beginBatchCommand();
		controller.setValue( counter, 0 );
		commandSystemProvider.beginBatchCommand();
		controller.setValue( counter, 1 );
		commandSystemProvider.beginBatchCommand();
		controller.setValue( counter, 2 );
		commandSystemProvider.beginBatchCommand();
		controller.setValue( counter, 3 );
		variantValue = controller.getValue( counter );
		CHECK( variantValue.tryCast( value ) );
		CHECK( value == 3 );
		commandSystemProvider.abortBatchCommand();
		variantValue = controller.getValue( counter );
		CHECK( variantValue.tryCast( value ) );
		CHECK( value == 2 );
		commandSystemProvider.abortBatchCommand();
		variantValue = controller.getValue( counter );
		CHECK( variantValue.tryCast( value ) );
		CHECK( value == 1);
		commandSystemProvider.abortBatchCommand();
		variantValue = controller.getValue( counter );
		CHECK( variantValue.tryCast( value ) );
		CHECK( value == 0);
		commandSystemProvider.endBatchCommand();
		variantValue = controller.getValue( counter );
		CHECK( variantValue.tryCast( value ) );
		CHECK( value == 0);
	}

	{
		int value = 0;
		controller.setValue( counter, value );
		Variant variantValue = controller.getValue( counter );
		CHECK(variantValue.tryCast( value ));
		CHECK_EQUAL(0, value);

		auto & commandSystemProvider = getCommandSystemProvider();
		commandSystemProvider.beginBatchCommand();
		controller.invoke( incrementCounter, ReflectedMethodParameters() );
		controller.invoke( incrementCounter, ReflectedMethodParameters() );
		commandSystemProvider.endBatchCommand();
		variantValue = controller.getValue( counter );
		CHECK(variantValue.tryCast( value ));
		CHECK_EQUAL(2, value);

		commandSystemProvider.beginBatchCommand();
		controller.invoke( incrementCounter, ReflectedMethodParameters() );
		controller.invoke( incrementCounter, ReflectedMethodParameters() );
		commandSystemProvider.abortBatchCommand();
		variantValue = controller.getValue( counter );
		CHECK(variantValue.tryCast( value ));
		CHECK_EQUAL(2, value);
	}
}