/*
 * Copyright 2012, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */
 
 
#include "SharedSolver.h"
 
#include <set>
 
#include <ALMLayout.h>
#include <AutoDeleter.h>
#include <ObjectList.h>
 
#include "RowColumnManager.h"
 
 
using LinearProgramming::LinearSpec;
 
 
namespace {
 
type_code kConstraintsTypeCode = 'cnst';
const char* kConstraintsField = "SharedSolver:constraints";
 
};
 
 
struct SharedSolver::MinSizeValidator {
	inline void CallSolverMethod(LinearSpec* spec, VariableList* vars)
	{
		spec->FindMins(vars);
	}
 
	inline void Finalize(BALMLayout* layout, SharedSolver* solver,
		ResultType solveResult)
	{
		if (solveResult == LinearProgramming::kUnbounded) {
			solver->SetMinSize(layout, BSize(0, 0));
		} else {
			solver->SetMinSize(layout, BSize(ceilf(layout->Right()->Value()),
				ceilf(layout->Bottom()->Value())));
		}
	}
};
 
 
struct SharedSolver::MaxSizeValidator {
	inline void CallSolverMethod(LinearSpec* spec, VariableList* vars)
	{
		spec->FindMaxs(vars);
	}
 
	inline void Finalize(BALMLayout* layout, SharedSolver* solver,
		ResultType solveResult)
	{
		if (solveResult == LinearProgramming::kUnbounded) {
			solver->SetMaxSize(layout,
				BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
		} else {
			solver->SetMaxSize(layout, BSize(floorf(layout->Right()->Value()),
				floorf(layout->Bottom()->Value())));
		}
	}
};
 
 
struct SharedSolver::PreferredSizeValidator {
	inline void CallSolverMethod(LinearSpec* spec, VariableList* vars)
	{
		spec->Solve();
	}
 
