/************************************************************************
**                                                                     **
** PerfectCar Option Relationship Rules Engine                         **
** Copyright (c) 2004 Xprima Corporation, All Rights Reserved          **
**                                                                     **
** Francis Caporuscio                                                  **
** December 2003-January 2004                                          **
**                                                                     **
************************************************************************/

var dashes = "----------------------------------------------------------------------------------------------------------";


// #######################################################################
function setCH()
{
  var i;

  for(i = 0; i < V.length; i++) {
    opname = "OP" + i.toString();
    if(document.forms.pcOptionSelection[opname].checked == true) { ch[i] = 1; ch2[i] = 1; }
    else { ch[i] = 0; ch2[i] = 0; }
  }
}


// #######################################################################
function itemAllowed(idx, r)
{
  var i;

  if(r == 0) {
    processChange = false;
    req="";
    setCH();
  }

  success = false;
  if(ch[idx] == 1) {
    success = itemAllowed_Select(idx, r);
  }
  else {
    success = itemAllowed_Unselect(idx, r);
  }

  if(success && r == 0) {
    // We're done, actually make the changes
    for(i = 0; i < V.length; i++) {
      op = "OP" + i.toString();
      if(ch[i] == 1) {
        document.forms.pcOptionSelection[op].checked = true;
      }
      else {
        document.forms.pcOptionSelection[op].checked = false;
      }
    }

    if(_regColors) {
      // Process Color Rules
      processColorRulesCur();
    }

    // Set the images (dependency/exclusion)
    setImages2();
    calcPrice2();
  }

  return success;
}


// #######################################################################
function itemAllowed_Select(idx, r)
{
  selectedOptions = getOptionByCodes(V[idx][2]);
  groups = getGroupsForOptions(selectedOptions);
  rules = getRulesForGroups(groups);

  if(rules.length == 0) {
    selectOptions(selectedOptions);
    return true;
  }

  return processRules_Select(idx, r, rules);
}


// #######################################################################
function itemAllowed_Unselect(idx, r)
{
  selectedOptions = getOptionByCodes(V[idx][2]);
  groups = getGroupsForOptions(selectedOptions);
  rules = getRulesForGroups(groups);

  if(rules.length == 0) {
    unselectOptions(selectedOptions);
    return true;
  }

  return processRules_Unselect(idx, r, rules);
}


// #######################################################################
function processRules_Select(idx, r, rules)
{
  // Process Each Rule as long as the previous has succeeded.  Return the
  // final success

  var ruleidx;
  var success = true;
  var rule;
  var cmpType;
  var L;
  var R;
  var g;
  var _i;
  var G2;

  for(ruleidx = 0; success && ruleidx < rules.length; ruleidx++) {
    rule = GA[rules[ruleidx]];
    cmpType = (rule[0] * 100) + (rule[1] * 10) + rule[2];

    L = rule[3];
    R = rule[4];
    switch(cmpType) {
      case   0: success = cmp000(L,R);
                if(!success) {
                  listRequirements(idx, G[R], false);
                  g = makeArrayWithElement(G[R],0);
                  success = processRequirements(g);
                }
                break;

      case   1: success = cmp001(L,R);
                if(!success) {
                  if(listRequirements(idx, G[R], true)) {
                    success = processRequirements(G[R]);
                  }
                }
                break;

      case  10: success = cmp010(L,R);
                if(!success) {
                  listRequirements(idx, G[R], false);
                  g = makeArrayWithElement(G[R],0);
                  success = processRequirements(g);
                }
                break;

      case  11: success = cmp011(L,R);
                if(!success) {
                  if(listRequirements(idx, G[R], true)) {
                    success = processRequirements(G[R]);
                  }
                }
                break;

      case 100: success = cmp100(L,R);
                if(!success) {
                  success = proposeExclusionS(idx, rule);
                }
                break;

      case 101: success = cmp101(L,R);
                if(!success) {
                  success = proposeExclusionS(idx, rule);
                }
                break;

      case 110: success = cmp110(L,R);
                if(!success) {
                  success = proposeExclusionS(idx, rule);
                }
                break;

      case 111: success = cmp111(L,R);
                if(!success) {
                  success = proposeExclusionS(idx, rule);
                }
                break;

      case 211: // Extended Dependency Rule
                G2 = new Array();
                R = new Array();
                for(_i = 4; _i < rule.length; _i++) {
                  R[R.length] = rule[_i];
                  G2[G2.length] = G[rule[_i]];
                }

                success = cmp211(L,R);
                if(!success) {
                  listRequirementsE(idx, G2, false);
                  success = processRequirements(G2[0]);
                }
                break;

      default:  success = false;
                break;
    }
  }

  return success;
}


