/*
 * This file is part of OpenTTD.
 * OpenTTD 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, version 2.
 * OpenTTD 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. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 */

/** @file extended_ver_sl.h Functions/types related to handling save/load extended version info. */

#ifndef SL_EXTENDED_VER_SL_H
#define SL_EXTENDED_VER_SL_H

#include "../core/bitmath_func.hpp"
#include "../core/enum_type.hpp"

#include <array>
#include <vector>

enum SaveLoadVersion : uint16_t;

/**
 * List of extended features, each feature has its own (16 bit) version
 */
enum SlXvFeatureIndex {
	XSLFI_NULL                          = 0,      ///< Unused value, to indicate that no extended feature test is in use
	XSLFI_VERSION_LABEL,                          ///< Version label
	XSLFI_UPSTREAM_VERSION,                       ///< Corresponding upstream savegame version
	XSLFI_TRACE_RESTRICT,                         ///< Trace restrict
	XSLFI_TRACE_RESTRICT_OWNER,                   ///< Trace restrict: train owner test
	XSLFI_TRACE_RESTRICT_ORDRCND,                 ///< Trace restrict: slot conditional order
	XSLFI_TRACE_RESTRICT_STATUSCND,               ///< Trace restrict: train status condition
	XSLFI_TRACE_RESTRICT_REVERSE,                 ///< Trace restrict: reverse
	XSLFI_TRACE_RESTRICT_NEWSCTRL,                ///< Trace restrict: news control
	XSLFI_TRACE_RESTRICT_COUNTER,                 ///< Trace restrict: counters
	XSLFI_TRACE_RESTRICT_TIMEDATE,                ///< Trace restrict: time/date
	XSLFI_TRACE_RESTRICT_BRKCND,                  ///< Trace restrict: realistic braking related conditionals
	XSLFI_TRACE_RESTRICT_CTGRYCND,                ///< Trace restrict: category conditionals
	XSLFI_TRACE_RESTRICT_PENCTRL,                 ///< Trace restrict: PF penalty control
	XSLFI_TRACE_RESTRICT_TUNBRIDGE,               ///< Trace restrict: restricted signalled tunnel/bridge support
	XSLFI_TRACE_RESTRICT_SPDADAPTCTRL,            ///< Trace restrict: speed adaptation control
	XSLFI_PROG_SIGS,                              ///< programmable pre-signals patch
	XSLFI_ADJACENT_CROSSINGS,                     ///< Adjacent level crossings closure patch
	XSLFI_SAFER_CROSSINGS,                        ///< Safer level crossings
	XSLFI_DEPARTURE_BOARDS,                       ///< Departure boards patch, in ticks mode
	XSLFI_TIMETABLES_START_TICKS,                 ///< Timetable start time format: 1: is in ticks, instead of days, 2: also has subticks, 3: uses StateTicks
	XSLFI_TOWN_CARGO_ADJ,                         ///< Town cargo adjustment patch
	XSLFI_SIG_TUNNEL_BRIDGE,                      ///< Signals on tunnels and bridges
	XSLFI_IMPROVED_BREAKDOWNS,                    ///< Improved breakdowns patch
	XSLFI_CONSIST_BREAKDOWN_FLAG,                 ///< Consist breakdown flag
	XSLFI_TT_WAIT_IN_DEPOT,                       ///< Timetabling waiting time in depot patch
	XSLFI_AUTO_TIMETABLE,                         ///< Auto timetables and separation patch
	XSLFI_VEHICLE_REPAIR_COST,                    ///< Vehicle repair costs patch
	XSLFI_ENH_VIEWPORT_PLANS,                     ///< Enhanced viewport patch: plans
	XSLFI_INFRA_SHARING,                          ///< Infrastructure sharing patch
	XSLFI_VARIABLE_DAY_LENGTH,                    ///< Variable day length patch
	XSLFI_ORDER_OCCUPANCY,                        ///< Running average of order occupancy
	XSLFI_MORE_COND_ORDERS,                       ///< More conditional orders patch
	XSLFI_EXTRA_LARGE_MAP,                        ///< Extra large map
	XSLFI_REVERSE_AT_WAYPOINT,                    ///< Reverse at waypoint orders
	XSLFI_VEH_LIFETIME_PROFIT,                    ///< Vehicle lifetime profit patch
	XSLFI_LINKGRAPH_DAY_SCALE,                    ///< Linkgraph job duration & interval may be in non-scaled days
	XSLFI_TEMPLATE_REPLACEMENT,                   ///< Template-based train replacement
	XSLFI_MORE_RAIL_TYPES,                        ///< Increased number of rail types
	XSLFI_CARGO_TYPE_ORDERS,                      ///< Cargo-specific load/unload order flags
	XSLFI_EXTENDED_GAMELOG,                       ///< Extended gamelog
	XSLFI_STATION_CATCHMENT_INC,                  ///< Station catchment radius increase
	XSLFI_CUSTOM_BRIDGE_HEADS,                    ///< Custom bridge heads
	XSLFI_CHUNNEL,                                ///< Tunnels under water (channel tunnel)
	XSLFI_SCHEDULED_DISPATCH,                     ///< Scheduled vehicle dispatching
	XSLFI_MORE_TOWN_GROWTH_RATES,                 ///< More town growth rates
	XSLFI_MULTIPLE_DOCKS,                         ///< Multiple docks
	XSLFI_TIMETABLE_EXTRA,                        ///< Vehicle timetable extra fields
	XSLFI_TRAIN_FLAGS_EXTRA,                      ///< Train flags field extra size
	XSLFI_VEHICLE_FLAGS_EXTRA,                    ///< Vehicle flags field extra size
	XSLFI_TRAIN_THROUGH_LOAD,                     ///< Train through load/unload
	XSLFI_ORDER_EXTRA_DATA,                       ///< Order extra data field(s)
	XSLFI_WHOLE_MAP_CHUNK,                        ///< Whole map chunk
	XSLFI_ST_LAST_VEH_TYPE,                       ///< Per-cargo station last vehicle type
	XSLFI_SELL_AT_DEPOT_ORDER,                    ///< Sell vehicle on arrival at depot orders
	XSLFI_BUY_LAND_RATE_LIMIT,                    ///< Buy land rate limit
	XSLFI_DUAL_RAIL_TYPES,                        ///< Two rail-types per tile
	XSLFI_CONSIST_SPEED_RD_FLAG,                  ///< Consist speed reduction flag
	XSLFI_SAVEGAME_UNIQUE_ID,                     ///< Savegame unique ID
	XSLFI_RV_OVERTAKING,                          ///< Roadvehicle overtaking
	XSLFI_LINKGRAPH_MODES,                        ///< Linkgraph additional distribution modes
	XSLFI_GAME_EVENTS,                            ///< Game event flags
	XSLFI_ROAD_LAYOUT_CHANGE_CTR,                 ///< Road layout change counter
	XSLFI_TOWN_CARGO_MATRIX,                      ///< Town cargo matrix savegame format changes (now obsolete)
	XSLFI_STATE_CHECKSUM,                         ///< State checksum
	XSLFI_DEBUG,                                  ///< Debugging info
	XSLFI_FLOW_STAT_FLAGS,                        ///< FlowStat flags
	XSLFI_SPEED_RESTRICTION,                      ///< Train speed restrictions
	XSLFI_STATION_GOODS_EXTRA,                    ///< Extra station goods entry statuses
	XSLFI_DOCKING_CACHE_VER,                      ///< Multiple docks - docking tile cache version
	XSLFI_EXTRA_CHEATS,                           ///< Extra cheats
	XSLFI_TOWN_MULTI_BUILDING,                    ///< Allow multiple stadium/church buildings in a single town
	XSLFI_SHIP_LOST_COUNTER,                      ///< Ship lost counter
	XSLFI_BUILD_OBJECT_RATE_LIMIT,                ///< Build object rate limit
	XSLFI_LOCAL_COMPANY,                          ///< Local company ID
	XSLFI_THROUGH_TRAIN_DEPOT,                    ///< Drive-through train depots
	XSLFI_MORE_VEHICLE_ORDERS,                    ///< More vehicle orders - VehicleOrderID is 16 bits instead of 8
	XSLFI_ORDER_FLAGS_EXTRA,                      ///< Order flags field extra size
	XSLFI_ONE_WAY_DT_ROAD_STOP,                   ///< One-way drive-through road stops
	XSLFI_ONE_WAY_ROAD_STATE,                     ///< One-way road state cache
	XSLFI_VENC_CHUNK,                             ///< VENC chunk
	XSLFI_ANIMATED_TILE_EXTRA,                    ///< Animated tile extra info
	XSLFI_NEWGRF_INFO_EXTRA,                      ///< Extra NewGRF info in savegame
	XSLFI_INDUSTRY_CARGO_ADJ,                     ///< Industry cargo adjustment patch
	XSLFI_REALISTIC_TRAIN_BRAKING,                ///< Realistic train braking
	XSLFI_INFLATION_FIXED_DATES,                  ///< Inflation is applied between fixed dates
	XSLFI_WATER_FLOODING,                         ///< Water flooding map bit
	XSLFI_MORE_HOUSES,                            ///< More house types
	XSLFI_CUSTOM_TOWN_ZONE,                       ///< Custom town zones
	XSLFI_STATION_CARGO_HISTORY,                  ///< Station waiting cargo history
	XSLFI_TRAIN_SPEED_ADAPTATION,                 ///< Train speed adaptation
	XSLFI_EXTRA_STATION_NAMES,                    ///< Extra station names
	XSLFI_DEPOT_ORDER_EXTRA_FLAGS,                ///< Depot order extra flags
	XSLFI_EXTRA_SIGNAL_TYPES,                     ///< Extra signal types
	XSLFI_BANKRUPTCY_EXTRA,                       ///< Extra company bankruptcy fields
	XSLFI_OBJECT_GROUND_TYPES,                    ///< Object ground types
	XSLFI_LINKGRAPH_AIRCRAFT,                     ///< Link graph last aircraft update field and aircraft link scaling setting
	XSLFI_COMPANY_PW,                             ///< Company passwords
	XSLFI_ST_INDUSTRY_CARGO_MODE,                 ///< Station industry cargo mode setting
	XSLFI_TL_SPEED_LIMIT,                         ///< Through load maximum speed setting
	XSLFI_RAIL_DEPOT_SPEED_LIMIT,                 ///< Rail depot maximum speed setting
	XSLFI_WAYPOINT_FLAGS,                         ///< Waypoint flags
	XSLFI_ROAD_WAYPOINTS,                         ///< Road waypoints
	XSLFI_MORE_STATION_TYPES,                     ///< More station types (field widening)
	XSLFI_RV_ORDER_EXTRA_FLAGS,                   ///< Road vehicle order extra flags
	XSLFI_GRF_ROADSTOPS,                          ///< NewGRF road stops
	XSLFI_INDUSTRY_ANIM_MASK,                     ///< Industry tile animation masking
	XSLFI_NEW_SIGNAL_STYLES,                      ///< New signal styles
	XSLFI_NO_TREE_COUNTER,                        ///< No tree counter
	XSLFI_TOWN_SETTING_OVERRIDE,                  ///< Town setting overrides
	XSLFI_LINKGRAPH_SPARSE_EDGES,                 ///< Link graph edge matrix is stored in sparse format, and saved in order
	XSLFI_AUX_TILE_LOOP,                          ///< Auxiliary tile loop
	XSLFI_NEWGRF_ENTITY_EXTRA,                    ///< NewGRF entity mappings are 16 bit
	XSLFI_TNNC_CHUNK,                             ///< TNNC chunk
	XSLFI_MULTI_CARGO_SHIPS,                      ///< Multi-cargo ships
	XSLFI_REMAIN_NEXT_ORDER_STATION,              ///< Remain in station if next order is for same station
	XSLFI_LABEL_ORDERS,                           ///< Label orders
	XSLFI_VARIABLE_TICK_RATE,                     ///< Variable tick rate
	XSLFI_ROAD_VEH_FLAGS,                         ///< Road vehicle flags
	XSLFI_STATION_TILE_CACHE_FLAGS,               ///< Station tile cache flags
	XSLFI_INDUSTRY_CARGO_TOTALS,                  ///< Industry cargo totals are 32 bit

