/*
 * File    : Arith.java
 * Created : 06-may-2001 18:28
 * By      : fbusquets
 *
 * JClic - Authoring and playing system for educational activities
 *
 * Copyright (C) 2000 - 2018 Francesc Busquets & Departament
 * d'Educacio de la Generalitat de Catalunya
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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 General Public License for more details (see the LICENSE file).
 */

package edu.xtec.jclic.automation.arith;

import edu.xtec.jclic.automation.ActiveBagContentKit;
import edu.xtec.jclic.automation.AutoContentProvider;
import edu.xtec.jclic.boxes.ActiveBagContent;
import edu.xtec.util.JDomUtility;
import edu.xtec.util.ResourceBridge;
import java.text.DecimalFormat;
import java.util.Iterator;
import java.util.Random;

/**
 * <CODE>Arith</CODE> is the first implementation of
 * {@link edu.xtec.jclic.automation.AutoContentProvider}. It is based on the
 * code of ARITH2.DLL, made originally for Clic 3.0. It provides activities with
 * randomly generated menthal arithmetics operations. The operations can be
 * additions, substractions, multiplications or divides. The unknown can be the
 * result of the operation or any of the two operators (in the form A # B = ?, A
 * # ? = C or ? # B = C), or also the operation itself (like A ? B = C).
 * Activities must implement
 * {@link edu.xtec.jclic.automation.ActiveBagContentKit.Compatible} in order to
 * use <CODE>Arith
 * </CODE>.
 *
 * @author Francesc Busquets (fbusquets@xtec.cat)
 * @version 13.08.28
 */
public class Arith extends AutoContentProvider {

  private static final String DLL_TITLE = "ARITH2.DLL";
  private static final int ARITHVER = 0x02;

  protected static final int NMAXLOOPS = 60;
  protected static final int NOSORT = 0, SORTASC = 1, SORTDESC = 2;
  protected static final int SUM = 1, REST = 2, MULT = 4, DIV = 8, NOPERACIONS = 4;

  // Math signs:
  // \u00D7 - multiply (cross)
  // \u00B7 - multiply (middle dot)
  // \u00F7 - division (two points+bar)
  protected static final String[] OPSTR = { "+", "-", "\u00D7", ":" };
  protected static final int ABX = 1, AXC = 2, XBC = 4, AXBC = 8, CAXB = 16, NTIPUSEX = 5;
  protected static final int INDIF = 0, AGB = 1, BGA = 2;

  private static final int RES = -12345;
  private static final int MAX_STR_LEN = 100;

  private static String S = "\u00A0";

  protected class Num {
    float vf;
    int c;
  }

  protected class Operacio {
    Num numA = new Num();
    Num numB = new Num();
    Num numR = new Num();
    int op;
  }

  protected static final String ID = "id", A = "A", B = "B", OPERATIONS = "operations", PLUS = "plus", MINUS = "minus",
      MULTIPLY = "multiply", DIVIDE = "divide", UNKNOWN = "unknown", RESULT = "result", FIRST = "first", LAST = "last",
      OPERAND = "operand", INVERSE = "inverse", FROM = "from", TO = "to", NOT_CARRY = "notCarry",
      DUPLICATES = "duplicates", ORDER = "order", ASCENDING = "ascending", DESCENDING = "descending",
      CONDITION = "condition", FIRST_BIG = "firstBig", LAST_BIG = "lastBig";

  protected static DecimalFormat[] DF;
  protected static final int WILDCARD_DF = 5;

  Operator opA, opB;
  boolean use_add, use_subst, use_mult, use_div;
  boolean exp_abx, exp_axc, exp_xbc, exp_axbc, exp_caxb;
  int resultLimInf, resultLimSup;
  boolean resultCarry, resultNoDup;
  int resultOrder;
  int opCond;

  private Random random;

  /** Creates new Arith */
  public Arith() {
    random = new Random();
    opA = new Operator();
    opB = new Operator();
    exp_abx = true;
    use_add = true;
    resultLimInf = Operator.LIM0;
    resultLimSup = Operator.NOLIM;
    resultOrder = NOSORT;
    opCond = INDIF;
    if (DF == null) {
      DF = new DecimalFormat[WILDCARD_DF + 1];
      DF[0] = new DecimalFormat("0");
      DF[1] = new DecimalFormat("0.0");
      DF[2] = new DecimalFormat("0.00");
      DF[3] = new DecimalFormat("0.000");
      DF[4] = new DecimalFormat("0.0000");
      DF[WILDCARD_DF] = new DecimalFormat("0000000000");
    }
  }