// #######################################################################
function processRules_Unselect(idx, r, rules)
{
  var ruleidx;
  var _i;

  var success = true;
  for(ruleidx = 0; success && ruleidx < rules.length; ruleidx++) {
    rule = GA[rules[ruleidx]];
    cmpType = (rule[0] * 100) + (rule[1] * 10) + rule[2];

    L = rule[3];
    R = rule[4];
    switch(cmpType) {
      case   0: success = cmpu000(L,R,idx);
                if(!success) {
                  success = proposeExclusionU(idx, rule);
                }
                break;

      case   1: success = cmpu001(L,R,idx);
                if(!success) {
                  success = proposeExclusionU(idx, rule);
                }
                break;

      case  10: success = cmpu010(L,R,idx);
                if(!success) {
                  success = proposeExclusionU(idx, rule);
                }
                break;

      case  11: success = cmpu011(L,R,idx);
                if(!success) {
                  success = proposeExclusionU(idx, rule);
                }
                break;

      case 100: success = true; // exclusions have no effect on unselect
                break;

      case 101: success = true; // exclusions have no effect on unselect
                break;

      case 110: success = true; // exclusions have no effect on unselect
                break;

      case 111: success = true; // exclusions have no effect on unselect
                break;

      case 211: // Extended Dependency Rule
                R = new Array();
                for(_i = 4; _i < rule.length; _i++) {
                  R[R.length] = rule[_i];
                }

                success = cmpu211(L,R,idx);
                if(!success) {
                  success = proposeExclusionU(idx, rule);
                }
                break;

      default:  success = false;
                break;
    }
  }

  return success;
}


// #######################################################################
function selectOptions(options)
{
  var i;
  for(i = 0; i < options.length; i++) {
    if(options[i] != -1 && ch[options[i]] != 1) {
      ch[options[i]] = 1;
    }
  }
}


// #######################################################################
function unselectOptions(selectedOptions)
{
  var i;
  for(i = 0; i < selectedOptions.length; i++) {
    if(selectedOptions[i] != -1 && ch[selectedOptions[i]] == 1) {
      ch[selectedOptions[i]] = 0;
    }
  }
}







// #######################################################################
function getGroupsForOptions(selectedOptions)
{
  var i;
  var j;

  // Find all the groups that refer to these options
  groups = new Array();

  for(i = 0; i < G.length; i++) {
    found = false;
    for(j = 0; !found && j < G[i].length; j++) {
      for(index = 0; !found && index < selectedOptions.length; index++) {
        found = G[i][j] == selectedOptions[index] ? true : false;
        if(found) { groups[groups.length] = i; }
      }
    }
  }

  return groups;
}


// #######################################################################
function getRulesForGroups(selectedOptions)
{
  var i;
  var j;
  var k;
  var dependencies = new Array();
  var exclusions = new Array();
  var rules = new Array();
  var done;

  for(i = 0; i < GA.length; i++) {
    for(j = 0; j < groups.length; j++) {
      if(GA[i].length == 5) {
        if(GA[i][3] == groups[j] || GA[i][4] == groups[j]) {
          // Exclusion or dependency?
          if(GA[i][0] == 1) {
            exclusions[exclusions.length] = i;
          }
          else {
            dependencies[dependencies.length] = i;
          }
        }
      }
      else {
        done = false;
        for(k = 4; !done && k < GA[i].length; k++) {
          if(GA[i][3] == groups[j] || GA[i][k] == groups[j]) {
            done = true;

            // Exclusion or dependency?
            if(GA[i][0] == 1) {
              exclusions[exclusions.length] = i;
            }
            else {
              dependencies[dependencies.length] = i;
            }
          }
        }
      }
    }
  }

  for(i = 0; i < exclusions.length; i++) {
    rules[rules.length] = exclusions[i];
  }

  for(i = 0; i < dependencies.length; i++) {
    rules[rules.length] = dependencies[i];
  }

  return rules;
}



