package org.netxilia.server.js;
import static org.netxilia.server.js.NX.nx;
import static org.stjs.javascript.Global.$array;
import static org.stjs.javascript.Global.$castArray;
import static org.stjs.javascript.Global.$map;
import org.netxilia.server.js.Utils.StartEnd;
import org.netxilia.server.js.data.JsCellReference;
import org.stjs.javascript.Array;
import org.stjs.javascript.Map;
public class CellRange {
private final Sheet sheet;
public Cell start;
public Cell end;
public boolean replicated;
public boolean fullRow;
public boolean fullCol;
public CellRange(Sheet sheet) {
this.sheet = sheet;
this.start = this.end = null;
// true if the range is used for replication
this.replicated = false;
this.fullRow = false;
this.fullCol = false;
}
/**
* @param cell1
* can be a string: C20 or can be an object {col, row}
* @param cell2
* same as cell1
* @param replicated
* - if true only one line or column is selected - as in replication mode start can be null, to move only
* the end part
*/
public void setRange(JsCellReference start, JsCellReference end, boolean replicated, boolean fullRow,
boolean fullCol) {
this.select(false);
this.replicated = replicated;
this.fullRow = fullRow;
this.fullCol = fullCol;
Cell c1 = start != null ? this.sheet.cell(start, null) : this.start;
Cell c2 = end != null ? this.sheet.cell(end, null) : c1;
if (c1 == null || c2 == null) {
return;
}
// start is top-left, end is bottom-right
if (c1.col < c2.col || c1.col == c2.col && c1.row <= c2.row) {
this.start = c1;
this.end = c2;
} else {
this.start = c2;
this.end = c1;
}
if (replicated && this.start != this.end) {
if (this.end.col - this.start.col > this.end.row - this.start.row) {// horizontal
this.end = this.sheet.cell(this.start.row, this.end.col);
} else {// vertical
this.end = this.sheet.cell(this.end.row, this.start.col);
}
}
this.select(true);
}
public void select(boolean sel) {
}
/**
* rebuild the cells array as the underlying table may have changed
*/
public void refresh() {
this.setRange(this.start, this.end, replicated, fullRow, fullCol);
}
public int drow(int r, int dr, int defaultValue) {
Integer tr = this.sheet.rowIndex(r, false);
Integer ret = this.sheet.rowIndex(tr + dr, true);
return ret != null ? ret : defaultValue;
}
public int dcol(int c, int dc, int defaultValue) {
Integer td = this.sheet.colIndex(c, false);
Integer ret = this.sheet.colIndex(td + dc, true);
return ret < 0 || ret >= this.sheet.columnCount() ? defaultValue : ret;
}
public void move(final int dc, final int dr) {
JsCellReference s = new JsCellReference(null, drow(start.row, dr, start.row), dcol(start.col, dc, start.col));
JsCellReference e = null;
if (this.end != this.start) {
e = new JsCellReference(null, drow(end.row, dr, end.row), dcol(end.col, dc, end.col));
}
this.setRange(s, e, replicated, fullRow, fullCol);
}
public Array<CellWithStyle> borders(Map<String, Array<String>> styles) {
// special cases row 0 and col 0
Array<CellWithStyle> updates = $array();
if (styles.$get("h") != null) {
Array<StartEnd> refs = nx.utils.intervals(styles.$get("h"), this.start.col, this.end.col);
for (int r : refs) {
updates.push(new CellWithStyle(this.sheet.areaRef(this.start.row, refs.$get(r).start, this.end.row,
refs.$get(r).end, true), "br"));
}
}
if (styles.$get("v") != null) {
Array<StartEnd> refs = nx.utils.intervals(styles.$get("v"), this.start.row, this.end.row);
for (int r : refs) {
updates.push(new CellWithStyle(this.sheet.areaRef(refs.$get(r).start, this.start.col, refs.$get(r).end,
this.end.col, true), "bb"));
}
}
return updates;
}
public String editableValue() {
String s = "";
for (Integer r = this.start.row; r != null && r <= this.end.row; r = this.drow(r, 1, 0)) {
if (r != this.start.row) {
s += "\n";
}
for (int c = this.start.col; c <= this.end.col; c = this.dcol(c, 1, 0)) {
if (c != this.start.col) {
s += "\t";
}
s += this.sheet.cell(r, c).getValue();
}
}
return s;
}
public String ref(boolean addSheetName) {
return this.sheet.areaRef(this.fullCol ? null : this.start.row, this.fullRow ? null : this.start.col,
this.fullCol ? null : this.end.row, this.fullRow ? null : this.end.col, addSheetName);
}
public String mergeCss(String css1, String css2) {
if (css1 == null) {
return css2;
}
if (css2 == null) {
return css1;
}
Array<String> entries1 = $castArray(css1.split(" "));
Array<String> entries2 = $castArray(css2.split(" "));
Map<String, Boolean> entries = $map();
for (int e : entries1) {
entries.$put(entries1.$get(e), true);
}
for (int e : entries2) {
entries.$put(entries2.$get(e), true);
}
String css = "";
for (String e : entries) {
css += e + " ";
}
return css;
}
/**
*
* @return {css: [all the css classes found], partial: [true if not all the cells share the css]}
*/
public StyleRange css() {
StyleRange ret = new StyleRange();
for (Integer r = this.start.row; r != null && r <= this.end.row; r = this.drow(r, 1, 0)) {
for (int c = this.start.col; c <= this.end.col; c = this.dcol(c, 1, 0)) {
String cellCss = this.sheet.cell(r, c).getCss();
ret.css = this.mergeCss(ret.css, cellCss);
ret.partial = ret.partial || (ret.css != cellCss);
}
}
return ret;
}
public static class StyleRange {
public String css = "";
public boolean partial = false;
}
}