/*
* workmanager.h
*
* Copyright (C) 2001-2002 Atomic Blue (info@planeshift.it, http://www.atomicblue.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 (version 2 of the License)
* 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.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
*/

#ifndef __WORKMANAGER_H__
#define __WORKMANAGER_H__

#include "msgmanager.h"             // Parent class
#include "net/messages.h"           // Message definitions
#include "net/msghandler.h"         // Network access
#include <csutil/sysfunc.h>
#include "gem.h"

class psWorkGameEvent;
class psWorkManager;
class psItem;
class Client;

// Define the work event types
#define MANUFACTURE 0                   // Digging, collecting, farming
#define PRODUCTION  1                   // Crafting
#define LOCKPICKING 2                   // Picking of locks
#define CLEANUP     3                   // Cleaning up of public containers
#define REPAIR      4                   // Repairing items takes time too

#define GARBAGE_PATTERNID   1           // Define for hard coded pattern IDs
#define CLEANUP_DELAY       600         // Seconds to wait before performing cleanup event
//#define CLEANUP_DELAY       10 // for testing only

/** Holds the possible return values for a test to see if an item is transformable.
    This is binary since each transform atempt could have multiple reasons for failure
 */
enum TradePatternMatch
{   
    TRANSFORM_MATCH                 = 0x0000,       ///< There was a transform that matched correctly
    TRANSFORM_UNKNOWN_PATTERN       = 0x0001,       ///< There was no pattern found
    TRANSFORM_UNKNOWN_ITEM          = 0x0002,       ///< Item is not used with any available transform.
    TRANSFORM_MISSING_ITEM          = 0x0004,       ///< No item found to use with work item.
    TRANSFORM_UNKNOWN_WORKITEM      = 0x0008,       ///< No work item container choosen.
    TRANSFORM_MISSING_EQUIPMENT     = 0x0010,       ///< Transform found but not correct equipment
    TRANSFORM_BAD_TRAINING          = 0x0020,       ///< Did not meet required skills.
    TRANSFORM_BAD_SKILLS            = 0x0040,       ///< Did not meet required skills.
    TRANSFORM_OVER_SKILLED          = 0x0080,       ///< Over skilled for this task.
    TRANSFORM_FAILED_CONSTRAINTS    = 0x0100,       ///< Failed one or more contstraints on the transform
    TRANSFORM_BAD_QUANTITY          = 0x0200,       ///< Item was ok for transform but quantity was not.         
    TRANSFORM_BAD_COMBINATION       = 0x0400,       ///< Combination items or ammounts not correct.
    TRANSFORM_TOO_MANY_ITEMS        = 0x0800,       ///< Tried to transform too many items.
    TRANSFORM_BAD_USE               = 0x1000,       ///< Use of item or ammounts not correct.
    TRANSFORM_NO_STAMINA            = 0x2000        ///< Too tired to do trade skill work.
};

/** Holds the possible transformation types.
 */
enum TradeTransfomType
{   
    TRANSFORMTYPE_UNKNOWN=0,        ///< A unknown tranformation
    TRANSFORMTYPE_AUTO_CONTAINER,   ///< Transforming an item by putting it into auto-transform container
    TRANSFORMTYPE_SLOT,             ///< Transforming an item in an equiped slot
    TRANSFORMTYPE_CONTAINER,        ///< Transforming an item that is in a container
    TRANSFORMTYPE_SLOT_CONTAINER    ///< Transforming an item that is in a container in an equiped slot
};

//-----------------------------------------------------------------------------

/** This class keeps natural resource concentrations across the world.
 */
struct NaturalResource
{
    int sector;                 ///< The id of the sector this resource is in.
    csVector3 loc;              ///< Centre point of resource location.
    float    radius;            ///< Radius around the centre where resource can be found.
    float    visible_radius;
    float    probability;       ///< Probability of finding resource on attempt.
    psSkillInfo *skill;         ///< Skill used to harvest resource. 
    int      skill_level;       ///< Skill level required to be able to harvest resource. 
    unsigned int item_cat_id;       
    float    item_quality;
    csString anim;                  ///< Name of animation to play while harvesting 
    int      anim_duration_seconds; ///< Length of time the animation should play.
    int      reward;
    csString reward_nickname;
};

//-----------------------------------------------------------------------------