// #######################################################################
function getOptionByCodes(code)
{
  var i;

  o=new Array();
  for(i = 0; i < V.length; i++) {
    if(V[i][2] == code) o[o.length] = i;
  }

  return o;
}


// #######################################################################
function processRequirements(options)
{
  var pr_idx;

  for(pr_idx = 0; pr_idx < options.length; pr_idx++) {
    if(options[pr_idx] != -1 && ch[options[pr_idx]] != 1) {
      ch[options[pr_idx]] = 1;
    }
  }

  success = true;
  for(pr_idx = 0; success && pr_idx < options.length; pr_idx++) {
    if(options[pr_idx] != -1 && ch[options[pr_idx]] == 1) {
      success = itemAllowed_Select(options[pr_idx], 1);
    }
  }

  return success;
}








/*************************************************************************
** Ls = Left side satisfied                                             **
** Rs = Right side satisfied                                            **
*************************************************************************/

// #######################################################################
function cmp000(L,R)
{
  // Lany require Rany
  var Ls = Lany(L);
  if(!Ls) return true;

  var Rs = Rany(R);
  return Rs;
}


// #######################################################################
function cmp001(L,R)
{
  // Lany require Rall

  var Ls = Lany(L);
  if(!Ls) return true;

  var Rs = Rall(R);
  return Rs;
}


// #######################################################################
function cmp010(L,R)
{
  // Lall require Rany

  var Ls = Lall(L);
  if(!Ls) return true;

  var Rs = Rany(R);
  return Rs;
}


// #######################################################################
function cmp011(L,R)
{
  // Lall require Rall

  var Ls = Lall(L);
  if(!Ls) return true;

  var Rs = Rall(R);
  return Rs;
}


// #######################################################################
function cmp100(L,R)
{
  // Lany exclude Rany

  var Ls = Lany(L);
  if(!Ls) return true;

  var Rs = Rany(R);
  return !Rs;
}


// #######################################################################
function cmp101(L,R)
{
  // Lany exclude Rall

  var Ls = Lany(L);
  if(!Ls) return true;

  var Rs = Rall(R);
  return !Rs;
}


// #######################################################################
function cmp110(L,R)
{
  // Lall exclude Rany

  var Ls = Lall(L);
  if(!Ls) return true;

  var Rs = Rany(R);
  return !Rs;
}


// #######################################################################
function cmp111(L,R)
{
  // Lall exclude Rall

  var Ls = Lall(L);
  if(!Ls) return true;

  var Rs = Rall(R);
  return !Rs;
}


// #######################################################################
function cmp211(L,R)
{
  // Extended Dependency, R is an array -- and (Lall,Rxall) success is a success.
  // Lall require Rall

  var Rs = false;
  var i;

  var Ls = Lall(L);
  if(!Ls) return true;

  for(i = 0; !Rs && i < R.length; i++) {
    Rs = cmp011(L,R[i]);
  }

  return Rs;
}


// #######################################################################
// Lside cannot have OR, so this will never get called.
function cmpu000(L,R,idx)
{
  var i;

  // Find out if idx is being removed from L or R
  var isL = false;
  for(i = 0; !isL && i < G[L].length; i++) {
    if(G[L][i] == idx) isL = true;
  }

  if(isL) {
    // We're removing from the left side of the equasion, this would
    // make the L side unsatisfied --- this means the rule is valid
    // (yes, an unsatisfied L is ok, a satisfied L REQUIRES a satisfied R)
    return true;
  }

  /*
  // Loptions depend on any Roption, so as long as another also exists the move is good.
  otherSelected = false;
  for(i = 0; i < G[R].length; i++) {
    if(G[R][i] != -1 && G[R][i] != idx) {
      if(ch[G[R][i]] == 1) {
        otherSelected = true;
      }
    }
  }
  */

  otherSelected = Rany(R);
  return otherSelected;
}