	inline void Finalize(BALMLayout* layout, SharedSolver* solver,
		ResultType solveResult)
	{
		float width = layout->Right()->Value() - layout->Left()->Value();
		float height = layout->Bottom()->Value() - layout->Top()->Value();
		solver->SetPreferredSize(layout, BSize(width, height));
	}
};
 
 
SharedSolver::SharedSolver()
	:
	fConstraintsValid(false),
	fMinValid(false),
	fMaxValid(false),
	fPreferredValid(false),
	fLayoutValid(false),
	fLayoutContext(NULL)
{
}
 
 
SharedSolver::SharedSolver(BMessage* archive)
	:
	BArchivable(archive),
	fConstraintsValid(false),
	fMinValid(false),
	fMaxValid(false),
	fPreferredValid(false),
	fLayoutValid(false),
	fLayoutContext(NULL)
{
}
 
 
SharedSolver::~SharedSolver()
{
	if (fLayouts.CountItems() > 0)
		debugger("SharedSolver deleted while still in use!");
 
	_SetContext(NULL);
}
 
 
LinearSpec*
SharedSolver::Solver() const
{
	return const_cast<LinearSpec*>(&fLinearSpec);
}
 
 
void
SharedSolver::Invalidate(bool children)
{
	if (!fConstraintsValid && !fMaxValid && !fMinValid && !fLayoutValid)
		return;
 
	fConstraintsValid = false;
	fMinValid = false;
	fMaxValid = false;
	fPreferredValid = false;
	fLayoutValid = false;
 
	_SetContext(NULL);
 
	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--)
		fLayouts.ItemAt(i)->InvalidateLayout(children);
}
 
 
void
SharedSolver::RegisterLayout(BALMLayout* layout)
{
	fLayouts.AddItem(layout);
	Invalidate(false);
}
 
 
void
SharedSolver::LayoutLeaving(const BALMLayout* layout)
{
	fLayouts.RemoveItem(const_cast<BALMLayout*>(layout));
	Invalidate(false);
}
 
 
ResultType
SharedSolver::ValidateMinSize()
{
	_Validate<MinSizeValidator>(fMinValid, fMinResult);
	return fMinResult;
}
 
 
ResultType
SharedSolver::ValidateMaxSize()
{
	_Validate<MaxSizeValidator>(fMaxValid, fMaxResult);
	return fMaxResult;
}
 
 
ResultType
SharedSolver::ValidatePreferredSize()
{
	_Validate<PreferredSizeValidator>(fPreferredValid, fPreferredResult);
	return fPreferredResult;
}
 
 
ResultType
SharedSolver::ValidateLayout(BLayoutContext* context)
{
	if (fLayoutValid && fLayoutContext == context)
		return fLayoutResult;
 
	_SetContext(context);
	_ValidateConstraints();
 
	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
		BALMLayout* layout = fLayouts.ItemAt(i);
		BSize size(layout->LayoutArea().Size());
		layout->Right()->SetRange(size.width, size.width);
		layout->Bottom()->SetRange(size.height, size.height);
	}
 
	fLayoutResult = fLinearSpec.Solve();
	fLayoutValid = true;
	return fLayoutResult;
}
 
 
status_t
SharedSolver::Archive(BMessage* archive, bool deep) const
{
	return BArchivable::Archive(archive, deep);
}
 
 
status_t
SharedSolver::AllArchived(BMessage* archive) const
{
	/*
	for each archived layout:
		add it to our archive
		add constraints created by areas and column manager to a set
		add range constraints on the left/top/right/bottom tabs to the same set
	for each constraint in the linear spec:
		if it is not in the set above:
			archive it
	*/
	BArchiver archiver(archive);
	std::set<const Constraint*> autoConstraints;
	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
		BALMLayout* layout = fLayouts.ItemAt(i);
		if (!archiver.IsArchived(layout))
			continue;
 
		for (int32 j = layout->CountItems() - 1; j >= 0; j--)
			_AddConstraintsToSet(layout->AreaAt(j), autoConstraints);
 
		for (int32 j = layout->fRowColumnManager->fRows.CountItems() - 1;
				j >= 0; j--) {
			Row* row = layout->fRowColumnManager->fRows.ItemAt(j);
			autoConstraints.insert(row->fPrefSizeConstraint);
		}
 
		for (int32 j = layout->fRowColumnManager->fColumns.CountItems() - 1;
				j >= 0; j--) {
			Column* column = layout->fRowColumnManager->fColumns.ItemAt(j);
			autoConstraints.insert(column->fPrefSizeConstraint);
		}
 
		Variable* corners[] = {layout->fLeft, layout->fTop, layout->fRight,
			layout->fBottom};
 
		for (int32 j = 0; j < 4; j++) {
			const Constraint* min;
			const Constraint* max;
			fLinearSpec.GetRangeConstraints(corners[j], &min, &max);
			if (min)
				autoConstraints.insert(min);
			if (max)
				autoConstraints.insert(max);
		}
	}
 
	status_t err = B_OK;
	const ConstraintList& constraints = fLinearSpec.Constraints();
	for (int32 i = constraints.CountItems() - 1; i >= 0 && err == B_OK; i--) {
		Constraint* constraint = constraints.ItemAt(i);
		if (autoConstraints.find(constraint) == autoConstraints.end())
			err = _AddConstraintToArchive(constraint, archive);
	}
	return err;
}
 
 
status_t
SharedSolver::AllUnarchived(const BMessage* archive)
{
	int32 count = 0;
	archive->GetInfo(kConstraintsField, NULL, &count);
 
	status_t err = B_OK;
	BUnarchiver unarchiver(archive);
	for (int32 i = 0; i < count && err == B_OK; i++) {
		const void* constraintData;
		ssize_t numBytes;
		err = archive->FindData(kConstraintsField, kConstraintsTypeCode,
			i, &constraintData, &numBytes);
		if (err != B_OK)
			return err;
 
		err = _InstantiateConstraint(constraintData, numBytes, unarchiver);
	}
	return err;
}
 
 
void
SharedSolver::_AddConstraintsToSet(Area* area,
	std::set<const Constraint*>& constraints)
{
	if (area->fMinContentWidth)
		constraints.insert(area->fMinContentWidth);
	if (area->fMaxContentWidth)
		constraints.insert(area->fMaxContentWidth);
	if (area->fMinContentHeight)
		constraints.insert(area->fMinContentHeight);
	if (area->fMaxContentHeight)
		constraints.insert(area->fMaxContentHeight);
	if (area->fContentAspectRatioC)
		constraints.insert(area->fContentAspectRatioC);
}
 
 
status_t
SharedSolver::_AddConstraintToArchive(Constraint* constraint,
	BMessage* archive)
{
	/* Format:
	 * int32: summandCount
	 *  { int32 : token
	 *    double: coefficient
	 *  } [summandCount]	
	 *  int32: operator
	 *  double: rightSide
	 *  double: penaltyNeg
	 *  double: penaltyPos
	 */
 
	// TODO: check Read/Write calls
	BArchiver archiver(archive);
	BMallocIO buffer;
	SummandList* leftSide = constraint->LeftSide();
	int32 summandCount = leftSide->CountItems();
	buffer.Write(&summandCount, sizeof(summandCount));
 
	for (int32 i = 0; i < summandCount; i++) {
		Summand* summand = leftSide->ItemAt(i);
		Variable* var = summand->Var();
		BArchivable* varArchivable = dynamic_cast<BArchivable*>(var);
		if (!varArchivable || !archiver.IsArchived(varArchivable))
			return B_NAME_NOT_FOUND;
 
		int32 token;
		archiver.GetTokenForArchivable(varArchivable, token);
		buffer.Write(&token, sizeof(token));
 
		double coefficient = summand->Coeff();
		buffer.Write(&coefficient, sizeof(coefficient));
	}
 
	int32 op = constraint->Op();
	buffer.Write(&op, sizeof(op));
 
	double rightSide = constraint->RightSide();
	buffer.Write(&rightSide, sizeof(rightSide));
	double penaltyNeg = constraint->PenaltyNeg();
	buffer.Write(&penaltyNeg, sizeof(penaltyNeg));
	double penaltyPos = constraint->PenaltyPos();
	buffer.Write(&penaltyPos, sizeof(penaltyPos));
 
	return archive->AddData(kConstraintsField, kConstraintsTypeCode,
		buffer.Buffer(), buffer.BufferLength(), false);
}
 
 
status_t
SharedSolver::_InstantiateConstraint(const void* rawData, ssize_t numBytes,
	BUnarchiver& unarchiver)
{
	/* Format:
	 * int32: summandCount
	 *  { int32 : token
	 *    double: coefficient
	 *  } [summandCount]	
	 *  int32: operator
	 *  double: rightSide
	 *  double: penaltyNeg
	 *  double: penaltyPos
	 */
 
	// TODO: check Read/Write calls
	BMemoryIO buffer(rawData, numBytes);
	int32 summandCount;
	buffer.Read((void*)&summandCount, sizeof(summandCount));
 
	SummandList* summandList = new SummandList(20, true);
	ObjectDeleter<SummandList> deleter(summandList);
	status_t err = B_OK;
	for (int32 i = 0; i < summandCount; i++) {
		int32 token;
		buffer.Read((void*)&token, sizeof(token));
 
		BArchivable* varArchivable;
		err = unarchiver.GetObject(token, varArchivable);
		if (err != B_OK)
			break;
		Variable* var = dynamic_cast<Variable*>(varArchivable);
		if (!var)
			return B_BAD_TYPE;
 
		fLinearSpec.AddVariable(var);
 
		double coefficient;
		buffer.Read(&coefficient, sizeof(coefficient));
 
		summandList->AddItem(new Summand(coefficient, var));
	}
 
	int32 op;
	buffer.Read(&op, sizeof(op));
 
	double rightSide;
	buffer.Read(&rightSide, sizeof(rightSide));
	double penaltyNeg;
	buffer.Read(&penaltyNeg, sizeof(penaltyNeg));
	double penaltyPos;
	buffer.Read(&penaltyPos, sizeof(penaltyPos));
 
	if (err != B_OK)
		return err;
 
	if (fLinearSpec.AddConstraint(summandList, (OperatorType)op, rightSide,
			penaltyNeg, penaltyPos) != NULL) {
		deleter.Detach();
		return B_OK;
	}
 
	return B_NO_MEMORY;
}
 
 
void
SharedSolver::SetMaxSize(BALMLayout* layout, const BSize& max)
{
	layout->fMaxSize = max;
}
 
 
void
SharedSolver::SetMinSize(BALMLayout* layout, const BSize& min)
{
	layout->fMinSize = min;
}
 
 
void
SharedSolver::SetPreferredSize(BALMLayout* layout, const BSize& preferred)
{
	layout->fPreferredSize = preferred;
}
 
 
void
SharedSolver::LayoutContextLeft(BLayoutContext* context)
{
	fLayoutContext = NULL;
}
 
 
void
SharedSolver::_SetContext(BLayoutContext* context)
{
	if (fLayoutContext)
		fLayoutContext->RemoveListener(this);
	fLayoutContext = context;
	if (fLayoutContext)
		fLayoutContext->AddListener(this);
}
 
 
void
SharedSolver::_ValidateConstraints()
{
	if (!fConstraintsValid) {
		for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
			fLayouts.ItemAt(i)->UpdateConstraints(fLayoutContext);
		}
		fConstraintsValid = true;
	}
}
 
 
template <class Validator>
void
SharedSolver::_Validate(bool& isValid, ResultType& result)
{
	if (isValid)
		return;
 
	_ValidateConstraints();
 
	VariableList variables(fLayouts.CountItems() * 2);
	Validator validator;
 
	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--) {
		BALMLayout* layout = fLayouts.ItemAt(i);
		layout->Right()->SetRange(0, 20000);
		layout->Bottom()->SetRange(0, 20000);
 
		variables.AddItem(layout->Right());
		variables.AddItem(layout->Bottom());
	}
 
	validator.CallSolverMethod(&fLinearSpec, &variables);
	result = fLinearSpec.Result();
 
	for (int32 i = fLayouts.CountItems() - 1; i >= 0; i--)
		validator.Finalize(fLayouts.ItemAt(i), this, result);
 
	isValid = true;
	fLayoutValid = false;
}
 
 
BArchivable*
SharedSolver::Instantiate(BMessage* archive)
{
	if (validate_instantiation(archive, "BPrivate::SharedSolver"))
		return new SharedSolver(archive);
	return NULL;
}

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fMinResult, fMaxResult, fPreferredResult, fLayoutResult.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fMinResult, fMaxResult, fPreferredResult, fLayoutResult.