export default class ColumnManager {
  constructor(columns) {
    this.columns = columns;
    this._cached = {};
  }

  isAnyColumnsFixed() {
    return this._cache('isAnyColumnsFixed', () => this.columns.some(column => !!column.fixed));
  }

  isAnyColumnsLeftFixed() {
    return this._cache('isAnyColumnsLeftFixed', () =>
      this.columns.some(column => column.fixed === 'left' || column.fixed === true),
    );
  }

  isAnyColumnsRightFixed() {
    return this._cache('isAnyColumnsRightFixed', () =>
      this.columns.some(column => column.fixed === 'right'),
    );
  }

  leftColumns() {
    return this._cache('leftColumns', () =>
      this.groupedColumns().filter(column => column.fixed === 'left' || column.fixed === true),
    );
  }

  rightColumns() {
    return this._cache('rightColumns', () =>
      this.groupedColumns().filter(column => column.fixed === 'right'),
    );
  }

  leafColumns() {
    return this._cache('leafColumns', () => this._leafColumns(this.columns));
  }

  leftLeafColumns() {
    return this._cache('leftLeafColumns', () => this._leafColumns(this.leftColumns()));
  }

  rightLeafColumns() {
    return this._cache('rightLeafColumns', () => this._leafColumns(this.rightColumns()));
  }

  // add appropriate rowspan and colspan to column
  groupedColumns() {
    return this._cache('groupedColumns', () => {
      const _groupColumns = (columns, currentRow = 0, parentColumn = {}, rows = []) => {
        // track how many rows we got
        rows[currentRow] = rows[currentRow] || [];
        const grouped = [];
        const setRowSpan = column => {
          const rowSpan = rows.length - currentRow;
          if (
            column &&
            !column.children && // parent columns are supposed to be one row
            rowSpan > 1 &&
            (!column.rowSpan || column.rowSpan < rowSpan)
          ) {
            column.rowSpan = rowSpan;
          }
        };
        columns.forEach((column, index) => {
          const newColumn = { ...column };
          rows[currentRow].push(newColumn);
          parentColumn.colSpan = parentColumn.colSpan || 0;
          if (newColumn.children && newColumn.children.length > 0) {
            newColumn.children = _groupColumns(newColumn.children, currentRow + 1, newColumn, rows);
            parentColumn.colSpan += newColumn.colSpan;
          } else {
            parentColumn.colSpan += 1;
          }
          // update rowspan to all same row columns
          for (let i = 0; i < rows[currentRow].length - 1; i += 1) {
            setRowSpan(rows[currentRow][i]);
          }
          // last column, update rowspan immediately
          if (index + 1 === columns.length) {
            setRowSpan(newColumn);
          }
          grouped.push(newColumn);
        });
        return grouped;
      };
      return _groupColumns(this.columns);
    });
  }

  reset(columns) {
    this.columns = columns;
    this._cached = {};
  }

  _cache(name, fn) {
    if (name in this._cached) {
      return this._cached[name];
    }
    this._cached[name] = fn();
    return this._cached[name];
  }

  _leafColumns(columns) {
    const leafColumns = [];
    columns.forEach(column => {
      if (!column.children) {
        leafColumns.push(column);
      } else {
        leafColumns.push(...this._leafColumns(column.children));
      }
    });
    return leafColumns;
  }
}