// #######################################################################
// Lside cannot have OR, so this will never get called.
function cmpu001(L,R,idx)
{
  var i;

  // Find out if idx is being removed from L or R
  var isL = false;
  for(i = 0; !isL && i < G[L].length; i++) {
    if(G[L][i] == idx) isL = true;
  }

  if(isL) {
    // We're removing from the left side of the equasion, this would
    // make the L side unsatisfied --- this means the rule is valid
    // (yes, an unsatisfied L is ok, a satisfied L REQUIRES a satisfied R)
    return true;
  }

  return false;  // we need all Roptions
}


// #######################################################################
function cmpu010(L,R,idx)
{
  var i;

  // Find out if idx is being removed from L or R
  var isL = false;
  for(i = 0; !isL && i < G[L].length; i++) {
    if(G[L][i] == idx) isL = true;
  }

  if(isL) {
    // We're removing from the left side of the equasion, this would
    // make the L side unsatisfied --- this means the rule is valid
    // (yes, an unsatisfied L is ok, a satisfied L REQUIRES a satisfied R)
    return true;
  }

  otherSelected = Rany(R);
  return otherSelected;
}


// #######################################################################
function cmpu011(L,R,idx)
{
  var i;

  // Find out if idx is being removed from L or R
  var isL = false;
  for(i = 0; !isL && i < G[L].length; i++) {
    if(G[L][i] == idx) isL = true;
  }

  if(isL) {
    // We're removing from the left side of the equasion, this would
    // make the L side unsatisfied --- this means the rule is valid
    // (yes, an unsatisfied L is ok, a satisfied L REQUIRES a satisfied R)
    return true;
  }

  // We're removing from the R side... this means that if Lall is true, then it's a bad move
  // otherwise it's ok.

  if(Lall(L)) {
    return false;
  }

  // At this point it's a legal move
  return true;
}

// #######################################################################
function cmpu211(L,R,idx)
{
  var i;
  var Rs = false;

  // Find out if idx is being removed from L or R
  var isL = false;
  for(i = 0; !isL && i < G[L].length; i++) {
    if(G[L][i] == idx) isL = true;
  }

  if(isL) {
    // We're removing from the left side of the equasion, this would
    // make the L side unsatisfied --- this means the rule is valid
    // (yes, an unsatisfied L is ok, a satisfied L REQUIRES a satisfied R)
    return true;
  }

  // We're removing from the R side...
  // If Lall isn't satisfied, we're fine

  if(!Lall(L)) {
    return true;
  }

  // L is satisfied.  We need to see if ANY of the Rs are satisfied.  If so, the move
  // is legal, otherwise it's not.

  for(i = 0; !Rs && i < R.length; i++) {
    Rs = Rall(R[i]);
  }

  return Rs;
}


// #######################################################################
function Lall(L)
{
  var Ls = false;
  var Lc = 0;
  var i;

  for(i = 0; i < G[L].length; i++) {
    if(G[L][i] != -1) {
      if(ch[G[L][i]] == 1) Lc++;
    }
    else {
      Lc++; // so that the count matches
    }
  }

  Ls = Lc == G[L].length ? true : false;
  return Ls;
}

// #######################################################################
function Lany(L)
{
  var Ls = false;
  var i;

  for(i = 0; !Ls && i < G[L].length; i++) {
    if(G[L][i] != -1) {
      if(ch[G[L][i]] == 1) {
        Ls = true;
      }
    }
  }

  return Ls;
}


