/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.formulaeditor;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.TextView;
import org.catrobat.catroid.CatroidApplication;
import org.catrobat.catroid.ProjectManager;
import org.catrobat.catroid.R;
import org.catrobat.catroid.content.Sprite;
import org.catrobat.catroid.formulaeditor.FormulaElement.ElementType;
import java.io.Serializable;
import java.util.List;
public class Formula implements Serializable {
private static final long serialVersionUID = 1L;
private FormulaElement formulaTree;
private transient Integer formulaTextFieldId = null;
private transient InternFormula internFormula = null;
private transient String displayText = null;
public Object readResolve() {
if (formulaTree == null) {
formulaTree = new FormulaElement(ElementType.NUMBER, "0 ", null);
}
internFormula = new InternFormula(formulaTree.getInternTokenList());
return this;
}
public Formula(FormulaElement formulaElement) {
formulaTree = formulaElement;
internFormula = new InternFormula(formulaTree.getInternTokenList());
}
public Formula(Integer value) {
if (value < 0) {
formulaTree = new FormulaElement(ElementType.OPERATOR, Operators.MINUS.toString(), null);
formulaTree.setRightChild(new FormulaElement(ElementType.NUMBER, Long.toString(Math.abs((long) value)),
formulaTree));
internFormula = new InternFormula(formulaTree.getInternTokenList());
} else {
formulaTree = new FormulaElement(ElementType.NUMBER, value.toString(), null);
internFormula = new InternFormula(formulaTree.getInternTokenList());
}
}
public Formula(Float value) {
this(Double.valueOf(value));
}
public Formula(Double value) {
if (value < 0) {
formulaTree = new FormulaElement(ElementType.OPERATOR, Operators.MINUS.toString(), null);
formulaTree.setRightChild(new FormulaElement(ElementType.NUMBER, Double.toString(Math.abs(value)),
formulaTree));
internFormula = new InternFormula(formulaTree.getInternTokenList());
} else {
formulaTree = new FormulaElement(ElementType.NUMBER, value.toString(), null);
internFormula = new InternFormula(formulaTree.getInternTokenList());
}
}
public void updateVariableReferences(String oldName, String newName, Context context) {
internFormula.updateVariableReferences(oldName, newName, context);
formulaTree.updateVariableReferences(oldName, newName);
displayText = null;
}
public void getVariableAndListNames(List<String> variables, List<String> lists) {
internFormula.getVariableAndListNames(variables, lists);
formulaTree.getVariableAndListNames(variables, lists);
}
public void updateCollisionFormulas(String oldName, String newName, Context context) {
internFormula.updateCollisionFormula(oldName, newName, context);
formulaTree.updateCollisionFormula(oldName, newName);
displayText = null;
}
public void updateCollisionFormulasToVersion(float catroidLanguageVersion) {
internFormula.updateCollisionFormulaToVersion(CatroidApplication.getAppContext(), catroidLanguageVersion);
formulaTree.updateCollisionFormulaToVersion(catroidLanguageVersion);
displayText = null;
}
public boolean containsSpriteInCollision(String name) {
return formulaTree.containsSpriteInCollision(name);
}
public Formula(String value) {
if (value.equalsIgnoreCase(Functions.ARDUINOANALOG.toString())) {
formulaTree = new FormulaElement(ElementType.SENSOR, Functions.ARDUINOANALOG.toString(), null);
} else if (value.equalsIgnoreCase(Functions.ARDUINODIGITAL.toString())) {
formulaTree = new FormulaElement(ElementType.SENSOR, Functions.ARDUINODIGITAL.toString(), null);
} else {
formulaTree = new FormulaElement(ElementType.STRING, value, null);
internFormula = new InternFormula(formulaTree.getInternTokenList());
}
}
public void setDisplayText(String text) {
displayText = text;
}
public Boolean interpretBoolean(Sprite sprite) throws InterpretationException {
int result = interpretDouble(sprite).intValue();
return result != 0;
}
public Integer interpretInteger(Sprite sprite) throws InterpretationException {
Double returnValue = interpretDouble(sprite);
return returnValue.intValue();
}
public Double interpretDouble(Sprite sprite) throws InterpretationException {
try {
Object returnValue = formulaTree.interpretRecursive(sprite);
Double doubleReturnValue;
if (returnValue instanceof String) {
doubleReturnValue = Double.valueOf((String) returnValue);
if (doubleReturnValue.isNaN()) {
throw new InterpretationException("NaN in interpretDouble()");
}
return doubleReturnValue;
} else {
doubleReturnValue = (Double) returnValue;
if (doubleReturnValue.isNaN()) {
throw new InterpretationException("NaN in interpretDouble()");
}
return (Double) returnValue;
}
} catch (ClassCastException classCastException) {
throw new InterpretationException("Couldn't interpret Formula.", classCastException);
} catch (NumberFormatException numberFormatException) {
throw new InterpretationException("Couldn't interpret Formula.", numberFormatException);
}
}
public Float interpretFloat(Sprite sprite) throws InterpretationException {
Double returnValue = interpretDouble(sprite);
return returnValue.floatValue();
}
public String interpretString(Sprite sprite) throws InterpretationException {
Object interpretation = formulaTree.interpretRecursive(sprite);
if (interpretation instanceof Double && ((Double) interpretation).isNaN()) {
throw new InterpretationException("NaN in interpretString()");
}
return String.valueOf(interpretation);
}
public Object interpretObject(Sprite sprite) {
return formulaTree.interpretRecursive(sprite);
}
public void setRoot(FormulaElement formula) {
displayText = null;
formulaTree = formula;
internFormula = new InternFormula(formula.getInternTokenList());
}
public FormulaElement getRoot() {
return formulaTree;
}
public void setTextFieldId(int id) {
formulaTextFieldId = id;
}
public String getDisplayString(Context context) {
if (displayText != null) {
return displayText;
}
if (context != null) {
internFormula.generateExternFormulaStringAndInternExternMapping(context);
}
return internFormula.getExternFormulaString();
}
public void refreshTextField(View view) {
refreshTextField(view, getTrimmedFormulaString(view.getContext()));
}
public void refreshTextField(View view, String formulaString) {
if (formulaTextFieldId != null && formulaTree != null && view != null) {
TextView formulaTextField = (TextView) view.findViewById(formulaTextFieldId);
if (formulaTextField != null) {
formulaTextField.setText(formulaString);
}
}
}
@SuppressWarnings("deprecation")
public void highlightTextField(View brickView) {
Drawable highlightBackground;
highlightBackground = brickView.getResources().getDrawable(R.drawable.textfield_pressed_android4);
TextView formulaTextField = (TextView) brickView.findViewById(formulaTextFieldId);
if (formulaTextField != null) {
formulaTextField.setBackgroundDrawable(highlightBackground);
}
}
public String getTrimmedFormulaString(Context context) {
return internFormula.trimExternFormulaString(context);
}
public void prepareToRemove() {
formulaTextFieldId = null;
}
public InternFormulaState getInternFormulaState() {
return internFormula.getInternFormulaState();
}
public boolean containsElement(FormulaElement.ElementType elementType) {
return formulaTree.containsElement(elementType);
}
public boolean isSingleNumberFormula() {
return formulaTree.isSingleNumberFormula();
}
@Override
public Formula clone() {
if (formulaTree != null) {
return new Formula(formulaTree.clone());
}
return new Formula(0);
}
public void removeVariableReferences(String name, Context context) {
internFormula.removeVariableReferences(name, context);
}
public int getRequiredResources() {
return formulaTree.getRequiredResources();
}
public String getResultForComputeDialog(Context context) {
Sprite sprite = ProjectManager.getInstance().getCurrentSprite();
ElementType type = formulaTree.getElementType();
if (formulaTree.isLogicalOperator()) {
boolean result;
try {
result = this.interpretBoolean(sprite);
} catch (InterpretationException interpretationException) {
return "ERROR";
}
int logicalFormulaResultIdentifier = result ? R.string.formula_editor_true : R.string.formula_editor_false;
return context.getString(logicalFormulaResultIdentifier);
} else if (type == ElementType.STRING
|| type == ElementType.SENSOR
|| (type == ElementType.FUNCTION
&& (Functions.getFunctionByValue(formulaTree.getValue()) == Functions.LETTER
|| Functions.getFunctionByValue(formulaTree.getValue()) == Functions.JOIN))) {
try {
return interpretString(sprite);
} catch (InterpretationException interpretationException) {
return "ERROR";
}
} else if (formulaTree.isUserVariableWithTypeString(sprite)) {
DataContainer userVariables = ProjectManager.getInstance().getSceneToPlay().getDataContainer();
UserVariable userVariable = userVariables.getUserVariable(formulaTree.getValue(), sprite);
return (String) userVariable.getValue();
} else {
Double interpretationResult;
try {
interpretationResult = this.interpretDouble(sprite);
} catch (InterpretationException interpretationException) {
return "ERROR";
}
return String.valueOf(interpretationResult);
}
}
public FormulaElement getFormulaTree() {
return formulaTree;
}
}