/***************************************************************************
                          vehicle.h  -  description
                             -------------------
    begin                : Fri Sep 29 2000
    copyright            : (C) 2000 by Martin Bickel
    email                : bickel@asc-hq.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef vehicleH
 #define vehicleH

 #include "typen.h"
 #include "vehicletype.h"
 #include "containerbase.h"
 #include "ascstring.h"

 class Vehicle : public ContainerBase {
    bool cleanRemove;

    //! a vehicle must not be created without a type or map
    Vehicle (  );

    //! cloning of vehicles is not allowed too
    Vehicle ( const Vehicle& v );

    //! is  the vehicle currently viewing the map? if yes, the view has to be removed on destruction
    bool viewOnMap;

    /** remaining movement for this turn.
        Note that the usable movement can be less due to lack of fuel.
        Use #getmovement to optain the usable movement
    */
    int  _movement;

    void init ( void );

    ResourceMatrix repairEfficiency;

    bool generatoractive;

    bool movementLeft() const;

    Vehicle ( const Vehicletype* t, pmap actmap, int player, int networkID  );

   protected:
     bool isBuilding() const { return false; };

   public:

    Vehicle ( const Vehicletype* t, pmap actmap, int player );


    const Vehicletype* typ;
    int          ammo[16];
    int          weapstrength[16];
    int          experience;

    //! did the unit already attack this turn
    bool         attacked;

    //! the the unit and all cargo to #attacked = true
    void setAttacked();

    //! the current level of height ( BITMAPPED ! )
    int          height;

    //! the direction in which the unit is facing
    char         direction;

    //! the position on the map
    int          xpos, ypos;
#ifndef karteneditor
  private:
#endif  
    //! the resources that the unit is carrying
    Resources    tank;
  public:

    //! if events are triggered by this unit (if it is lost for example), this will set connection != 0
    int          connection;

    int          armor;

    /** a unique identification of the unit that is used everywhere in ASC
       (and not only the network protocols where is was first used, hence the name)
    */
    int          networkid;
    ASCString    name;

    //! The class that manages the reaction fire which causes a unit to attack approaching enemies even if it is not the unit's turn
    class  ReactionFire {
         friend class Vehicle;

         Vehicle* unit;
       public:
         ReactionFire ( Vehicle* _unit );
         enum Status { off, init1a, init2, ready, init1b };

         //! for each player that can still be attacked one bit is set
         int enemiesAttackable;
         Status getStatus() const { return status;};

         //! enables the reaction fire or returns an error code ( result < 0 = -errorNr ) 
         int enable();
         void disable();

         vector<int> weaponShots;
         vector<int> nonattackableUnits;

         //! hook that is called when a turn ends
         void endOwnTurn();
         void endAnyTurn();
         void checkData();

         bool canMove() const;
       private:
         Status status;
         void resetShotCount();

    } reactionfire;

    AiParameter* aiparam[8];

    //! can the unit move from its current position (does not check neighbouring fields)
    bool canMove ( void ) const;

    //! returns the movement points the unit has left for this turn. CheckFuel should almost always be true.
    int getMovement ( bool checkFuel = true ) const ;

    /** sets a new distance that the unit can move
        \param cargoDivisor : the cargo of this unit gets 1/cargodivisor the change that this unit is getting; if 0 the cargo is not touched ; -1 is default
    */
    void setMovement ( int newmove, double cargoDivisor = -1 );

    //! did the unit move this turn
    bool hasMoved ( void ) const;

    //! the maximum distance that the unit can drive in a single turn on the current level of height
    int maxMovement ( void );

    /** reduces the movement by the given amount. Negative values are possible.
        Don't use something like "setmovement ( getmovement() - amount )", because getmovement may return a lower amount due to lack of fuel. */
    void decreaseMovement ( int movement );

    //! resets a units movement. This is called at the beginning of each turn.
    void resetMovement( void );

    //! changes a units height and adjusts the movement so that the percentage of used movepoints remains constant
    void setNewHeight( int height );

    //! add the objects like tracks or broken ice
    void spawnMoveObjects( const MapCoordinate& start, const MapCoordinate& dest );

    int putResource ( int amount, int resourcetype, bool queryonly, int scope = 1 );
    int getResource ( int amount, int resourcetype, bool queryonly, int scope = 1 );

    Resources putResource ( const Resources& res, bool queryonly, int scope = 1 ) { return ContainerBase::putResource ( res, queryonly, scope ); };
    Resources getResource ( const Resources& res, bool queryonly, int scope = 1 ) { return ContainerBase::getResource ( res, queryonly, scope ); };

    Resources getTank() { return ContainerBase::getResource(typ->tank,true);};


    //! weight of unit including cargo
    int weight( void ) const;

    //! returns the units name or, if it does not exist, the unit type's name or description
    const ASCString&    getName() const;


  protected:
    const ResourceMatrix& getRepairEfficiency ( void ) { return repairEfficiency; };

    virtual void postRepair ( int oldDamage );

  private:
    /** if a unit (searchedInnerVehicle) is inside a transport, its payload depends also on the transport.
        Since transports can be arbitrarily stacked, this function is used to search the entire
        stack from outmost to the searched inner vehicle */
    int searchstackforfreeweight ( Vehicle* searchedInnerVehicle );

  public:

    //! returns the free weight that can be used for cargo
    int freeWeight();

    //! Returns the size of a unit. A size is equal to the weight of the unit without any cargo or carried resources.
    int size ( void );

    //! hook that is called when a player ends his turn
    void endOwnTurn( void );

    //! hook that is called when any player (including owner) ends turn
    void endAnyTurn( void );


    //! hook that is called the next round begins ( active player switching from player8 to player1 )
    void endRound ( void );

    int getIdentification() { return networkid; };


    //! constructs a vehicle at the given position.
    Vehicle* constructvehicle ( pvehicletype tnk, int x, int y );

    //! checks whether the unit can construct a vehicle of the given type at the given position.
    bool vehicleconstructable ( pvehicletype tnk, int x, int y );

    //! displays the unit at position x/y on the screen
    void putimage ( int x, int y );

    //! sets the unit (and its cargo) the a new position (the unit will not be chained to a field)
    void setnewposition ( int x, int y );
    void setnewposition ( const MapCoordinate& mc );

    /** converts the unit so it is owned by 'player'. Note that the player is passed
        as parameter (values [0..8]), and not his color (which would be the values
        [0,8,16,..,64] )
    */
    void convert ( int player );

    //! sets the status of the unit's energy generator
    void setGeneratorStatus( bool status );
    bool getGeneratorStatus () const { return generatoractive; };

    //! callback that is called after the unit has attacked
    void postAttack();

    /** adds the units view to the map. The view must then be evaluated by functions like #evaluateviewcalculation ( pmap, int)
        \sa viewcalculation.cpp */
    void addview ( void );

    /** removes the units view to the map. The view must then be evaluated by functions like #evaluateviewcalculation ( pmap, int)
        \sa viewcalculation.cpp */
    void removeview ( void );

    /** returns true if the units view is currently added to the maps global visibility.
        \sa viewcalculation.cpp */
    bool isViewing ( ) const { return viewOnMap; };
    const SingleWeapon *getWeapon( unsigned weaponNum );

    /** checks whether the unit can construct a building of the given type.
        This method does not check if there is enough space around the unit to place
        the building */
    bool buildingconstructable ( pbuildingtype bld );

    /** searches for mineral resources.
        \returns > 0 on success ; < 0 on failure (error number is returned => message.txt )
    */
    int searchForMineralResources( void );

    bool searchForMineralResourcesAvailable();

    //! returns the units position
    MapCoordinate3D getPosition ( ) const;

    //! returns the units position; if  inside building then Height is -1
    MapCoordinate3D getPosition3D ( ) const;

    /** can the unit repair anything? This does not necessarily mean that the unit can repair
        other units, because a service weapon is additionally needed for that. It may be the
        case that the unit can only repair itself.
    */
    bool canRepair( const ContainerBase* item );

    /** fills a unit with all resources it can carry and sets it class to "to be determined on map load".
        This function should only be called in the mapeditor !
    */
    void fillMagically( void );


    /** generates a new unit, loads its parameters from the stream (where they have been
        previously written with #write) and returns this new unit.
    */
    static Vehicle* newFromStream ( pmap gamemap, tnstream& stream, int forceNetworkID = -1 );

    /** replaces the parameters of the unit by the ones form the stream. The VehicleType on
        the stream and the current one must be the same!
    */
    void read ( tnstream& stream );

    void write ( tnstream& stream, bool includeLoadedUnits = true );
  private:
    void readData ( tnstream& stream );
  public:

    //! For the AI: calculating the ValueType if the unit was on the height uheight
    int getValueType ( int uheight ) { return log2(uheight); };

    //! For the AI: calculating the ValueType
    int getValueType ( ) { return log2(height); };


    //! to be used with EXTREME caution, and only in the mapeditor !!
    void transform ( const pvehicletype type );

    /** returns the method for changing the height in the specified direction, or none if there is none.
        \param dir  +1 is up;  -1 is down
        \param height if != 0:  assume this level of height instead of the actual one
    */
    const Vehicletype::HeightChangeMethod* getHeightChange( int dir, int height = 0 ) const;

    //! Is the unit able to shoot ?
    bool weapexist ( void );
    void prepareForCleanRemove() { cleanRemove = true; };
    ~Vehicle ( );
};


#endif