// #######################################################################
function Rall(R)
{
  var Rs = false;
  var Rc = 0;
  var d = new Array();
  var i;

  for(i = 0; !Rs && i < G[R].length; i++) {
    if(G[R][i] != -1) {
      if(ch[G[R][i]] == 1) {
        Rc++;
      }
    }
    else {
      Rc++; // so that the count matches
    }
  }

  Rs = Rc == G[R].length ? true : false;
  return Rs;
}


// #######################################################################
function Rany(R)
{
  var Rs = false;
  var i;

  for(i = 0; !Rs && i < G[R].length; i++) {
    if(G[R][i] != -1) {
      if(ch[G[R][i]] == 1) {
        Rs = true;
      }
    }
  }

  return Rs;
}


// #######################################################################
function listRequirements(idx, reqs, all)
{
  var msg;

  msg = t_selectop + ":\n- " + V[idx][1] + " ($" + V[idx][0] + ")\n";
  msg += dashes + "\n\n";
  msg += t_otherop + ".  ";

  if(all == true) {
    msg += t_reqop + ":\n";
  }
  else {
    msg += t_reqop1 + ":\n";
  }

  for(i = 0; i < reqs.length; i++) {
    if(reqs[i] != -1) {
      msg += "- " + V[reqs[i]][1] + " ($" + V[reqs[i]][0] + ")";

      if(!all && reqs.length > 1 && i == 0) {
        msg += " \n    " + t_autoselect;
      }

      msg += "\n";
    }
  }

  if(!all) {
    msg = msg + "\n\n" + t_weakdepselection + ".\n";
  }

  msg += "\n\n" + t_otherreq + ".\n";

  if(all) {
    msg += "\n\n" + t_proceed;
    return confirm(msg);
  }

  alert(msg);
}


// #######################################################################
function listRequirementsE(idx, reqs, all)
{
  var msg;
  var i;
  var j;

  msg = t_selectop + ":\n- " + V[idx][1] + " ($" + V[idx][0] + ")\n";
  msg += dashes + "\n\n";
  msg += t_otherop + ".  ";

  if(all == true) {
    msg += t_reqop + ":\n";
  }
  else {
    msg += t_reqop1 + ":\n";
  }


  for(i = 0; i < reqs.length; i++) {
    if(i > 0) {
      msg += "\n--- " + t_or + " ---\n\n";
    }

    for(j = 0; j < reqs[i].length; j++) {
      if(reqs[i][j] != -1) {
        msg += "- " + V[reqs[i][j]][1] + " ($" + V[reqs[i][j]][0] + ")";
        msg += "\n";
      }
    }

    if(!all && reqs[i].length > 1 && i == 0) {
      msg += "    " + t_autoselect;
      msg += "\n";
    }
  }

  if(!all) {
    msg = msg + "\n\n" + t_weakdepselection + ".\n";
  }

  msg += "\n\n" + t_otherreq + ".\n";

  if(all) {
    msg += "\n\n" + t_proceed;
    return confirm(msg);
  }

  alert(msg);
}