  public DecimalFormat getDF(int index) {
    return DF[index <= WILDCARD_DF ? index : WILDCARD_DF - 1];
  }

  @Override
  public org.jdom.Element getJDomElement() {

    org.jdom.Element e = super.getJDomElement();
    org.jdom.Element op = opA.getJDomElement();
    op.setAttribute(ID, A);
    e.addContent(op);
    op = opB.getJDomElement();
    op.setAttribute(ID, B);
    e.addContent(op);

    org.jdom.Element eop = new org.jdom.Element(OPERATIONS);
    eop.setAttribute(PLUS, JDomUtility.boolString(use_add));
    eop.setAttribute(MINUS, JDomUtility.boolString(use_subst));
    eop.setAttribute(MULTIPLY, JDomUtility.boolString(use_mult));
    eop.setAttribute(DIVIDE, JDomUtility.boolString(use_div));
    e.addContent(eop);

    org.jdom.Element eu = new org.jdom.Element(UNKNOWN);
    eu.setAttribute(RESULT, JDomUtility.boolString(exp_abx));
    eu.setAttribute(FIRST, JDomUtility.boolString(exp_xbc));
    eu.setAttribute(LAST, JDomUtility.boolString(exp_axc));
    eu.setAttribute(OPERAND, JDomUtility.boolString(exp_axbc));
    eu.setAttribute(INVERSE, JDomUtility.boolString(exp_caxb));
    e.addContent(eu);

    org.jdom.Element er = new org.jdom.Element(RESULT);
    er.setAttribute(FROM, Operator.LIM_CH[resultLimInf]);
    er.setAttribute(TO, Operator.LIM_CH[resultLimSup]);
    if (resultCarry)
      er.setAttribute(NOT_CARRY, JDomUtility.boolString(resultCarry));
    er.setAttribute(DUPLICATES, JDomUtility.boolString(!resultNoDup));
    if (resultOrder != NOSORT)
      er.setAttribute(ORDER, resultOrder == SORTASC ? ASCENDING : DESCENDING);
    if (opCond != INDIF)
      er.setAttribute(CONDITION, opCond == AGB ? FIRST_BIG : LAST_BIG);
    e.addContent(er);

    return e;
  }

  public void setProperties(org.jdom.Element e, Object aux) throws Exception {

    org.jdom.Element child;
    String s;
    Iterator itr = e.getChildren(Operator.ELEMENT_NAME).iterator();
    while (itr.hasNext()) {
      child = (org.jdom.Element) (itr.next());
      s = child.getAttributeValue(ID);
      if (A.equals(s))
        opA.setProperties(child, aux);
      else if (B.equals(s))
        opB.setProperties(child, aux);
      else
        throw new IllegalArgumentException("Unknown operator: " + s);
    }
    if ((child = e.getChild(OPERATIONS)) != null) {
      use_add = JDomUtility.getBoolAttr(child, PLUS, use_add);
      use_subst = JDomUtility.getBoolAttr(child, MINUS, use_subst);
      use_mult = JDomUtility.getBoolAttr(child, MULTIPLY, use_mult);
      use_div = JDomUtility.getBoolAttr(child, DIVIDE, use_div);
    }
    if ((child = e.getChild(UNKNOWN)) != null) {
      exp_abx = JDomUtility.getBoolAttr(child, RESULT, exp_abx);
      exp_xbc = JDomUtility.getBoolAttr(child, FIRST, exp_xbc);
      exp_axc = JDomUtility.getBoolAttr(child, LAST, exp_axc);
      exp_axbc = JDomUtility.getBoolAttr(child, OPERAND, exp_axbc);
      exp_caxb = JDomUtility.getBoolAttr(child, INVERSE, exp_caxb);
    }
    if ((child = e.getChild(RESULT)) != null) {
      resultLimInf = JDomUtility.getStrIndexAttr(child, FROM, Operator.LIM_CH, resultLimInf);
      resultLimSup = JDomUtility.getStrIndexAttr(child, TO, Operator.LIM_CH, resultLimSup);
      resultCarry = JDomUtility.getBoolAttr(child, NOT_CARRY, resultCarry);
      resultNoDup = !JDomUtility.getBoolAttr(child, DUPLICATES, !resultNoDup);
      s = child.getAttributeValue(ORDER);
      resultOrder = (s == null ? NOSORT : s.equals(ASCENDING) ? SORTASC : SORTDESC);
      s = child.getAttributeValue(CONDITION);
      opCond = (s == null ? INDIF : s.equals(FIRST_BIG) ? AGB : BGA);
    }
  }