	XSLFI_SCRIPT_INT64,                           ///< See: SLV_SCRIPT_INT64
	XSLFI_U64_TICK_COUNTER,                       ///< See: SLV_U64_TICK_COUNTER
	XSLFI_LINKGRAPH_TRAVEL_TIME,                  ///< See: SLV_LINKGRAPH_TRAVEL_TIME
	XSLFI_LAST_LOADING_TICK,                      ///< See: SLV_LAST_LOADING_TICK
	XSLFI_SCRIPT_LEAGUE_TABLES,                   ///< See: Scriptable league tables (PR #10001)
	XSLFI_VELOCITY_NAUTICAL,                      ///< See: SLV_VELOCITY_NAUTICAL (PR #10594)
	XSLFI_CONSISTENT_PARTIAL_Z,                   ///< See: SLV_CONSISTENT_PARTIAL_Z (PR #10570)
	XSLFI_MORE_CARGO_AGE,                         ///< See: SLV_MORE_CARGO_AGE (PR #10596)
	XSLFI_AI_START_DATE,                          ///< See: SLV_AI_START_DATE (PR #10653)
	XSLFI_EXTEND_VEHICLE_RANDOM,                  ///< See: SLV_EXTEND_VEHICLE_RANDOM (PR #10701)
	XSLFI_DISASTER_VEH_STATE,                     ///< See: SLV_DISASTER_VEH_STATE (PR #10798)
	XSLFI_SAVEGAME_ID,                            ///< See: SLV_SAVEGAME_ID (PR #10719)
	XSLFI_NEWGRF_LAST_SERVICE,                    ///< See: SLV_NEWGRF_LAST_SERVICE (PR #11124)
	XSLFI_CARGO_TRAVELLED,                        ///< See: SLV_CARGO_TRAVELLED (PR #11283)
	XSLFI_SHIP_ACCELERATION,                      ///< See: SLV_SHIP_ACCELERATION (PR #10734)
	XSLFI_DEPOT_UNBUNCHING,                       ///< See: SLV_DEPOT_UNBUNCHING (PR #11945)
	XSLFI_VEHICLE_ECONOMY_AGE,                    ///< See: SLV_VEHICLE_ECONOMY_AGE (PR #12141)