// #######################################################################
function proposeExclusion(idx, groupid, all)
{
  var i;
  var j;
  var id;
  var msg = "";
  var selected = new Array();
  var processed = new Array(V.length);
  var group = G[groupid]
  var success;

  for(i = 0; i < V.length; i++) {
    processed[i] = 0;
  }

  for(i = 0; i < group.length; i++) {
    if(group[i] != -1) {
      if(group[i] != idx && ch[group[i]] == 1 && processed[group[i]] != 1) {
        selected[selected.length] = group[i];
        processed[group[i]] = 1;
      }
    }
  }

  if(selected.length == 0) {
    return true; // none of them are selected, so there's no problem
  }

  // All those listed in 'selected' will need to be excluded if the user
  // absolutely wants 'idx' to be selected --- or one of them if 'all' is false;
  var accept = false;

  if(selected.length == 1 && V[selected[0]][3] == V[idx][3] && V[idx][3] != -1) {
    // Just 1 selected and it's from the same category-type as the one we're
    // selecting... auto-accept the selection.
    accept = true;
  }

  if(!accept) {
    msg = t_warning + "\n";
    msg += dashes + "\n";
    msg += t_selectop + ":\n- " + V[idx][1] + " ($" + V[idx][0] + ")\n";
    msg += dashes + "\n";

    if(all) {
      msg += t_combine1 + ":";
    }
    else {
      msg += t_combine2 + ":";
    }

    msg += "\n";

    for(i = 0; i < selected.length; i++) {
      msg += "- " + V[selected[i]][1] + " ($" + V[selected[i]][0] + ")\n";
    }

    msg += "\n\n";
    msg += t_unselect + ".\n\n";
    msg += t_unselectwarn + ".\n\n";
    msg += t_proceed21 + " '" + V[idx][1] + "' " + t_proceed22 + " ?";

    accept = confirm(msg);
  }

  if(accept) {
    success = true;
    for(i = 0; success && i < (all ? selected.length: 1); i++) {
      ch[selected[i]] = 0; // unselect the option
      success = itemAllowed(selected[i], 1);
    }
    return success;
  }

  return false;
}


// #######################################################################
function proposeExclusionU(idx, rule)
{
  // This function should only get called when we try to unselect 'idx'
  // and we know that idx belongs in the Roption list of the rule
  // and that the rule is a dependency.

  var ed = rule[0];
  var Lo = rule[1];
  var Ro = rule[2];
  var L  = rule[3];
  var R  = rule[4];
  var i;
  var msg;

  // Loptions depend on some/all Roptions, since we've trying to unselect an Roption, we
  // need to propose to remove some/all Loptions.

  Loptions = new Array();
  for(i = 0; i < G[L].length; i++) {
    if(G[L][i] != -1 && ch[G[L][i]] == 1) {
      Loptions[Loptions.length] = G[L][i];
    }
  }

  if(Loptions.length == 0) {
    // None of the Loptions are selected
    return true;
  }

  msg = t_warning + "\n";
  msg += dashes + "\n";
  msg += t_removeoption + ":\n- " + V[idx][1] + " ($" + V[idx][0] + ")\n";
  msg += dashes + "\n";

  msg += t_isrequired + "\n";

  for(i = 0; i < Loptions.length; i++) {
    msg += "- " + V[Loptions[i]][1] + " ($" + V[Loptions[i]][0] + ")\n";
  }

  msg += "\n\n";
  msg += t_remove + " ";

  if(Loptions.length > 1) {
    msg += t_remove2;
  }
  else {
    msg += t_remove3;
  }

  msg += " " + t_remove4 + ".";

  if(Loptions.length > 1) {
    msg += "  " + t_removenote + ".";
  }

  msg += "\n\n\n" + t_wishremove + " '" + V[idx][1] + "' ?";

  var accept = confirm(msg);
  if(accept) {
    success = true;
    for(i = 0; success && i < Loptions.length; i++) {
      ch[Loptions[i]] = 0; // unselect the option
      success = itemAllowed(Loptions[i], 1);
    }
    return success;
  }

  return false;
}


// #######################################################################
function rolloutOptions(o)
{
  var success = true;
  var i;

  for(i = 0; success && i < o.length; i++) {
    if(o[i] != -1) {
      if(ch[o[i]] != 1) {
        ch[o[i]] = 1;
        success = itemAllowed(o[i], 1);
      }
    }
  }
  return success;
}


// #######################################################################
function rollbackOptions(o)
{
  var i;

  for(i = 0; i < o.length; i++) {
    if(o[i] != -1) {
      if(ch[o[i]] != ch2[o[i]]) {
        ch[o[i]] = ch2[o[i]];
      }
    }
  }
}