  @Override
  public boolean setClic3Properties(byte[] ops) {
    int v;
    int i, lb, hb;
    boolean fromBlank = false;
    int p = 0;
    int arithVer;

    p = opA.setClic3Properties(ops, p);

    p = opB.setClic3Properties(ops, p);

    v = ops[p++] & 0x7F;
    if (v == 0)
      v = SUM;
    use_add = (v & SUM) != 0;
    use_subst = (v & REST) != 0;
    use_mult = (v & MULT) != 0;
    use_div = (v & DIV) != 0;

    v = ops[p++] & 0x7F;
    if (v == 0)
      v = ABX;
    exp_abx = (v & ABX) != 0;
    exp_axc = (v & AXC) != 0;
    exp_xbc = (v & XBC) != 0;
    exp_axbc = (v & AXBC) != 0;
    exp_caxb = (v & CAXB) != 0;

    resultLimInf = ((i = ops[p++] & 0x7F) == 0 ? Operator.LIM0 : i);
    resultLimSup = ((i = ops[p++] & 0x7F) == 0 ? Operator.NOLIM : i);
    resultCarry = (ops[p++] & 0x1) == 1;
    resultNoDup = (ops[p++] & 0x1) == 1;
    resultOrder = ops[p++] & 0x3;
    opCond = ops[p++] & 0x3;

    if (p < ops.length)
      arithVer = ops[p++] & 0x7F;
    else
      arithVer = 0;

    if (arithVer == 0) {
      arithVer = ARITHVER;
      if (!opA.fromBlank) {
        opA.limInf = Operator.adjustLimVer(opA.limInf);
        opA.limSup = Operator.adjustLimVer(opA.limSup);
        opB.limInf = Operator.adjustLimVer(opB.limInf);
        opB.limSup = Operator.adjustLimVer(opB.limSup);
        resultLimInf = Operator.adjustLimVer(resultLimInf);
        resultLimSup = Operator.adjustLimVer(resultLimSup);
      }
    }

    if (arithVer > ARITHVER) {
      return false;
    }

    return true;
  }

  boolean genNum(Num n, Operator op, long limInf2, long limSup2) {
    int r, exp, rang;
    long ls, li, k, v;
    boolean resolt;

    n.c = op.numDec;
    exp = n.c == 0 ? 1 : n.c == 1 ? 10 : 100;
    ls = Operator.LIMITS[op.limSup];
    if (limSup2 != RES && limSup2 < ls)
      ls = limSup2;
    li = Operator.LIMITS[op.limInf];
    if (limInf2 != RES && limInf2 > li)
      li = limInf2;

    resolt = false;
    if (op.fromList > 0) {
      n.vf = (long) op.lst[random.nextInt(op.fromList)];
      resolt = true;
    }
    if (!resolt) {
      r = random.nextInt(100);
      if (op.wZero && r <= 10) {
        n.vf = 0;
        resolt = true;
      } else if (op.wOne && r > 10 && r <= 20) {
        n.vf = 1;
        resolt = true;
      } else if (op.wMinusOne && r > 20 && r <= 30) {
        n.vf = -1;
        resolt = true;
      }
    }
    if (!resolt) {
      if (li > ls) {
        k = li;
        li = ls;
        ls = k;
      }
      rang = (int) (ls - li + 1);
      if (rang < 0)
        rang = 1;
      v = (long) (random.nextInt(rang) + li) * exp;
      if (exp > 1)
        v += random.nextInt(exp);
      n.vf = ((float) v) / exp;
    }
    return true;
  }