	XSLFI_TABLE_PATS,                             ///< Use upstream table format for PATS
	XSLFI_TABLE_MISC_SL,                          ///< Use upstream table format for miscellaneous chunks:
	                                              ///<     v1: DATE, VIEW, MAPS
	                                              ///<     v2: SUBS, CMDL, CMPU, ERNW, DEPT, CAPY, ECMY, EIDS, ENGN, GOAL, GRPS, RAIL, OBJS, SIGN, PSAC, STPE, STPA
	XSLFI_TABLE_SCRIPT_SL,                        ///< Use upstream table format for script chunks
	XSLFI_TABLE_NEWGRF_SL,                        ///< Use upstream table format for NewGRF/ID mapping chunks
	XSLFI_TABLE_INDUSTRY_SL,                      ///< Use upstream table format for industry chunks: IBLD, ITBL

	XSLFI_RIFF_HEADER_60_BIT,                     ///< Size field in RIFF chunk header is 60 bit
	XSLFI_HEIGHT_8_BIT,                           ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk
	XSLFI_ZPOS_32_BIT,                            ///< Vehicle/sign z_pos is 32 bit instead of 8 bit, but savegame version may be before this became true in trunk
	XSLFI_MIGHT_USE_PAX_SIGNALS,                  ///< This save game might use the pax-signals feature
	XSLFI_TRAFFIC_LIGHTS,                         ///< This save game uses road traffic lights
	XSLFI_RAIL_AGEING,                            ///< This save game uses the rail aging patch
	XSLFI_SPRINGPP,                               ///< This is a SpringPP game, use this for loading some settings
	XSLFI_JOKERPP,                                ///< This is a JokerPP game, use this for loading some settings
	XSLFI_CHILLPP,                                ///< This is a ChillPP game, use this for loading some settings

