/********************************************************************** 
 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
   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, 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.
***********************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>

#include "fcintl.h"
#include "game.h"
#include "log.h"
#include "map.h"
#include "mem.h"
#include "shared.h" /* ARRAY_SIZE */
#include "support.h"
#include "tech.h"

#include "improvement.h"

static const char *genus_names[IG_LAST] = {
  "GreatWonder", "SmallWonder", "Improvement", "Special"
};

static const char *flag_names[] = {
  "VisibleByOthers", "SaveSmallWonder", "Gold"
};
/* Note that these strings must correspond with the enums in impr_flag_id,
   in common/improvement.h */

/**************************************************************************
All the city improvements:
Use improvement_by_number(id) to access the array.
The improvement_types array is now setup in:
   server/ruleset.c (for the server)
   client/packhand.c (for the client)
**************************************************************************/
static struct impr_type improvement_types[B_LAST];

/**************************************************************************
  Convert impr genus names to enum; case insensitive;
  returns IG_LAST if can't match.
**************************************************************************/
enum impr_genus_id find_genus_by_rule_name(const char *s)
{
  enum impr_genus_id i;

  for(i = 0; i < ARRAY_SIZE(genus_names); i++) {
    if (mystrcasecmp(genus_names[i], s) == 0) {
      break;
    }
  }
  return i;
}

/****************************************************************************
  Inialize building structures.
****************************************************************************/
void improvements_init(void)
{
  int i;

  /* Can't use impr_type_iterate or improvement_by_number here because
   * num_impr_types isn't known yet. */
  for (i = 0; i < ARRAY_SIZE(improvement_types); i++) {
    struct impr_type *p = &improvement_types[i];

    p->index = i;
    requirement_vector_init(&p->reqs);
  }
}

/**************************************************************************
  Frees the memory associated with this improvement.
**************************************************************************/
static void improvement_free(Impr_type_id id)
{
  struct impr_type *p = improvement_by_number(id);

  free(p->helptext);
  p->helptext = NULL;

  requirement_vector_free(&p->reqs);
}

/***************************************************************
 Frees the memory associated with all improvements.
***************************************************************/
void improvements_free()
{
  impr_type_iterate(impr) {
    improvement_free(impr);
  } impr_type_iterate_end;
}

/**************************************************************************
Returns 1 if the improvement_type "exists" in this game, 0 otherwise.
An improvement_type doesn't exist if one of:
- id is out of range;
- the improvement_type has been flagged as removed by setting its
  tech_req to A_LAST;
- it is a space part, and the spacerace is not enabled.
Arguably this should be called improvement_type_exists, but that's too long.
**************************************************************************/
bool improvement_exists(Impr_type_id id)
{
  if (id < 0 || id >= B_LAST || id>= game.control.num_impr_types)
    return FALSE;

  if (!game.info.spacerace
      && (building_has_effect(id, EFT_SS_STRUCTURAL)
	  || building_has_effect(id, EFT_SS_COMPONENT)
	  || building_has_effect(id, EFT_SS_MODULE))) {
    /* This assumes that space parts don't have any other effects. */
    return FALSE;
  }

  return TRUE;
}

/**************************************************************************
  Returns the improvement type for the given index/ID.  Returns NULL for
  an out-of-range index.
**************************************************************************/
struct impr_type *improvement_by_number(const Impr_type_id id)
{
  if (id < 0 || id >= game.control.num_impr_types) {
    return NULL;
  }
  return &improvement_types[id];
}

/**************************************************************************
  Return the (translated) name of the given improvement. 
  You don't have to free the return pointer.
**************************************************************************/
const char *improvement_name_translation(Impr_type_id id)
{
  struct impr_type *itp = improvement_by_number(id);

  if (NULL == itp->name.translated) {
    /* delayed (unified) translation */
    itp->name.translated = ('\0' == itp->name.vernacular[0])
			   ? itp->name.vernacular
			   : Q_(itp->name.vernacular);
  }
  return itp->name.translated;
}

/****************************************************************************
  Return the (untranslated) rule name of the improvement.
  You don't have to free the return pointer.
****************************************************************************/
const char *improvement_rule_name(Impr_type_id id)
{
  return Qn_(improvement_by_number(id)->name.vernacular); 
}

/****************************************************************************
  Returns the number of shields it takes to build this improvement.
****************************************************************************/
int impr_build_shield_cost(Impr_type_id id)
{
  int base = improvement_by_number(id)->build_cost;

  return MAX(base * game.info.shieldbox / 100, 1);
}

/****************************************************************************
  Returns the amount of gold it takes to rush this improvement.
****************************************************************************/
int impr_buy_gold_cost(Impr_type_id id, int shields_in_stock)
{
  int cost = 0;
  const int missing = impr_build_shield_cost(id) - shields_in_stock;

  if (improvement_has_flag(id, IF_GOLD)) {
    /* Can't buy capitalization. */
    return 0;
  }

  if (missing > 0) {
    cost = 2 * missing;
  }

  if (is_wonder(id)) {
    cost *= 2;
  }
  if (shields_in_stock == 0) {
    cost *= 2;
  }
  return cost;
}

/****************************************************************************
  Returns the amount of gold received when this improvement is sold.
****************************************************************************/
int impr_sell_gold(Impr_type_id id)
{
  return impr_build_shield_cost(id);
}

/**************************************************************************
...
**************************************************************************/
bool is_wonder(Impr_type_id id)
{
  return (is_great_wonder(id) || is_small_wonder(id));
}

/**************************************************************************
  Does a linear search of improvement_types[].name.translated
  Returns B_LAST if none match.
**************************************************************************/
Impr_type_id find_improvement_by_translated_name(const char *s)
{
  impr_type_iterate(i) {
    if (0 == strcmp(improvement_name_translation(i), s)) {
      return i;
    }
  } impr_type_iterate_end;

  return B_LAST;
}

/****************************************************************************
  Does a linear search of improvement_types[].name.vernacular
  Returns B_LAST if none match.
****************************************************************************/
Impr_type_id find_improvement_by_rule_name(const char *s)
{
  const char *qs = Qn_(s);

  impr_type_iterate(i) {
    if (0 == mystrcasecmp(improvement_rule_name(i), qs)) {
      return i;
    }
  } impr_type_iterate_end;

  return B_LAST;
}

/**************************************************************************
 Return TRUE if the impr has this flag otherwise FALSE
**************************************************************************/
bool improvement_has_flag(Impr_type_id id, enum impr_flag_id flag)
{
  assert(flag >= 0 && flag < IF_LAST);
  return TEST_BIT(improvement_by_number(id)->flags, flag);
}

/**************************************************************************
 Convert flag names to enum; case insensitive;
 returns IF_LAST if can't match.
**************************************************************************/
enum impr_flag_id find_improvement_flag_by_rule_name(const char *s)
{
  enum impr_flag_id i;

  assert(ARRAY_SIZE(flag_names) == IF_LAST);
  
  for(i = 0; i < IF_LAST; i++) {
    if (mystrcasecmp(flag_names[i], s) == 0) {
      return i;
    }
  }
  return IF_LAST;
}

/**************************************************************************
 Return TRUE if the improvement should be visible to others without spying
**************************************************************************/
bool is_improvement_visible(Impr_type_id id)
{
  return (is_wonder(id) || improvement_has_flag(id, IF_VISIBLE_BY_OTHERS));
}

/**************************************************************************
 Returns 1 if the improvement is obsolete, now also works for wonders
**************************************************************************/
bool improvement_obsolete(const struct player *pplayer, Impr_type_id id) 
{
  struct impr_type *impr = improvement_by_number(id);

  if (!tech_exists(impr->obsolete_by)) {
    return FALSE;
  }

  if (is_great_wonder(id)) {
    /* a great wonder is obsolete, as soon as *any* player researched the
       obsolete tech */
   return game.info.global_advances[impr->obsolete_by];
  }

  return (get_invention(pplayer, impr->obsolete_by) == TECH_KNOWN);
}

/**************************************************************************
  Returns TRUE iff improvement provides units buildable by player
**************************************************************************/
bool impr_provides_buildable_units(const struct player *pplayer,
                                   Impr_type_id id)
{
  struct impr_type *impr = improvement_by_number(id);

  /* Fast check */
  if (! impr->allows_units) {
    return FALSE;
  }

  unit_type_iterate(ut) {
    if (ut->impr_requirement == id) {
      if (can_player_build_unit(pplayer, ut)) {
        return TRUE;
      }
    }
  } unit_type_iterate_end;

  return FALSE;
}