  boolean genOp(Operacio o) {
    int i;
    int[] ops = new int[NOPERACIONS];
    int nops, op;
    long rlinf, rlsup, ri2, rs2;
    float q;

    rlinf = Operator.LIMITS[resultLimInf];
    rlsup = Operator.LIMITS[resultLimSup];

    nops = 0;
    if (use_add)
      ops[nops++] = SUM;
    if (use_subst)
      ops[nops++] = REST;
    if (use_mult)
      ops[nops++] = MULT;
    if (use_div)
      ops[nops++] = DIV;

    op = ops[random.nextInt(nops)];
    switch (op) {
    case SUM:
      for (i = 0; i < NMAXLOOPS; i++) {
        genNum(o.numA, opA, RES, rlsup);
        ri2 = o.numA.vf < rlinf ? rlinf - (long) o.numA.vf : RES;
        rs2 = rlsup - (long) o.numA.vf;
        switch (opCond) {
        case AGB:
          if (rs2 == RES || rs2 > o.numA.vf)
            rs2 = (long) o.numA.vf;
          break;
        case BGA:
          if (ri2 == RES || ri2 < o.numA.vf)
            ri2 = (long) o.numA.vf;
          break;
        }
        genNum(o.numB, opB, ri2, rs2);
        o.numR.vf = o.numA.vf + o.numB.vf;
        if (o.numR.vf >= rlinf && o.numR.vf <= rlsup)
          break;
      }
      o.numR.c = o.numA.c > o.numB.c ? o.numA.c : o.numB.c;
      o.op = 0;
      if (resultCarry && o.numA.vf > 0 && o.numB.vf > 0) {
        int va, vb;
        q = o.numR.c == 2 ? 100 : o.numR.c == 1 ? 10 : 1;
        char[] bufa = getDF(WILDCARD_DF).format((long) (o.numA.vf * q + 0.5)).toCharArray();
        char[] bufb = getDF(WILDCARD_DF).format((long) (o.numB.vf * q + 0.5)).toCharArray();
        for (i = 0; i < 10; i++)
          if (bufa[i] != '0' || bufb[i] != '0')
            break;
        for (; i < 10; i++) {
          va = bufa[i] - '0';
          vb = bufb[i] - '0';
          if (va + vb < 10)
            continue;
          while (va + vb > 9) {
            if (va > vb)
              va = (va > 0 ? random.nextInt(va) : 0);
            else
              vb = (vb > 0 ? random.nextInt(vb) : 0);
          }
          bufa[i] = '0';
          for (int x = 0; x < va; x++)
            bufa[i]++;
          bufb[i] = '0';
          for (int x = 0; x < vb; x++)
            bufb[i]++;
        }

        o.numA.vf = (float) (Long.parseLong(new String(bufa)));
        o.numB.vf = (float) (Long.parseLong(new String(bufb)));
        o.numR.vf = (float) (long) (o.numA.vf + o.numB.vf + 0.5);

        o.numA.vf /= q;
        o.numB.vf /= q;
        o.numR.vf /= q;
      }
      break;

    case REST:
      for (i = 0; i < NMAXLOOPS; i++) {
        genNum(o.numA, opA, rlinf, RES);
        ri2 = o.numA.vf > rlsup ? (long) (o.numA.vf - rlsup) : RES;
        rs2 = (long) (o.numA.vf - rlinf);
        switch (opCond) {
        case AGB:
          if (rs2 == RES || rs2 > o.numA.vf)
            rs2 = (long) o.numA.vf;
          break;
        case BGA:
          if (ri2 == RES || ri2 < o.numA.vf)
            ri2 = (long) o.numA.vf;
          break;
        }
        genNum(o.numB, opB, ri2, rs2);
        o.numR.vf = o.numA.vf - o.numB.vf;
        if (o.numR.vf >= rlinf && o.numR.vf <= rlsup)
          break;
      }
      o.numR.c = o.numA.c > o.numB.c ? o.numA.c : o.numB.c;
      o.op = 1;
      if (resultCarry && o.numA.vf > 0 && o.numB.vf > 0 && o.numA.vf >= o.numB.vf) {
        int va, vb;
        q = (o.numR.c == 2 ? 100 : (o.numR.c == 1 ? 10 : 1));
        char[] bufa = getDF(WILDCARD_DF).format((long) (o.numA.vf * q + 0.5)).toCharArray();
        char[] bufb = getDF(WILDCARD_DF).format((long) (o.numB.vf * q + 0.5)).toCharArray();
        for (i = 0; i < 10; i++)
          if (bufb[i] != '0')
            break;
        for (; i < 10; i++) {
          va = bufa[i] - '0';
          vb = bufb[i] - '0';
          if (va >= vb)
            continue;
          vb = (va > 0 ? random.nextInt(va) : 0);
          bufb[i] = '0';
          for (int x = 0; x < vb; x++)
            bufb[i]++;
        }

        o.numA.vf = (float) (Long.parseLong(new String(bufa)));
        o.numB.vf = (float) (Long.parseLong(new String(bufb)));
        o.numR.vf = (float) (long) (o.numA.vf - o.numB.vf + 0.5);

        o.numA.vf /= q;
        o.numB.vf /= q;
        o.numR.vf /= q;
      }
      break;

    case MULT:
      for (i = 0; i < NMAXLOOPS; i++) {
        genNum(o.numA, opA, RES, RES);
        ri2 = Operator.LIMITS[opB.limInf];
        rs2 = Operator.LIMITS[opB.limSup];
        switch (opCond) {
        case AGB:
          if (rs2 > o.numA.vf)
            rs2 = (long) o.numA.vf;
          break;
        case BGA:
          if (ri2 < o.numA.vf)
            ri2 = (long) o.numA.vf;
          break;
        }
        genNum(o.numB, opB, ri2, rs2);
        o.numR.vf = o.numA.vf * o.numB.vf;
        if (o.numR.vf >= rlinf && o.numR.vf <= rlsup)
          break;
      }
      o.numR.c = o.numA.c + o.numB.c;
      o.op = 2;
      break;

    case DIV:
      for (i = 0; i < NMAXLOOPS; i++) {
        genNum(o.numA, opA, RES, RES);
        ri2 = Operator.LIMITS[opB.limInf];
        rs2 = Operator.LIMITS[opB.limSup];
        switch (opCond) {
        case AGB:
          if (rs2 > o.numA.vf)
            rs2 = (long) o.numA.vf;
          break;
        case BGA:
          if (ri2 < o.numA.vf)
            ri2 = (long) o.numA.vf;
          break;
        }
        genNum(o.numB, opB, ri2, rs2);
        if (o.numB.vf != 0 && Math.abs(o.numA.vf) >= Math.abs(o.numB.vf) && (o.numR.vf = o.numA.vf / o.numB.vf) >= rlinf
            && o.numR.vf <= rlsup)
          break;
      }
      if (o.numB.vf == 0)
        o.numB.vf = 1;
      o.numR.vf = o.numA.vf / o.numB.vf;
      i = o.numA.c - o.numB.c;
      q = (float) (Math.pow(10, i));
      o.numA.vf *= q;
      o.numR.vf *= q;
      o.numR.vf = ((long) o.numR.vf);
      o.numA.vf = o.numR.vf * o.numB.vf;
      o.numA.vf /= q;
      o.numR.vf /= q;
      o.numR.c = i > 0 ? i : 0;
      o.op = 3;
      break;

    default:
      return false;
    }

    return true;
  }

