package com.numix.calculator.view;
import java.util.regex.Pattern;
import org.ejml.simple.SimpleMatrix;
import org.javia.arity.SyntaxException;
import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.widget.EditText;
import android.widget.TableLayout;
import android.widget.TableRow;
import com.numix.calculator.Logic;
import com.numix.calculator.MutableString;
import com.numix.calculator.R;
public class MatrixView extends TableLayout {
private int mRows, mColumns = 0;
private AdvancedDisplay mParent;
private Logic mLogic;
private String mSeparator;
public MatrixView(Context context) {
super(context);
}
public MatrixView(AdvancedDisplay parent) {
super(parent.getContext());
this.mParent = parent;
setup();
}
private void setup() {
mSeparator = getSeparator(getContext());
setBackgroundResource(R.drawable.matrix_background);
setFocusable(true);
mLogic = mParent.mLogic;
}
public static String getPattern(Context context) {
String separator = getSeparator(context);
return "[[" + separator + "][" + separator + "]]";
}
private static String getSeparator(Context context) {
return context.getString(R.string.matrix_separator);
}
private static String getDecimal(Context context) {
return context.getString(R.string.dot);
}
public void addRow() {
mRows++;
TableRow tr = new TableRow(getContext());
tr.setLayoutParams(new MatrixView.LayoutParams(MatrixView.LayoutParams.WRAP_CONTENT, MatrixView.LayoutParams.WRAP_CONTENT, 1));
addView(tr);
for(int i = 0; i < mColumns; i++) {
tr.addView(createEditText());
}
}
public void removeRow() {
mRows--;
removeViewAt(getChildCount() - 1);
if(mRows == 0 || mColumns == 0) mParent.removeView(this);
}
public void addColumn() {
mColumns++;
for(int i = 0; i < mRows; i++) {
TableRow tr = (TableRow) getChildAt(i);
tr.addView(createEditText());
}
}
public void removeColumn() {
mColumns--;
for(int i = 0; i < mRows; i++) {
TableRow tr = (TableRow) getChildAt(i);
tr.removeViewAt(tr.getChildCount() - 1);
}
if(mRows == 0 || mColumns == 0) mParent.removeView(this);
}
public SimpleMatrix getSimpleMatrix() throws SyntaxException {
SimpleMatrix sm = new SimpleMatrix(getData());
return sm;
}
private double[][] getData() throws SyntaxException {
double[][] data = new double[mRows][mColumns];
for(int row = 0; row < mRows; row++) {
TableRow tr = (TableRow) getChildAt(row);
for(int column = 0; column < mColumns; column++) {
String input = ((EditText) tr.getChildAt(column)).getText().toString();
if(input.isEmpty()) throw new SyntaxException();
try {
data[row][column] = Double.valueOf(stringify(mLogic.evaluate(input)));
}
catch(Exception e) {
e.printStackTrace();
data[row][column] = Double.NaN;
}
}
}
return data;
}
private String[][] getDataAsString() {
String[][] data = new String[mRows][mColumns];
for(int row = 0; row < mRows; row++) {
TableRow tr = (TableRow) getChildAt(row);
for(int column = 0; column < mColumns; column++) {
String input = ((EditText) tr.getChildAt(column)).getText().toString();
data[row][column] = input;
}
}
return data;
}
private String stringify(String input) {
if(input.isEmpty()) return "";
else {
input = mLogic.convertToDecimal(input);
if(input.charAt(0) == '\u2212') {
if(input.length() == 1) input = "";
else input = "-" + input.substring(1);
}
if(input.startsWith(".")) {
input = "0" + input;
}
else if(input.startsWith("-.")) {
input = "-0" + input.substring(1);
}
return input;
}
}
boolean isEmpty() {
boolean empty = true;
for(int row = 0; row < mRows; row++) {
TableRow tr = (TableRow) getChildAt(row);
for(int column = 0; column < mColumns; column++) {
String input = ((EditText) tr.getChildAt(column)).getText().toString();
if(!input.isEmpty()) empty = false;
}
}
return empty;
}
private EditText createEditText() {
final EditText et = new MatrixEditText(mParent, this);
et.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT, TableRow.LayoutParams.WRAP_CONTENT, 1));
return et;
}
View nextView(View currentView) {
boolean foundCurrentView = false;
for(int row = 0; row < mRows; row++) {
TableRow tr = (TableRow) getChildAt(row);
for(int column = 0; column < mColumns; column++) {
if(foundCurrentView) return tr.getChildAt(column);
else if(currentView == tr.getChildAt(column)) foundCurrentView = true;
}
}
return mParent.getChildAt(mParent.getChildIndex(this) + 1);
}
View previousView(View currentView) {
boolean foundCurrentView = false;
for(int row = mRows - 1; row >= 0; row--) {
TableRow tr = (TableRow) getChildAt(row);
for(int column = mColumns - 1; column >= 0; column--) {
if(foundCurrentView) return tr.getChildAt(column);
else if(currentView == tr.getChildAt(column)) foundCurrentView = true;
}
}
return mParent.getChildAt(mParent.getChildIndex(this) - 1);
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
for(int row = 0; row < mRows; row++) {
TableRow tr = (TableRow) getChildAt(row);
for(int column = 0; column < mColumns; column++) {
tr.getChildAt(column).setEnabled(enabled);
}
}
}
@Override
public String toString() {
String input = "[";
String[][] data = getDataAsString();
for(int i = 0; i < mRows; i++) {
input += "[";
for(int j = 0; j < mColumns; j++) {
input += data[i][j] + mSeparator;
}
// Remove trailing ,
input = input.substring(0, input.length() - 1);
input += "]";
}
input += "]";
return input;
}
public static String matrixToString(SimpleMatrix matrix, Logic logic) throws SyntaxException {
int rows = matrix.numRows();
int columns = matrix.numCols();
String input = "[";
for(int i = 0; i < rows; i++) {
input += "[";
for(int j = 0; j < columns; j++) {
input += strip(Double.toString(matrix.get(i, j))) + ",";
}
// Remove trailing ,
input = input.substring(0, input.length() - 1);
input += "]";
}
input += "]";
return input;
}
private static String strip(String input) {
if(input.endsWith(".0")) return input.substring(0, input.length() - 2);
return input;
}
public static boolean load(final MutableString text, final AdvancedDisplay parent) {
boolean changed = MatrixView.load(text, parent, parent.getChildCount());
if(changed) {
// Always append a trailing EditText
CalculatorEditText.load(parent);
}
return changed;
}
public static boolean load(final MutableString text, final AdvancedDisplay parent, final int pos) {
if(!MatrixView.verify(parent.getContext(), text)) return false;
String matrix = MatrixView.parseMatrix(text.getText());
text.setText(text.substring(matrix.length()));
int rows = MatrixView.countOccurrences(matrix, '[') - 1;
int columns = MatrixView.countOccurrences(matrix, getSeparator(parent.getContext()).charAt(0)) / rows + 1;
MatrixView mv = new MatrixView(parent);
for(int i = 0; i < rows; i++) {
mv.addRow();
}
for(int i = 0; i < columns; i++) {
mv.addColumn();
}
String[] data = matrix.split(Pattern.quote(getSeparator(parent.getContext())) + "|\\]\\[");
for(int order = 0, row = 0; row < rows; row++) {
TableRow tr = (TableRow) mv.getChildAt(row);
for(int column = 0; column < columns; column++) {
EditText input = (EditText) tr.getChildAt(column);
input.setText(data[order].replaceAll("[\\[\\]]", ""));
order++;
}
}
AdvancedDisplay.LayoutParams params = new AdvancedDisplay.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.CENTER_VERTICAL;
mv.setLayoutParams(params);
parent.addView(mv, pos);
return true;
}
private static boolean verify(Context context, MutableString text) {
String separator = getSeparator(context);
String decimal = getDecimal(context);
String validMatrix = "\\[(\\[[\u2212-]?[A-F0-9]*(" + Pattern.quote(decimal) + "[A-F0-9]*)?(" + Pattern.quote(separator) + "[\u2212-]?[A-F0-9]*("
+ Pattern.quote(decimal) + "[A-F0-9]*)?)*\\])+\\].*";
return text.getText().matches(validMatrix);
}
private static int countOccurrences(String haystack, char needle) {
int count = 0;
for(int i = 0; i < haystack.length(); i++) {
if(haystack.charAt(i) == needle) {
count++;
}
}
return count;
}
private static String parseMatrix(String text) {
int bracket_open = 0;
int bracket_closed = 0;
for(int i = 0; i < text.length(); i++) {
if(text.charAt(i) == '[') {
bracket_open++;
}
else if(text.charAt(i) == ']') {
bracket_closed++;
}
if(bracket_open == bracket_closed) return text.substring(0, i + 1);
}
return "";
}
}