struct constraint
{
    bool (*constraintFunction)(psWorkManager* that, char* param);
    char* name;
    char* message;
};

//-----------------------------------------------------------------------------

/** This class handles all calculations around work, using statistics
*  and information from the pspccharacterinfo Prop Classes for both
*  the worker and the target.
*/
class psWorkManager : public MessageManager
{
protected:
    csPDelArray<NaturalResource> resources; /// list of all natural resources in game.
    MathScriptEngine *script_engine;        /// Scripting engine handles all RPG calculations.
    MathScript *calc_repair_time;           /// This is the calculation for how long a repair takes.
    MathScript *calc_repair_result;         /// This is the calculation for how many points of quality are added in a repair.
    MathScriptVar *var_time_Worker;         /// Variable representing worker in time calculation
    MathScriptVar *var_time_Object;         /// Variable representing the repaired object in time calculation
    MathScriptVar *var_time_Result;         /// Variable representing the answer in time calculation
    MathScriptVar *var_result_Worker;       /// Variable representing worker in result calculation
    MathScriptVar *var_result_Object;       /// Variable representing the repaired object in result calculation
    MathScriptVar *var_result_Result;       /// Variable representing the answer in result calculation
   
public:

    psWorkManager(EntityManager* celserv);
    virtual ~psWorkManager();

    virtual void HandleMessage(MsgEntry *me, Client *client);

//-----------------------------------------------------------------------------
// Entry points
//-----------------------------------------------------------------------------

    /** Handles using an item for working.  This is called when an item 
      * is tagetted and the use command issued.
      *
      * @param client  The client that placed the item inside
      */
    void HandleUse(Client *client);

    /** Start Combine work
      * This gets called when the player tries to combine items in a container using /combine command.
      *
      * @param client  The client that placed the item inside
      */
    void HandleCombine(Client *client);
    
    /** Start a work event for this client.  This is called when an item is placed
      * in a container.  If the container is an auto-transform container it can transform items 
      * automatically ( ie placing items in automatically triggers the transformation process to start ).
      *
      * @param client  The client that placed the item inside
      * @param container The work item container that can transform an item.
      * @param autoItem The item that was placed inside and to be transformed.
      * @param count The stack count of the item placed in.
      */
    void StartAutoWork(Client *client, psItem* container, psItem *autoItem, int count);
    
    /** Stop work event.  
     * This is called when a client removes an item from a container. 
     *  
     *  @param client The client that removed the item.
     *  @param item The item that was being transformed.
     */
    void StopWork(Client *client, psItem *item);

//-----------------------------------------------------------------------------

    /** Stop auto work event.  
     * This is called when a client removes an item from any container 
     * before it has had a chance to transform the item. 
     *  
     *  @param client The client that removed the item.
     *  @param autoItem The item that was being transformed.
     */
    void StopAutoWork(Client *client, psItem *autoItem);

    /** @name Work Event Handlers
      *  These are the functions fired when a psWorkGameEvent is Triggered.
      */
    //@{     
    /** Handles a transformation/combination event. Basically the manufacturing events. 
      * 
      * @param event The work event that was in the queue to fire.
      */      
    void HandleWorkEvent(psWorkGameEvent* workEvent);
    
    /** Handles a cleanup event. Basically removing discarded items from public containers. 
      * 
      * @param event The work event that was in the queue to fire.
      */      
    void HandleCleanupEvent(psWorkGameEvent* workEvent);
    
    /** Handles a resource/harvesting event. Basically the production events. 
      * 
      * @param event The work event that was in the queue to fire.
      */          
    void HandleProductionEvent(psWorkGameEvent* workEvent);

    /** Handles a repair event, which occurs after a few seconds of repairing an item. 
    * 
    * @param event The work event that was in the queue to fire.
    */          
    void HandleRepairEvent(psWorkGameEvent* workEvent);
    void LockpickComplete(psWorkGameEvent* workEvent);   
    //@}
    
    
    /** @name Constraint Functions
    *  
    */
    //@{         
    static bool constraintTime(psWorkManager* that, char* param);
    static bool constraintFriends(psWorkManager* that,char* param);
    static bool constraintLocation(psWorkManager* that,char* param);
    static bool constraintMode(psWorkManager* that,char* param);
    //@}
    
    csRef<gemObjectSafe> worker;    ///< Current worker that the work manager is dealing with.
//    int workerCID;                  ///< Current worker ID
    
