/*
 * Copyright (C) 2002,2003,2004,2005 Daniel Heck
 *
 * 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.
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */
#ifndef DISPLAY_HH_INCLUDED
#define DISPLAY_HH_INCLUDED

#include "enigma.hh"
#include "ecl.hh"
#include "server.hh"

//----------------------------------------
// Definition of models
//----------------------------------------
namespace display
{
    class DisplayLayer;
    class ModelLayer;

    /*! Animations can invoke a callback of this type on completion.
      Note that you may not delete or replace models other than the
      one that induced the callback from inside the callback--use a
      timer or a flag to do this. */
    class ModelCallback {
    public:
        virtual ~ModelCallback() {}
        virtual void animcb() = 0;
    };

    class Animation {
    public:
        virtual ~Animation() {}
        virtual void set_callback (ModelCallback *) {}
        virtual void reverse() {}
        virtual void restart() {}

	virtual bool is_garbage() const { return false; }
        virtual void tick(double /*dtime*/) {}
        virtual bool has_changed(ecl::Rect &/*changed_region*/) { return false; }
    };

    class Model : public Animation {
    public:

        virtual void draw(ecl::GC &/*gc*/, int /*x*/, int /*y*/) {}
        virtual void draw_shadow(ecl::GC &/*gc*/, int /*x*/, int /*y*/) {}

        virtual Model *get_shadow() const { return 0; }

        virtual void expose (ModelLayer * /*ml*/, int videox, int videoy) {}
        virtual void remove (ModelLayer * /*ml*/) {}

        virtual Model *clone() = 0;
        virtual void get_extension (ecl::Rect &r);
    };

/* -------------------- Functions -------------------- */

    void InitModels();
    void ShutdownModels();

    Model * MakeModel (const std::string &name);


    int DefineImage (const char *name, const char *fname,
                     int xoff, int yoff, int padding);
    int DefineSubImage (const char *name, const char *fname,
                        int xoff, int yoff, ecl::Rect r);
    void DefineRandModel (const char *name, int n, char **names);
    void DefineShadedModel (const char *name, const char *model, const char *shade);
    void DefineOverlayImage (const char *name, int n, char **images);
    void DefineComposite (const char *name, const char *bgname, const char *fgname);
    void DefineAnim (const char *name, bool loop_p);
    void AddFrame (const char *name, const char *model, double time);
    void DefineAlias (const char *name, const char *othername);
}

//----------------------------------------
// Models on the grid
//----------------------------------------
namespace display
{
    using enigma::GridPos;
    using enigma::GridLayer;
    using enigma::GridLoc;

    Model*  SetModel (const GridLoc & l, const std::string &modelname);
    Model*  SetModel (const GridLoc & l, Model *m);
    void    KillModel (const GridLoc & l);
    Model*  GetModel (const GridLoc & l);
    Model*  YieldModel (const GridLoc & l);
}

/* -------------------- Scrolling -------------------- */
namespace display
{
    enum FollowMode {
        FOLLOW_NONEOLD            = 0, // Don't follow any sprite
        FOLLOW_SCROLLING       = 1, // Scroll the screen
        FOLLOW_SCREEN          = 2, // Flip the screen region
        FOLLOW_SCREENSCROLLING = 3, // Scroll to the next screen
        FOLLOW_SMOOTH          = 4, // Follow pixel by pixel
    };
    
    enum FollowTyp {
        FOLLOW_NONE            = 0, // Don't follow any sprite
        FOLLOW_SCROLL          = 1, // Scroll pixelwise
        FOLLOW_FLIP            = 2, // Flip the display to destination
    };


    void SetFollowMode (FollowMode m);
    void UpdateFollowMode();
    void SetScrollBoundary (double boundary);

    void SetReferencePoint (const ecl::V2 &point);
    void GetReferencePointCoordinates(int *x, int *y);
    void FocusReferencePoint();
}