// #######################################################################
function proposeExclusionS(idx, rule)
{
  // This function should only get called when a user is trying to select 'idx'
  // but we know that to do so we'll need to exclude other options.  'idx' may be
  // a Loption or a Roption, we'll need to figure it out.

  var ed = rule[0];
  var Lo = rule[1];
  var Ro = rule[2];
  var L  = rule[3];
  var R  = rule[4];
  var i;
  var isL = false;
  var msg;

  for(i = 0; !isL && i < G[L].length; i++) {
    if(G[L][i] == idx) isL = true;
  }

  if(isL) {
    // idx is a Loption
    // we need to exclude one/all Roption(s) -- dependeing on Ro
    if(Ro == 1) {
      // exclude all Roptions
      return proposeExclusion(idx, R, true);
    }
    else {
      // exclude any (we'll pick the first) Roption
      return proposeExclusion(idx, R, false);
    }
  }
  else {
    // idx is a Roption
    // we need to exclude all Loptions
    return proposeExclusion(idx, L, true);
  }

  return false;
}


// #######################################################################
function setImages2()
{
  // Each selected option must be verified to see if it is another
  // option's dependency.  All non-selected option must be verified
  // to see if it is being excluded by a selected option.

  var i;
  var op;

  for(i = 0; i < V.length; i++) {
    op = document.forms.pcOptionSelection["IM" + i.toString()];

    if(ch[i] == 1) {
      if(_isDependency(i)) {
        op.src = iDep.src;
      }
      else {
        op.src = iOK.src;
      }
    }
    else {
      if(_isExclusion(i)) {
        if(_isWeakDependency(i) && op.src != iExc.src) {
          op.src = iWDep.src;
        }
        else {
          op.src = iExc.src;
        }
      }
      else {
        op.src = iOK.src;
      }
    }
  }
}


// #######################################################################
function _isDependency(idx)
{
  // We need to check if 'idx' is a dependency to other options.

  var selectedOptions = getOptionByCodes(V[idx][2]);
  var groups = getGroupsForOptions(selectedOptions);
  var rules = getRulesForGroups(groups);
  var i;
  var j;
  var k;
  var L;
  var R;
  var Ls;
  var Rs;

  if(rules.length == 0) {
    // 'idx' isn't part of ANY rules... this guarantees it's not a dependency
    return false;
  }

  // Go through the rules where 'idx' is an Roption, and a dependency rule
  for(i = 0; i < rules.length; i++) {
    rule = GA[rules[i]];
    // Dependency rule?
    if(rule[0] == 0) {
      Lo = rule[1];
      Ro = rule[2];
      L  = rule[3];
      R  = rule[4];

      // See if 'idx' is in R
      for(j = 0; j < G[R].length; j++) {
        if(G[R][j] == idx) {
          // First check if L is satisfied
          Ls = Lo == 0 ? Lany(L) : Lall(L);
          if(Ls) {
            // At this point we know that L is satisfied
            Rs = Ro == 0 ? Rany(R) : Rall(R);
            if(Rs) return true;
          }
        }
      }
    }
    else if(rule[0] == 2) {
      // Extended Dependency Rule
      Lo = rule[1];
      Ro = rule[2];
      L  = rule[3];

      for(k = 4; k < rule.length; k++) {
        R = rule[k];
        // See if 'idx' is in R
        for(j = 0; j < G[R].length; j++) {
          if(G[R][j] == idx) {
            // First check if L is satisfied
            Ls = Lall(L);
            if(Ls) {
              // At this point we know that L is satisfied
              Rs = Rall(R);
              if(Rs) return true;
            }
          }
        }
      }
    }
  }

  return false;
}