	XSLFI_SIZE,                                   ///< Total count of features, including null feature
};

extern std::array<uint16_t, XSLFI_SIZE> _sl_xv_feature_versions;
extern std::array<uint16_t, XSLFI_SIZE> _sl_xv_feature_static_versions;

/**
 * Operator to use when combining traditional savegame number test with an extended feature version test
 */
enum SlXvFeatureTestOperator {
	XSLFTO_OR                           = 0,      ///< Test if traditional savegame version is in bounds OR extended feature is in version bounds
	XSLFTO_AND                                    ///< Test if traditional savegame version is in bounds AND extended feature is in version bounds
};

/**
 * Structure to describe an extended feature version test, and how it combines with a traditional savegame version test
 */
struct SlXvFeatureTest {
	using TestFunctorPtr = bool (*)(uint16_t, bool, const std::array<uint16_t, XSLFI_SIZE> &);  ///< Return true if feature present, first parameter is standard savegame version, second is whether standard savegame version is within bounds

private:
	uint16_t min_version;
	uint16_t max_version;
	SlXvFeatureIndex feature;
	SlXvFeatureTestOperator op;
	TestFunctorPtr functor = nullptr;

public:
	constexpr SlXvFeatureTest()
			: min_version(0), max_version(0), feature(XSLFI_NULL), op(XSLFTO_OR) { }

	constexpr SlXvFeatureTest(SlXvFeatureTestOperator op_, SlXvFeatureIndex feature_, uint16_t min_version_ = 1, uint16_t max_version_ = 0xFFFF)
			: min_version(min_version_), max_version(max_version_), feature(feature_), op(op_) { }

	constexpr SlXvFeatureTest(TestFunctorPtr functor_)
			: min_version(0), max_version(0), feature(XSLFI_NULL), op(XSLFTO_OR), functor(functor_) { }