    uint32_t clientNum;             ///< Current client the work manager is dealing with.
    psItem* workItem;               ///< The current work item that is in use ( example, ore furnace )
    psItem* autoItem;               ///< The current item that is being transformed by auto-transformation container
    psCharacter *owner;             ///< The character data of the current character being used. 
    uint32 patternId;                  ///< Current pattern ID
    uint32 groupPatternId;             ///< Current group pattern ID
    float patternKFactor;            ///< Pattern factor that's part of quality calculation
    float currentQuality;            ///< Current result item quality
    psTradeTransformations* trans;    ///< Current work transformation
    const char* preworkModeString;        ///< Mode string prior to work

    // Lockpicking
    void StartLockpick(Client* client,psItem* item);
    
   
    /** Sets up the internal structure of the work manager to handle a particular client.
      * This sets many variables of the workmanager to work with a single user at a time. 
      *
      * @param client The client that is the current one to use. 
      *
      * @return False if there is a problem loading stuff.
      */
    bool LoadLocalVars(Client* client);

    /** Send clear client view message to remove items from autocontainers.
      *
      * @param slotID The slot number to clear. 
      * @param containerID The container ID that has item that needs to be cleared. 
      *
      * @return False if there is a problem sending message.
      */
    bool SendClearUpdate( unsigned int slotID, unsigned int containerID );

protected:
    EntityManager* entitymanager;

    /** Handles stopping the use of the item for working.  This is called when an item 
      * is tagetted and the /use command issued and it's already in use.
      *
      * @param client  The client that issues the /use command
      */
    void StopUseWork(Client *client);

    /** Checks to see if the item can be used for working.
      *
      * @param client  The client that issues the /use command
      */
    void StartUseWork(Client *client);
    
    /** Handles stopping the combining in the work container.  This is called when an item 
      * is tagetted and the /combine command issued and it's already in use.
      *
      * @param client  The client that issues the /combine command
      */
    void StopCombineWork(Client *client);

    /** Checks to see if the work container item can be used for combining.
      *
      * @param client  The client that issues the /combine command
      */
    void StartCombineWork(Client *client);

    /** Handles stopping the cleanup event for a particular item.  This is called when an item 
      * is removed from a container.
      *
      * @param client  The client that removes the item
      * @param cleanItem  The item that is removed
      */
    void StopCleanupWork(Client* client, psItem* cleanItem);

    /** Sends an error message to the client based on the trade pattern error. 
      * @see TradePatternMatch for the list of error conditions. 
      * @param clientNum the client to send the message to.
      * @param result The error code from the transformable test. 
      */
    void SendTransformError( uint32 clientNum, int result, uint32 curItemId = 0, int CurItemQty = 0 );
     
    /** Returns with the result ID and quantity of the combination
      *  if work item container has the correct items in the correct amounts
      * Note:  This assumes that the combination items array is sorted by
      *  resultId and then itemId
      *
      * @return resultID The item ID of the resulting item.
      * @return resultQty The stack quantity of the resulting item. 
      *
      * @return False if combination is not possible.
      */
    bool IsContainerCombinable(uint32 &resultId, int &resultQty);

    /** Returns with the result ID and quantity of the combination
      *  if player has the correct items in the correct amounts in hand
      * Note:  This assumes that the combination items array is sorted by
      *  resultId and then itemId
      *
      * @return resultID The item ID of the resulting item.
      * @return resultQty The stack quantity of the resulting item. 
      *
      * @return False if combination is not possible.
      */
    bool IsHandCombinable(uint32 &resultId, int &resultQty);

    /** Returns with the result ID and quantity of the combination
      *  if the item list matches every item in a valid combination
      *
      * @param itemArray The array of items that need to be checked.
      * @return resultID The item ID of the resulting item.
      * @return resultQty The stack quantity of the resulting item. 
      *
      * @return False if combination is not possible.
      */
    bool ValidateCombination(csArray<psItem*> itemArray, uint32 &resultId, int &resultQty);
    
    /** Check to see if there is a possible trasnform available.
      * @param patternID The current pattern to use
      * @param targetID the id of the item trying to transform.
      * @param targetQty The stack count of the item to transform.
      *
      * @return A integer that indicates pattern match status.
      */
    int IsTransformable(uint32 patternId, uint32 targetId, int targetQty);
    