/* -------------------- Sprites -------------------- */
namespace display
{
    enum SpriteLayer {
        SPRITE_ACTOR, SPRITE_EFFECT, SPRITE_DEBRIS
    };

    typedef unsigned int SpriteId;

    class DL_Sprites;

    class SpriteHandle {
        DL_Sprites *layer;
        unsigned id;
    public:
        SpriteHandle (DL_Sprites *l, unsigned spriteid);
        SpriteHandle();

        void kill();
        void move (const ecl::V2 &newpos) const;
        void replace_model (Model *m) const;
        Model *get_model () const;
        void set_callback (ModelCallback *cb) const;
        void hide() const;
        void show() const;
    };

    /*! Add a new effect sprite.  Sprites of this type are
      automatically deleted once the animation has finished.  */
    SpriteHandle AddEffect (const ecl::V2 &pos, const char *modelname, bool isDispensible = false);

    /*! Create a new sprite.  If modelname==0, the sprite is
      considered invisible.  Sprites of this type are _never_
      automatically deleted.  */
    SpriteHandle AddSprite (const ecl::V2 &pos, const char *modelname=0);

}

/* -------------------- Rubber bands -------------------- */
namespace display
{
    class DL_Lines;

    class RubberHandle {
    public:
        RubberHandle (DL_Lines *layer=0, unsigned id=0);
        operator unsigned() { return id; }

        void update_first (const ecl::V2 &p1);
        void update_second (const ecl::V2 &p2);
        void kill();

        DL_Lines *line_layer;
        unsigned id;
    };

    RubberHandle AddRubber (const ecl::V2 &p1, const ecl::V2 &p2, unsigned short rc, unsigned short gc, unsigned short bc, bool isThick);
}


/* -------------------- Status bar -------------------- */
namespace display
{
    using enigma_player::Inventory;

    class StatusBar {
    public:
        virtual ~StatusBar() {}
        virtual void set_inventory(enigma::Player activePlayer, const std::vector<std::string> &modelnames) = 0;

        virtual void show_text (const std::string &str, 
                                bool scrolling, 
                                double duration = -1) = 0;
        virtual void hide_text() = 0;

        virtual void show_move_counter (bool active) = 0;
        virtual void setCMode(bool flag) = 0;
        virtual void setBasicModes(std::string flags) = 0;

        virtual void set_time (double time) = 0;
        virtual void set_speed (double speed) = 0;
        virtual void set_travelled_distance (double distance) = 0;
        virtual void set_counter (int nummoves) = 0;
    };

    StatusBar *GetStatusBar();

#define STATUSBAR display::GetStatusBar()

/* -------------------- Constants -------------------- */

    const int MIN_TextSpeed = 1;
    const int MAX_TextSpeed = 20;
    const int DEFAULT_TextSpeed = 8;
    const int FACTOR_TextSpeed = 20;  // Multiplicative factor for text speed

/* -------------------- Interface to display engine -------------------- */

    enum DisplayFlags
    {
        SHOW_FLOOR     = 0x01,
        SHOW_STONES    = 0x02,
        SHOW_ITEMS     = 0x04,
        SHOW_SHADES    = 0x08,
        SHOW_SPRITES   = 0x10,
        SHOW_TIME      = 0x20,
        SHOW_INVENTORY = 0x40,
        SHOW_ALL       = 0x7f
    };

    void ToggleFlag(DisplayFlags flag);


    void Init(bool show_fps = false);
    void Shutdown();

    void            NewWorld (int w, int h);
    void            ResizeGameArea (int w, int h);
    const ecl::Rect& GetGameArea ();

    void DrawAll (ecl::GC &gc);
    void RedrawAll (ecl::Screen *sfc);
    void Redraw (ecl::Screen *sfc);
    void Tick (double dtime);

    void SetTextSpeed(int newspeed);
    int GetTextSpeed();
}

#endif