  public boolean generateContent(Object kit, ResourceBridge rb) {
    boolean result = false;
    if (kit instanceof ActiveBagContentKit) {
      ActiveBagContentKit k = (ActiveBagContentKit) kit;
      result = generateContent(k.nRows, k.nCols, k.content, k.useIds, rb);
    }
    return result;
  }

  protected boolean generateContent(int nRows, int nCols, ActiveBagContent[] content, boolean useIds,
      ResourceBridge rb) {
    if (nRows <= 0 || nCols <= 0 || content == null || content.length < 1 || content[0] == null || rb == null)
      return false;

    Operacio o;
    Operacio[] op;
    int i, j, k;
    int[] tipus = new int[NTIPUSEX];
    int numTipus, tipX;
    boolean tipInv;
    String va, vb, vc, operator;
    String[] stra, strb, strc;
    int nColsB = nCols, nRowsB = nRows;
    int nCells = nRows * nCols;

    if (nCells < 2)
      return false;

    int[] ass = null;

    numTipus = 0;
    if (exp_abx)
      tipus[numTipus++] = ABX;
    if (exp_axc)
      tipus[numTipus++] = AXC;
    if (exp_xbc)
      tipus[numTipus++] = XBC;
    if (exp_axbc)
      tipus[numTipus++] = AXBC;
    if (numTipus == 0)
      return false;
    tipInv = exp_caxb;

    op = new Operacio[nCells];
    stra = new String[nCells];
    strb = new String[nCells];
    strc = new String[nCells];

    for (i = 0; i < nCells; i++) {
      o = new Operacio();
      for (j = 0; j < NMAXLOOPS; j++) {
        genOp(o);
        if (resultNoDup) {
          for (k = 0; k < i; k++) {
            if (o.numR.vf == op[k].numR.vf)
              break;
          }
          if (k == i)
            break;
        } else
          break;
      }
      op[i] = o;
    }

    if (resultOrder != 0) {
      for (i = nCells - 1; i > 0; i--) {
        for (j = 0; j < i; j++) {
          if ((resultOrder == SORTASC && op[j].numR.vf > op[j + 1].numR.vf)
              || (resultOrder == SORTDESC && op[j].numR.vf < op[j + 1].numR.vf)) {
            o = op[j];
            op[j] = op[j + 1];
            op[j + 1] = o;
          }
        }
      }
    }

    for (i = 0; i < nCells; i++) {
      tipX = tipus[random.nextInt(numTipus)];
      va = getDF(op[0].numA.c).format(op[i].numA.vf);
      vb = getDF(op[0].numB.c).format(op[i].numB.vf);
      vc = getDF(op[0].numR.c).format(op[i].numR.vf);
      operator = OPSTR[op[i].op];

      if (tipInv)
        strc[i] = vc + S + "=" + S + va + S + operator + S + vb;
      else
        strc[i] = va + S + operator + S + vb + S + "=" + S + vc;

      switch (tipX) {
      case AXC:
        strb[i] = vb;
        if (tipInv)
          stra[i] = vc + S + "=" + S + va + S + operator + S + "?";
        else
          stra[i] = va + S + operator + S + "?" + S + "=" + S + vc;
        break;

      case XBC:
        strb[i] = va;
        if (tipInv)
          stra[i] = vc + S + "=" + S + "?" + S + operator + S + vb;
        else
          stra[i] = "?" + S + operator + S + vb + S + "=" + S + vc;
        break;

      case AXBC:
        strb[i] = operator;
        if (tipInv)
          stra[i] = vc + S + "=" + S + va + S + "?" + S + vb;
        else
          stra[i] = va + S + "?" + S + vb + S + "=" + S + vc;
        break;

      default:
        strb[i] = vc;
        if (tipInv)
          stra[i] = "?" + S + "=" + S + va + S + operator + S + vb;
        else
          stra[i] = va + S + operator + S + vb + S + "=";
        break;
      }
    }

    if (useIds) {
      ass = new int[nCells];
      String[] strbx = new String[nCells];
      k = 0;
      for (i = 0; i < nCells; i++) {
        for (j = 0; j < k; j++)
          if (strb[i].equals(strbx[j]))
            break;
        if (j == k) {
          strbx[k] = strb[i];
          ass[i] = k;
          k++;
        } else
          ass[i] = j;
      }

      strb = new String[k];
      for (i = 0; i < k; i++)
        strb[i] = strbx[i];

      if (nRowsB * nColsB != k) {
        boolean distH = false;
        switch (k) {
        case 6:
          nRowsB = distH ? 2 : 3;
          nColsB = distH ? 3 : 2;
          break;

        case 8:
          nRowsB = distH ? 2 : 4;
          nColsB = distH ? 4 : 2;
          break;

        case 9:
          nRowsB = 3;
          nColsB = 3;
          break;

        case 10:
          nRowsB = distH ? 2 : 5;
          nColsB = distH ? 5 : 2;
          break;

        case 12:
          nRowsB = distH ? 3 : 4;
          nColsB = distH ? 4 : 3;
          break;

        case 14:
          nRowsB = distH ? 2 : 7;
          nColsB = distH ? 7 : 2;
          break;

        case 15:
          nRowsB = distH ? 3 : 5;
          nColsB = distH ? 3 : 5;
          break;

        case 16:
          nRowsB = 4;
          nColsB = 4;
          break;

        case 18:
          nRowsB = distH ? 6 : 3;
          nColsB = distH ? 3 : 6;
          break;

        case 20:
          nRowsB = distH ? 4 : 5;
          nColsB = distH ? 5 : 4;
          break;

        default:
          nRowsB = distH ? 1 : k;
          nColsB = distH ? k : 1;
          break;
        }
      }
    }

    content[0].setTextContent(stra, nCols, nRows);
    if (ass != null)
      content[0].setIds(ass);
    if (content.length > 1 && content[1] != null) {
      content[1].setTextContent(strb, nColsB, nRowsB);
      content[1].getShaper().reset(nColsB, nRowsB);
    }
    if (content.length > 2 && content[2] != null)
      content[2].setTextContent(strc, nCols, nRows);

    return true;
  }

  public static boolean checkClient(Class cl) {
    return ActiveBagContentKit.Compatible.class.isAssignableFrom(cl);
  }
}
