// FillStyle.h: variant fill styles
// 
//   Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
//   Free Software Foundation, Inc.
// 
// 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 3 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 St, Fifth Floor, Boston, MA  02110-1301  USA

#ifndef GNASH_FILL_STYLE_H
#define GNASH_FILL_STYLE_H

#include <boost/variant.hpp>
#include <vector> 
#include <iosfwd> 
#include <boost/intrusive_ptr.hpp>
#include <cassert>

#include "SWFMatrix.h"
#include "SWF.h"
#include "RGBA.h" 

namespace gnash {
    class movie_definition;
    class CachedBitmap;
}

namespace gnash {

class GradientRecord
{
public:
    GradientRecord(std::uint8_t ratio, rgba color)
        :
        ratio(ratio),
        color(std::move(color))
    { }
    
    //data:
    std::uint8_t ratio;
    rgba color;
};

/// A BitmapFill
//
/// BitmapFills can refer to a parsed bitmap tag or be constructed from
/// bitmap data. They are used for Bitmap characters.
//
/// Presently all members are immutable after construction. It is of course
/// possible to change the appearance of the fill by changing the CachedBitmap
/// it refers to.
//
/// Special member functions (ctor, dtor etc) are not inlined to avoid 
/// requiring the definition of movie_definition.
//
/// TODO: check the following:
//
/// It may be necessary to allow setting the smoothing policy; the use of
/// this should certainly be extended to non-static BitmapFills.
class DSOEXPORT BitmapFill
{
public:

    /// How to smooth the bitmap.
    enum SmoothingPolicy {
        SMOOTHING_UNSPECIFIED,
        SMOOTHING_ON,
        SMOOTHING_OFF
    };
    
    /// Whether the fill is tiled or clipped.
    //
    /// Clipped fills use the edge pixels to fill any area outside the bounds
    /// of the image.
    enum Type {
        CLIPPED,
        TILED
    };

    /// Construct a BitmapFill from arbitrary bitmap data.
    //
    /// TODO: check the smoothing policy here!
    BitmapFill(Type t, const CachedBitmap* bi, SWFMatrix m,
            SmoothingPolicy pol);

    /// Construct a static BitmapFill using a SWF tag.
    BitmapFill(SWF::FillType t, movie_definition* md, std::uint16_t id,
            SWFMatrix m);

    /// Destructor
    ~BitmapFill();

    /// Copy a BitmapFill
    //
    /// The copied BitmapFill refers to the same bitmap id in the same
    /// movie_definition as the original.
    BitmapFill(const BitmapFill& other);
    
    BitmapFill& operator=(const BitmapFill& other);

    /// Set this fill to a lerp of two other BitmapFills.
    void setLerp(const BitmapFill& a, const BitmapFill& b, double ratio);

    /// Get the Type of this BitmapFill
    //
    /// BitmapFills are either tiled or clipped.
    Type type() const {
        return _type;
    }

    /// Get the smoothing policy of this BitmapFill.
    SmoothingPolicy smoothingPolicy() const {
        return _smoothingPolicy;
    }

    /// Get the actual Bitmap data.
    const CachedBitmap* bitmap() const;

    /// Get the matrix of this BitmapFill.
    const SWFMatrix& matrix() const {
        return _matrix;
    }

private:

    Type _type;

    SmoothingPolicy _smoothingPolicy;

    SWFMatrix _matrix;
    
    /// A Bitmap, used for dynamic fills and to cache parsed bitmaps.
    mutable boost::intrusive_ptr<const CachedBitmap> _bitmapInfo;

    /// The movie definition containing the bitmap
    movie_definition* _md;

    // The id of the tag containing the bitmap
    std::uint16_t _id;
};

/// A GradientFill
class DSOEXPORT GradientFill
{
public:

    /// The type of GradientFill
    //
    /// A Radial fill is a gradient fill with a focal point.
    enum Type {
        LINEAR,
        RADIAL
    };

    enum SpreadMode {
        PAD,
        REPEAT,
        REFLECT
    };

    enum InterpolationMode {
        RGB,
        LINEAR_RGB
    };