    bool TransformContainedItem(psItem* oldItem, uint32 newId, int newQty, float itemQuality);
    psItem* CombineContainedItem(uint32 newId, int newQty, float itemQuality, psItem* containerItem);
    bool TransformSlotItem(int slot, uint32 newId, int newQty, float itemQuality);
//    bool TransformHandItem(uint32 newId, int newQty, float itemQuality);
    bool SendItemUpdate( unsigned int slotID, psItem *newItem );

    void StartTransformationEvent(int transType, int transSlot, int resultQty, float resultQuality, psItem* autoItem);
    void StartCleanupEvent(Client* client, psItem* item, gemActor* worker);

    bool ValidateTarget(Client* client);
    bool ValidateWork();
    bool ValidateMind();
    bool ValidateStamina(Client* client);
    bool IsOnHand(uint32 equipId);
    bool IsInContainer(uint32 equipId);
    psItem* CreateTradeItem(uint32 newId, int newQty, float itemQuality, psItem* parent, int slot);
    bool ValidateTraining(psTradeTransformations* transCandidate);
    bool ValidateSkills(psTradeTransformations* transCandidate);
    bool ValidateNotOverSkilled(psTradeTransformations* transCandidate);
    bool ValidateConstraints(psTradeTransformations* transCandidate);
    int CalculateEventDuration(int pointQty);
    bool CheckStamina(psCharacter * owner) const;

    void Initialize();
    bool ApplySkills(float factor, psItem* transItem);
    void HandleRepair(Client *client);
    /// Handle production events from clients
    void HandleProduction(Client *client,const char *type,const char *reward);
 public:
    /// Handle production events from super clients
    void HandleProduction(gemActor *actor,const char *type,const char *reward);
 protected:
    bool SameProductionPosition(gemActor *actor, const csVector3& startPos);
    NaturalResource *FindNearestResource(const char *reward,iSector *sector, csVector3& pos);
};



//-----------------------------------------------------------------------------

// work event class
class psWorkGameEvent : public psGameEvent, public iDeleteObjectCallback
{
public:
    psWorkGameEvent(psWorkManager* mgr,
                    gemActor* worker,
                    int delayticks, 
                    int cat,
                    csVector3& pos,
                    NaturalResource *natres=NULL,
                    Client *c=NULL,
                    psItem* object=NULL,
                    float repairAmount=0.0F);
    virtual ~psWorkGameEvent();

    void Interrupt();

    virtual void Trigger();  // Abstract event processing function

    virtual void DeleteObjectCallback(iDeleteNotificationObject * object);

    // Set the active trade transformation for the event.
    void SetTransformation(psTradeTransformations *t) { transformation = t; }

    // Return the active transformation, if any for this event.
    psTradeTransformations *GetTransformation() { return transformation; }

    // result quantity is only used when transaction result is zero
    int GetResultQuantity() { return resultQuantity; }
    void SetResultQuantity(int newQuantity) { resultQuantity = newQuantity; }

    // result quality is calculated immediately before the event
    float GetResultQuality() { return resultQuality; }
    void SetResultQuality(float newQuality) { resultQuality = newQuality; }

    // pattern Kfactor is based on the current pattern
    float GetKFactor() { return KFactor; }
    void SetKFactor(float newFactor) { KFactor = newFactor; }

    // slot to perform the transformation 
    int GetTransformationSlot() { return transSlot; }
    void SetTransformationSlot(int curSlot) { transSlot = curSlot; }

    psItem* GetTranformationItem() { return item; }
    void SetTransformationItem(psItem* i) { item = i; }

    psItem* GetWorkItem() { return workItem; }
    void SetWorkItem(psItem* w) { workItem = w; }

    // transformation type
    int GetTransformationType() { return transType; }
    void SetTransformationType(int t) { transType = t; }

    
    psWorkManager* workmanager;
    csRef<gemObjectSafe> worker;
    NaturalResource* nr;
    Client* client;
    int category;
    csVector3 position;
    psItem*    object;
    psTradeTransformations* transformation;
    float repairAmount;
    
    uint32_t effectID;      // The id of the psEffect tied to event.
    csArray<PublishDestination> multi;
    
private:
    int resultQuantity;
    float resultQuality;
    float KFactor;
    int transSlot;
    psItem* item;
    psItem* workItem;
    int transType;
};

#endif