	bool IsFeaturePresent(const std::array<uint16_t, XSLFI_SIZE> &feature_versions, SaveLoadVersion savegame_version, SaveLoadVersion savegame_version_from, SaveLoadVersion savegame_version_to) const;

	inline bool IsFeaturePresent(SaveLoadVersion savegame_version, SaveLoadVersion savegame_version_from, SaveLoadVersion savegame_version_to) const
	{
		return this->IsFeaturePresent(_sl_xv_feature_versions, savegame_version, savegame_version_from, savegame_version_to);
	}
};

bool SlXvIsFeaturePresent(const std::array<uint16_t, XSLFI_SIZE> &feature_versions, SlXvFeatureIndex feature, uint16_t min_version = 1, uint16_t max_version = 0xFFFF);

inline bool SlXvIsFeaturePresent(SlXvFeatureIndex feature, uint16_t min_version = 1, uint16_t max_version = 0xFFFF)
{
	return SlXvIsFeaturePresent(_sl_xv_feature_versions, feature, min_version, max_version);
}

/**
 * Returns true if @p feature is missing (i.e. has a version of 0, or less than the specified minimum version)
 */
inline bool SlXvIsFeatureMissing(SlXvFeatureIndex feature, uint16_t min_version = 1)
{
	return !SlXvIsFeaturePresent(feature, min_version);
}

/**
 * Returns true if @p feature is missing (i.e. has a version of 0, or less than the specified minimum version)
 */
inline bool SlXvIsFeatureMissing(const std::array<uint16_t, XSLFI_SIZE> &feature_versions, SlXvFeatureIndex feature, uint16_t min_version = 1)
{
	return !SlXvIsFeaturePresent(feature_versions, feature, min_version);
}

const char *SlXvGetFeatureName(SlXvFeatureIndex feature);

/**
 * sub chunk flags, this is saved as-is
 * (XSCF_EXTRA_DATA_PRESENT and XSCF_CHUNK_ID_LIST_PRESENT must only be set by the save code, and read by the load code)
 */
enum SlxiSubChunkFlags {
	XSCF_NULL                     = 0,       ///< zero value
	XSCF_IGNORABLE_UNKNOWN        = 1 << 0,  ///< the loader is free to ignore this without aborting the load if it doesn't know what it is at all
	XSCF_IGNORABLE_VERSION        = 1 << 1,  ///< the loader is free to ignore this without aborting the load if the version is greater than the maximum that can be loaded
	XSCF_EXTRA_DATA_PRESENT       = 1 << 2,  ///< extra data field is present, extra data in some sub-chunk/feature specific format
	XSCF_CHUNK_ID_LIST_PRESENT    = 1 << 3,  ///< chunk ID list field is present, list of chunks which this sub-chunk/feature adds to the save game, this can be used to discard the chunks if the feature is unknown

	XSCF_IGNORABLE_ALL            = XSCF_IGNORABLE_UNKNOWN | XSCF_IGNORABLE_VERSION, ///< all "ignorable" flags
};
DECLARE_ENUM_AS_BIT_SET(SlxiSubChunkFlags)

struct SlxiSubChunkInfo;

typedef uint32_t SlxiSubChunkSaveProc(const SlxiSubChunkInfo *info, bool dry_run);  ///< sub chunk save procedure type, must return length and write no data when dry_run is true
typedef void SlxiSubChunkLoadProc(const SlxiSubChunkInfo *info, uint32_t length);   ///< sub chunk load procedure, must consume length bytes

/** Handlers and description of chunk. */
struct SlxiSubChunkInfo {
	SlXvFeatureIndex index;                       ///< feature index, this is saved
	SlxiSubChunkFlags flags;                      ///< flags, this is saved
	uint16_t save_version;                        ///< version to save
	uint16_t max_version;                         ///< maximum version to accept on load
	const char *name;                             ///< feature name, this *IS* saved, so must be globally unique
	SlxiSubChunkSaveProc *save_proc;              ///< save procedure of the sub chunk, this may be nullptr in which case no extra chunk data is saved
	SlxiSubChunkLoadProc *load_proc;              ///< load procedure of the sub chunk, this may be nullptr in which case the extra chunk data must be missing or of 0 length
	const char *chunk_list;                       ///< this is a list of chunks that this feature uses, which should be written to the savegame, this must be a comma-seperated list of 4-character IDs, with no spaces, or nullptr
};

void SlXvResetState();

void SlXvSetCurrentState();
void SlXvSetStaticCurrentVersions();

bool SlXvCheckSpecialSavegameVersions();

bool SlXvIsChunkDiscardable(uint32_t id);

#endif /* SL_EXTENDED_VER_SL_H */