// #######################################################################
function _isExclusion(idx)
{
  // We need to check if 'idx' gets excluded by another

  var selectedOptions = getOptionByCodes(V[idx][2]);
  var groups = getGroupsForOptions(selectedOptions);
  var rules = getRulesForGroups(groups);
  var i;
  var j;
  var L;
  var Lo;
  var R;
  var Ro;
  var Ls;
  var Rs;
  var group;

  if(rules.length == 0) {
    // 'idx' isn't part of ANY rules... this guarantees it's not an exclusion
    return false;
  }

  // Exclusions are 2-way so we need validate all exclusion rules
  // (L and R)

  for(i = 0; i < rules.length; i++) {
    rule = GA[rules[i]];

    // Exclusion Rule?
    if(rule[0] == 1) {
      Lo = rule[1];
      Ro = rule[2];
      L  = rule[3];
      R  = rule[4];

      Ls = Lo == 0 ? Lany(L) : Lall(L);
      Rs = Ro == 0 ? Rany(R) : Rall(R);

      if(Ls || Rs) {
        // The false side contains the excluded options, find out if 'idx' is
        // part of the false side
        group = null;
        if(!Ls) {
          group = G[L];
        }
        else if(!Rs) {
          group = G[R];
        }

        if(group != null) {
          for(j = 0; j < group.length; j++) {
            if(group[j] == idx) {
              return true;
            }
          }
        }
      }
    }
  }

  return false;
}


// #######################################################################
function _isWeakDependency(idx)
{
  // We need to see if 'idx' is part of a weak dependency where another
  // option from the weak dependency is selected -- should only be called
  // if 'idx' is being excluded.

  var selectedOptions = getOptionByCodes(V[idx][2]);
  var groups = getGroupsForOptions(selectedOptions);
  var rules = getRulesForGroups(groups);
  var isWeak = false;
  var i;
  var j;
  var L;
  var Lo;
  var R;
  var Ro;
  var Ls;
  var Rs;
  var group;

  if(rules.length > 0 && ch[idx] != 1) {
    for(i = 0; i < rules.length; i++) {
      rule = GA[rules[i]];

      // We need to locate weak-dependency rules (OR on Roption)
      // Dependency Rule?
      if(rule[0] == 0) {
        Lo = rule[1];
        Ro = rule[2];
        L  = rule[3];
        R  = rule[4];

        if(Ro == 0) {
          // We need to make sure 'idx' is in R
          for(j = 0; j < G[R].length; j++) {
            if(G[R][j] == idx) {
              Ls = Lo == 0 ? Lany(L) : Lall(L);
              Rs = Rany(R);

              if(Ls && Rs) {
                // It is a weak dependency where another option is currently selected
                return true;
              }
            }
          }
        }
      }
    }
  }

  return isWeak;
}


// #######################################################################
function calcPrice2()
{
  var i;
  var j;
  var price = 0;
  var changed = false;
  var op;
  var ac = false;

  // First, let's set the prices on the form
  for(i = 0; i < V.length; i++) {
    price = V[i][0];
    changed = false;
    for(j = 0; j < V.length; j++) {
      if(i != j && ch[j] == 1) {
        if(!isNaN(PT[i][j]) && (PT[i][j] < price || !changed)) {
          price = PT[i][j];
          changed = true;
        }
      }
    }

    op = "PT" + i.toString();

    document.forms.pcOptionSelection[op].value = (price != 0) ? frmt(price) : t_nocost;

    if(ch[i] == 1 && V[i][4] == 1) {
      ac = true;
    }
  }

  price = 0;
  for(i = 0; i < V.length; i++) {
    if(ch[i] == 1) {
      price += _getPrice(i);
    }
  }

  price += curV;

  if(_regColors) {
    price += getColorSelectionPrice();
  }

  if(_regTaxes) {
    _hasAC = ac || _hasACDefault;
    calcTaxes(price, curTPDI);
  }
  
  var f = null;
  if(typeof(validateCalc) == 'function') {
    f = document.getElementById("buildercalc");
    if(f != null) {
      validateCalc(f);
    }
  }
}


// #######################################################################
function _getPrice(idx)
{
  // Prices can vary so we need to check the price table

  var i;
  var price = V[idx][0];
  var changed = false;

  for(i = 0; i < V.length; i++) {
    if(i != idx && ch[i] == 1) {
      if(!isNaN(PT[idx][i])) {
        if(PT[idx][i] < price || !changed) {
          price = PT[idx][i];
          changed = true;
        }
      }
    }
  }

  return price;
}


// #######################################################################
function makeArrayWithElement(group, idx)
{
  var g = new Array();

  if(idx < group.length) {
    g[g.length] = group[idx];
  }

  return g;
}