/**************************************************************************
   Whether player can build given building somewhere, ignoring whether it
   is obsolete.
**************************************************************************/
bool can_player_build_improvement_direct(const struct player *p,
					 Impr_type_id id)
{
  struct impr_type *impr;
  bool space_part = FALSE;

  /* This also checks if tech req is Never */
  if (!improvement_exists(id)) {
    return FALSE;
  }

  impr = improvement_by_number(id);

  requirement_vector_iterate(&impr->reqs, preq) {
    if (preq->range >= REQ_RANGE_PLAYER
        && !is_req_active(p, NULL, NULL, NULL, NULL, NULL, NULL, preq,
                          RPT_CERTAIN)) {
      return FALSE;
    }
  } requirement_vector_iterate_end;

  /* Check for space part construction.  This assumes that space parts have
   * no other effects. */
  if (building_has_effect(id, EFT_SS_STRUCTURAL)) {
    space_part = TRUE;
    if (p->spaceship.structurals >= NUM_SS_STRUCTURALS) {
      return FALSE;
    }
  }
  if (building_has_effect(id, EFT_SS_COMPONENT)) {
    space_part = TRUE;
    if (p->spaceship.components >= NUM_SS_COMPONENTS) {
      return FALSE;
    }
  }
  if (building_has_effect(id, EFT_SS_MODULE)) {
    space_part = TRUE;
    if (p->spaceship.modules >= NUM_SS_MODULES) {
      return FALSE;
    }
  }
  if (space_part &&
      (!get_player_bonus(p, EFT_ENABLE_SPACE) > 0
       || p->spaceship.state >= SSHIP_LAUNCHED)) {
    return FALSE;
  }

  if (is_great_wonder(id)) {
    /* Can't build wonder if already built */
    if (great_wonder_was_built(id)) {
      return FALSE;
    }
  }

  return TRUE;
}

/**************************************************************************
  Whether player can _eventually_ build given building somewhere -- i.e.,
  returns TRUE if building is available with current tech OR will be
  available with future tech.  Returns FALSE if building is obsolete.
**************************************************************************/
bool can_player_build_improvement(const struct player *p, Impr_type_id id)
{
  if (!can_player_build_improvement_direct(p, id)) {
    return FALSE;
  }
  if (improvement_obsolete(p, id)) {
    return FALSE;
  }
  return TRUE;
}

/**************************************************************************
  Whether player can _eventually_ build given building somewhere -- i.e.,
  returns TRUE if building is available with current tech OR will be
  available with future tech.  Returns FALSE if building is obsolete.
**************************************************************************/
bool can_player_eventually_build_improvement(const struct player *p,
					     Impr_type_id id)
{
  struct impr_type *building;

  if (!improvement_exists(id)) {
    return FALSE;
  }
  if (improvement_obsolete(p, id)) {
    return FALSE;
  }

  /* Check for requirements that aren't met and that are unchanging (so
   * they can never be met). */
  building = improvement_by_number(id);
  requirement_vector_iterate(&building->reqs, preq) {
    if (preq->range >= REQ_RANGE_PLAYER
	&& is_req_unchanging(preq)
        && !is_req_active(p, NULL, NULL, NULL, NULL, NULL, NULL, preq,
                          RPT_POSSIBLE)) {
      return FALSE;
    }
  } requirement_vector_iterate_end;
  /* FIXME: should check some "unchanging" reqs here - like if there's
   * a nation requirement, we can go ahead and check it now. */

  return TRUE;
}

/**************************************************************************
  Is this building a great wonder?
**************************************************************************/
bool is_great_wonder(Impr_type_id id)
{
  return (improvement_by_number(id)->genus == IG_GREAT_WONDER);
}

/**************************************************************************
  Is this building a small wonder?
**************************************************************************/
bool is_small_wonder(Impr_type_id id)
{
  return (improvement_by_number(id)->genus == IG_SMALL_WONDER);
}

/**************************************************************************
  Is this building a regular improvement?
**************************************************************************/
bool is_improvement(Impr_type_id id)
{
  return (improvement_by_number(id)->genus == IG_IMPROVEMENT);
}

/**************************************************************************
  Get the world city with this great wonder.
**************************************************************************/
struct city *find_city_from_great_wonder(Impr_type_id id)
{
  return (game_find_city_by_number(game.info.great_wonders[id]));
}

/**************************************************************************
  Get the player owning this small wonder.
**************************************************************************/
struct player *great_wonder_owner(Impr_type_id id)
{
  struct city *pcity = find_city_from_great_wonder(id);

  if (!pcity) {
    return NULL;
  }

  return city_owner(pcity);
}

/**************************************************************************
  Get the player city with this small wonder.
**************************************************************************/
struct city *find_city_from_small_wonder(const struct player *pplayer,
					 Impr_type_id id)
{
  if (!pplayer) {
    return NULL; /* Used in some places in the client. */
  }
  return (player_find_city_by_id(pplayer, pplayer->small_wonders[id]));
}

/**************************************************************************
  Was this great wonder built?
**************************************************************************/
bool great_wonder_was_built(Impr_type_id id)
{
  return (game.info.great_wonders[id] != 0);
}

/**************************************************************************
  Return TRUE iff the improvement can be sold.
**************************************************************************/
bool can_sell_building(Impr_type_id id)
{
  return is_improvement(id);
}

/****************************************************************************
  Return TRUE iff the city can sell the given improvement.
****************************************************************************/
bool can_city_sell_building(struct city *pcity, Impr_type_id id)
{
  return (city_got_building(pcity, id) ? can_sell_building(id) : FALSE);
}

