/*
 * returns:
 *  1 - can build more
 *  0 - cannot build any more
 * -1 - special case
 */

signed int HouseClass::CanBuildHowMany(TechnoTypeClass *item, bool BypassExtras, bool IncludeQueued) {
  if(BypassExtras) {
    goto extras_done;
  }
  if(item->Unbuildable) { // always set to false
    return 0;
  }

  for(int i = 0; i < item->PrerequisiteOverride.Length; ++i) {
    if(this->CurrentlyOwnedBuildingTypes.GetItemCount(item->PrerequisiteOverride[i]) > 0) {
      goto prereqs_done;
    }
  }

  for(int i = 0; i < this->OwnedSecretLabs.Length; ++i) {
    if(this->OwnedSecretLabs[i]->GetSecretProduction() == item) {
      goto prereqs_done;
    }
  }

  if(item->TechLevel == -1) {
    return 0;
  }

  if(item->RequiresStolenAlliedTech && !this->HasStolenAlliedTech) {
    return 0;
  }

  if(item->RequiresStolenSovietTech && !this->HasStolenSovietTech) {
    return 0;
  }

  if(item->RequiresStolenThirdTech && !this->HasStolenThirdTech) {
    return 0;
  }

  if(item->RequiredHouses != -1) {
    if(!((1 << this->Country->IndexInArray) & item->RequiredHouses)) {
      return 0;
    }
  }

  if(item->ForbiddenHouses != -1) {
    if((1 << this->Country->IndexInArray) & item->ForbiddenHouses) {
      return 0;
    }
  }

  if(!GameOptions.SWAllowed && item->WhatAmI() == IS_BUILDINGTYPE) {
    BuildingTypeClass *Type = (BuildingTypeClass*)item;
    if(item->SuperWeapon != -1) { // SW2 is ignored...
      if(!Rules->BuildTech.Includes(item) && vec_SuperWeaponTypes[item->SuperWeapon]->DisableableFromShell)) {
        return 0;
      }
    }
  }

  if(item->TechLevel > this->TechLevel) {
    return 0;
  }

  if(!this->ControlledByHuman && !this->ControlledByPlayer) {
    return 1; // AI cheater!
  }

extras_done:

// fun, part 1 - prerequisite list

  for(int i = 0; i < item->Prerequisite.Length; ++i) {
    // this is magic - non-negative prereqIdx stands for indices in [BuildingTypes], negative for generics
    int prereqIdx = item->Prerequisite[i];
    bool prereqMet = 0;
    switch(prereqIdx) {

      case PREREQ_POWER:
        for(int j = 0; j < Rules->PrerequisitePower.Length; ++j) {
          if(this->CurrentlyOwnedBuildingTypes.GetItemCount(Rules->PrerequisitePower[j]) > 0) {
            prereqMet = 1;
            break;
          }
        }
        break;

      case PREREQ_BARRACKS:
        for(int j = 0; j < Rules->PrerequisiteBarracks.Length; ++j) {
          if(this->CurrentlyOwnedBuildingTypes.GetItemCount(Rules->PrerequisiteBarracks[j]) > 0) {
            prereqMet = 1;
            break;
          }
        }
        break;

      case PREREQ_FACTORY:
        for(int j = 0; j < Rules->PrerequisiteFactory.Length; ++j) {
          if(this->CurrentlyOwnedBuildingTypes.GetItemCount(Rules->PrerequisiteFactory[j]) > 0) {
            prereqMet = 1;
            break;
          }
        }
        break;

      case PREREQ_RADAR:
        for(int j = 0; j < Rules->PrerequisiteRadar.Length; ++j) {
          if(this->CurrentlyOwnedBuildingTypes.GetItemCount(Rules->PrerequisiteRadar[j]) > 0) {
            prereqMet = 1;
            break;
          }
        }
        break;

      case PREREQ_TECH:
        for(int j = 0; j < Rules->PrerequisiteTech.Length; ++j) {
          if(this->CurrentlyOwnedBuildingTypes.GetItemCount(Rules->PrerequisiteTech[j]) > 0) {
            prereqMet = 1;
            break;
          }
        }
        break;

      case PREREQ_PROC:
        for(int j = 0; j < Rules->PrerequisiteProc.Length; ++j) {
          if(this->CurrentlyOwnedBuildingTypes.GetItemCount(Rules->PrerequisiteProc[j]) > 0) {
            prereqMet = 1;
            break;
          }
        }
        if(this->CurrentlyOwnedVehicleTypes.GetItemCount(Rules->PrerequisiteProcAlternate) > 0) {
          prereqMet = 1;
        }
        break;

      default:
        BuildingTypeClass *prereqTest = vec_BuildingTypes[prereqIdx];
        if(prereqTest->PowersUpBuilding) {
          for(int j = this->OwnedBuildings.Length - 1; j; --j) {
            BuildingClass *bld = this->OwnedBuildings[j];
            if(!bld->NotOnMap && bld->PowerOn
             && bld->GetCurrentMission != MISSION_SELLING && bld->QueuedMission != MISSION_SELLING)
            {
              for(int k = 0; k < 3; ++k) {
                if(bld->Upgrades[k] == item) {
                  prereqMet = 1;
                  break;
                }
              }
              break; // !!! what this does is break the loop after checking just the most recently placed building
            }
          }
        } else {
          if(this->CurrentlyOwnedBuildingTypes.GetItemCount(prereqTest) > 0) {
            prereqMet = 1;
          }
        }

    } // end of switch(PrereqIdx)

    if(!prereqMet) {
      return 0;
    }
  } // end of prereq loop

prereqs_done:

  // fun, part 2 - BuildLimit

  CounterClass *counter;

  switch(item->WhatAmI()) {
    case IS_VEHICLE:
      if(item->BuildLimit <= 0) {
        return this->TotalOwnedVehicleTypes->GetItemCount(item) < abs(item->BuildLimit) ? 1 : 0; // just 1
      } else if(this->CurrentlyOwnedVehicleTypes->GetItemCount(item) < item->BuildLimit) {
        return 1;
      } else { // owned == limit
        if(!IncludeQueued) {
          return -1;
        }
        for(int j = 0; j < vec_Factories.Length; ++j) {
          FactoryClass *Fact = vec_Factories[j];
          if(Fact->Owner != this) {
            continue;
          }
          if(Fact->GetProduct()->GetTechnoType() == item) {
            return Fact ? 1 : -1; // don't look at me, I didn't write this... if Fact is a valid pointer
          }
        }
        return -1;
      }

    case IS_INFANTRY:
      if(item->BuildLimit <= 0 && this->TotalOwnedInfantryTypes->GetItemCount(item) >= abs(item->BuildLimit)) {
        return 0;
      } else {
        InfantryTypeClass *infT = (InfantryTypeClass *)item;
        int count = this->CurrentlyOwnedInfantryTypes->GetItemCount(item);
        if(infT->VehicleThief) {
          for(int j = 0; j < vec_Units.Length; ++j) {
            UnitClass *v = vec_Units[j];
            if(v->OwnerHouse == this && v->HijackedByInfantry == infT) {
              ++count;
            }
          }
        }
        if(count < item->BuildLimit) {
          return 1;
        }
        if(!IncludeQueued) {
          return -1;
        }
        for(int j = 0; j < vec_Factories.Length; ++j) {
          FactoryClass *Fact = vec_Factories[j];
          if(Fact->Owner != this) {
            continue;
          }
          if(Fact->GetProduct()->GetTechnoType() == item) {
            return Fact ? 1 : -1; // don't look at me, I didn't write this... if Fact is a valid pointer
          }
        }
        return -1;
      }

    case IS_AIRCRAFT:
      if(item->BuildLimit <= 0) {
        return this->TotalOwnedAircraftTypes->GetItemCount(item) < abs(item->BuildLimit) ? 1 : 0; // just 1
      } else if(this->CurrentlyOwnedAircraftTypes->GetItemCount(item) < item->BuildLimit) {
        return 1;
      } else { // owned == limit
        if(!IncludeQueued) {
          return -1;
        }
        for(int j = 0; j < vec_Factories.Length; ++j) {
          FactoryClass *Fact = vec_Factories[j];
          if(Fact->Owner != this) {
            continue;
          }
          if(Fact->GetProduct()->GetTechnoType() == item) {
            return Fact ? 1 : -1; // don't look at me, I didn't write this... if Fact is a valid pointer
          }
        }
        return -1;
      }

    case IS_BUILDING:
      if(item->BuildLimit <= 0) {
        return this->TotalOwnedBuildingTypes->GetItemCount(item) < abs(item->BuildLimit) ? 1 : 0; // just 1
      } else if(this->CurrentlyOwnedBuildingTypes->GetItemCount(item) < item->BuildLimit) {
        return 1;
      } else { // owned == limit
        if(!IncludeQueued) {
          return -1;
        }
        for(int j = 0; j < vec_Factories.Length; ++j) {
          FactoryClass *Fact = vec_Factories[j];
          if(Fact->Owner != this) {
            continue;
          }
          if(Fact->GetProduct()->GetTechnoType() == item) {
            return Fact ? 1 : -1; // don't look at me, I didn't write this... if Fact is a valid pointer
          }
        }
        return -1;
      }

    default:
      return 1;
  }

}