package org.basex.core.cmd;

import static org.basex.core.Text.*;
import static org.basex.data.DataText.*;
import static org.basex.util.Token.*;
import java.io.IOException;
import org.basex.core.CommandBuilder;
import org.basex.core.User;
import org.basex.core.Commands.Cmd;
import org.basex.core.Commands.CmdInfo;
import org.basex.data.Data;
import org.basex.data.Nodes;
import org.basex.util.Table;
import org.basex.util.TokenBuilder;
import org.basex.util.list.TokenList;

/**
 * Evaluates the 'info storage' command and returns the table representation
 * of the currently opened database.
 *
 * @author BaseX Team 2005-11, BSD License
 * @author Christian Gruen
 */
public final class InfoStorage extends AQuery {
  /**
   * Default constructor.
   * @param arg optional arguments
   */
  public InfoStorage(final String... arg) {
    super(DATAREF | User.READ, arg);
  }

  @Override
  protected boolean run() throws IOException {
    // get arguments
    final String start = args.length > 0 ? args[0] : null;
    final String end = args.length > 1 ? args[1] : null;

    // evaluate input as number range or xquery
    if(start != null && toInt(start) == Integer.MIN_VALUE) {
      queryNodes();
      if(result == null) return false;
    }

    final Data data = context.data();
    if(result != null) {
      final Table table = th();
      for(final int n : ((Nodes) result).list) table(table, data, n);
      out.print(table.finish());
      result = null;
    } else {
      int ps = 0;
      int pe = 1000;

      if(start != null) {
        if(end != null) {
          ps = toInt(start);
          pe = toInt(end) + 1;
        } else {
          ps = toInt(start);
          pe = ps + 1;
        }
      }
      out.print(table(data, ps, pe));
    }
    return true;
  }

  /**
   * Prints the specified range of the table.
   * @param data data reference
   * @param s first node to be printed
   * @param e last node to be printed
   * @return table
   */
  public static byte[] table(final Data data, final int s, final int e) {
    final TokenBuilder tb = new TokenBuilder();
    final int ps = Math.max(0, s);
    final int pe = Math.min(data.meta.size, e);
    final Table table = th();
    for(int p = ps; p < pe; ++p) table(table, data, p);
    tb.add(table.finish());

    final byte[] ns = data.ns.table(ps, pe);
    if(ns.length != 0) tb.add(NL).add(ns).add(data.ns.toString(ps, pe)).add(NL);
    return tb.finish();
  }

  /**
   * Writes the header for the 'table' command.
   * @return table
   */
  private static Table th() {
    final Table t = new Table();
    t.header.add(TABLEPRE);
    t.header.add(TABLEDIST);
    t.header.add(TABLESIZE);
    t.header.add(TABLEATS);
    t.header.add(TABLENS);
    t.header.add(TABLEKND);
    t.header.add(TABLECON);
    for(int i = 0; i < 6; ++i) t.align.add(true);
    return t;
  }

  /**
   * Writes the entry for the specified pre value to the table.
   * @param t table reference
   * @param data data reference
   * @param p node to be printed
   */
  private static void table(final Table t, final Data data, final int p) {
    final int k = data.kind(p);
    final TokenList tl = new TokenList();
    tl.add(p);
    tl.add(p - data.parent(p, k));
    tl.add(data.size(p, k));
    tl.add(data.attSize(p, k));
    final int u = data.uri(p, k);
    if(data.nsFlag(p)) tl.add("+" + u);
    else tl.add(u);
    tl.add(TABLEKINDS[k]);

    byte[] cont = null;
    if(k == Data.ELEM) {
      cont = data.name(p, k);
    } else if(k == Data.ATTR) {
      cont = new TokenBuilder(data.name(p, k)).add(ATT1).add(
          data.text(p, false)).add(ATT2).finish();
    } else {
      cont = data.text(p, true);
    }
    tl.add(replace(chop(cont, 64), '\n', ' '));
    t.contents.add(tl);
  }

  @Override
  public void build(final CommandBuilder cb) {
    cb.init(Cmd.INFO + " " + CmdInfo.STORAGE);
    if(args.length > 0 && args[0] != null && toInt(args[0]) ==
      Integer.MIN_VALUE) {
      cb.xquery(0);
    } else {
      cb.arg(0).arg(1);
    }
  }
}