    typedef std::vector<GradientRecord> GradientRecords;

    /// Construct a GradientFill
    //
    /// Optionally the records can be passed here.
    //
    /// The actual matrix of the gradient depends on the type; the constructor
    /// handles this, and users should just pass the user matrix.
    GradientFill(Type t, const SWFMatrix& m,
            const GradientRecords& = GradientRecords());

    Type type() const {
        return _type;
    }

    const SWFMatrix& matrix() const {
        return _matrix;
    }

    /// Set this fill to a lerp of two other GradientFills.
    void setLerp(const GradientFill& a, const GradientFill& b, double ratio);
    
    void setRecords(const GradientRecords& recs) {
        assert(recs.size() > 1);
        _gradients = recs;
    }

    const GradientRecords &getRecords() const {
        return _gradients;
    }

    /// Get the number of records in this GradientFill
    size_t recordCount() const {
        return _gradients.size();
    }

    /// Query the GradientRecord at the specified index
    //
    /// There are recordCount() records.
    const GradientRecord& record(size_t i) const {
        assert(i < _gradients.size());
        return _gradients[i];
    }

    /// Set the focal point.
    //
    /// Value will be clamped to the range -1..1; callers don't need to check.
    void setFocalPoint(double d);

    /// Get the focal point of this GradientFill
    //
    /// If the focal point is 0.0, it is a simple radial fill.
    double focalPoint() const {
        return _focalPoint;
    }

    SpreadMode spreadMode;
    InterpolationMode interpolation;

private:

    double _focalPoint;
    GradientRecords _gradients;
    Type _type;
    SWFMatrix _matrix;
};

/// A SolidFill containing one color.
//
/// SolidFills are the simplest fill, containing only a single color.
struct DSOEXPORT SolidFill
{
public:

    /// Construct a SolidFill.
    explicit SolidFill(rgba c)
        :
        _color(std::move(c))
    { }

    /// Copy a SolidFill.
    SolidFill(const SolidFill& other)
        :
        _color(other._color)
    { }

    /// Set this fill to a lerp of two other SolidFills.
    void setLerp(const SolidFill& a, const SolidFill& b, double ratio) {
        _color = lerp(a.color(), b.color(), ratio);
    }

    /// Get the color of the fill.
    rgba color() const {
        return _color;
    }

private:
    rgba _color;
};

/// FillStyle describes the various fill styles for shapes
//
/// The FillStyle class is effectively a boost::variant, but to allow passing
/// FillStyles using a forward declaration (and reducing compile times),
/// it's necessary to use a class.
class DSOEXPORT FillStyle 
{
public:

    typedef boost::variant<BitmapFill, SolidFill, GradientFill> Fill;
    
    /// Construct a FillStyle from any Fill.
    //
    /// The non-explicit templated contructor allows the same syntax as a
    /// simple boost::variant:
    ///     FillStyle f = GradientFill();
    template<typename T> FillStyle(const T& f) : fill(f) {}

    FillStyle(const FillStyle& other)
        :
        fill(other.fill)
    { }

    Fill fill;

};
 
/// Set the FillStyle to a lerp of a and b.
//
/// Callers must ensure that all FillStyles have exactly the same type! Most
/// errors are caught by type-checking and will throw an unhandled exception.
void setLerp(FillStyle& f, const FillStyle& a, const FillStyle& b, double t);

/// Output operator for bitmap smoothing policy.
DSOEXPORT std::ostream& operator<<(std::ostream& os,
        const BitmapFill::SmoothingPolicy& p);

/// Output operator for FillStyles.
std::ostream& operator<<(std::ostream& os, const FillStyle& fs);

/// Output operator for GradientFill type.
std::ostream& operator<<(std::ostream& o, GradientFill::Type t);

/// Output operator for GradientFill spread mode.
std::ostream& operator<<(std::ostream& o, GradientFill::SpreadMode t);

/// Output operator for GradientFill interpolation mode.
std::ostream& operator<<(std::ostream& o, GradientFill::InterpolationMode t);

} // namespace gnash

#endif

// Local Variables:
// mode: C++
// indent-tabs-mode: nil
// End:
