text: ﻿#ifndef LAYOUTCONTEXT_H
  #define LAYOUTCONTEXT_H
  
  #include "property.h"
  #include "canvas.h"
  
  #include <list>
  #include <map>
  #include <set>
  
  namespace lunasvg {
  
  enum class LayoutId
  {
      Symbol,
      Group,
      Shape,
      Mask,
      ClipPath,
      Marker,
      LinearGradient,
      RadialGradient,
      Pattern,
      SolidColor
  };
  
  class RenderState;
  
  class LayoutObject
  {
  public:
      LayoutObject(LayoutId id);
      virtual ~LayoutObject();
      virtual void render(RenderState&) const;
      virtual void apply(RenderState&) const;
      virtual Rect map(const Rect&) const;
  
      virtual const Rect& fillBoundingBox() const { return Rect::Invalid;}
      virtual const Rect& strokeBoundingBox() const { return Rect::Invalid;}
  
      bool isPaint() const { return id == LayoutId::LinearGradient || id == LayoutId::RadialGradient || id == LayoutId::Pattern || id == LayoutId::SolidColor; }
      bool isHidden() const { return isPaint() || id == LayoutId::ClipPath || id == LayoutId::Mask || id == LayoutId::Marker; }
  
  public:
      LayoutId id;
  };
  
  using LayoutList = std::list<std::unique_ptr<LayoutObject>>;
  
  class LayoutContainer : public LayoutObject
  {
  public:
      LayoutContainer(LayoutId id);
  
      const Rect& fillBoundingBox() const;
      const Rect& strokeBoundingBox() const;
  
      LayoutObject* addChild(std::unique_ptr<LayoutObject> child);
      LayoutObject* addChildIfNotEmpty(std::unique_ptr<LayoutContainer> child);
      void renderChildren(RenderState& state) const;
  
  public:
      LayoutList children;
  
  protected:
      mutable Rect m_fillBoundingBox{Rect::Invalid};
      mutable Rect m_strokeBoundingBox{Rect::Invalid};
  };
  
  class LayoutClipPath : public LayoutContainer
  {
  public:
      LayoutClipPath();
  
      void apply(RenderState& state) const;
  
  public:
      Units units;
      Transform transform;
      const LayoutClipPath* clipper;
  };
  
  class LayoutMask : public LayoutContainer
  {
  public:
      LayoutMask();
  
      void apply(RenderState& state) const;
  
  public:
      double x;
      double y;
      double width;
      double height;
      Units units;
      Units contentUnits;
      double opacity;
      const LayoutMask* masker;
      const LayoutClipPath* clipper;
  };
  
  class LayoutSymbol : public LayoutContainer
  {
  public:
      LayoutSymbol();
  
      void render(RenderState& state) const;
      Rect map(const Rect& rect) const;
  
  public:
      double width;
      double height;
      Transform transform;
      Rect clip;
      double opacity;
      const LayoutMask* masker;
      const LayoutClipPath* clipper;
  };
  
  class LayoutGroup : public LayoutContainer
  {
  public:
      LayoutGroup();
  
      void render(RenderState& state) const;
      Rect map(const Rect& rect) const;
  
  public:
      Transform transform;
      double opacity;
      const LayoutMask* masker;
      const LayoutClipPath* clipper;
  };
  
  class LayoutMarker : public LayoutContainer
  {
  public:
      LayoutMarker();
  
      Transform markerTransform(const Point& origin, double angle, double strokeWidth) const;
      Rect markerBoundingBox(const Point& origin, double angle, double strokeWidth) const;
      void renderMarker(RenderState& state, const Point& origin, double angle, double strokeWidth) const;
  
  public:
      double refX;
      double refY;
      Transform transform;
      Angle orient;
      MarkerUnits units;
      Rect clip;
      double opacity;
      const LayoutMask* masker;
      const LayoutClipPath* clipper;
  };
  
  class LayoutPattern : public LayoutContainer
  {
  public:
      LayoutPattern();
  
      void apply(RenderState& state) const;
  
  public:
      double x;
      double y;
      double width;
      double height;
      Transform transform;
      Units units;
      Units contentUnits;
      Rect viewBox;
      PreserveAspectRatio preserveAspectRatio;
  };
  
  class LayoutGradient : public LayoutObject
  {
  public:
      LayoutGradient(LayoutId id);
  
  public:
      Transform transform;
      SpreadMethod spreadMethod;
      Units units;
      GradientStops stops;
  };
  
  class LayoutLinearGradient : public LayoutGradient
  {
  public:
      LayoutLinearGradient();
  
      void apply(RenderState& state) const;
  
  public:
      double x1;
      double y1;
      double x2;
      double y2;
  };
  
  class LayoutRadialGradient : public LayoutGradient
  {
  public:
      LayoutRadialGradient();
  
      void apply(RenderState& state) const;
  
  public:
      double cx;
      double cy;
      double r;
      double fx;
      double fy;
  };
  
  class LayoutSolidColor : public LayoutObject
  {
  public:
      LayoutSolidColor();
  
      void apply(RenderState& state) const;
  
  public:
      Color color;
  };
  
  class FillData
  {
  public:
      FillData() = default;
  
      void fill(RenderState& state, const Path& path) const;
  
  public:
      const LayoutObject* painter{nullptr};
      Color color{Color::Transparent};
      double opacity{0};
      WindRule fillRule{WindRule::NonZero};
  };
  
  class StrokeData
  {
  public:
      StrokeData() = default;
  
      void stroke(RenderState& state, const Path& path) const;
      void inflate(Rect& box) const;
  
  public:
      const LayoutObject* painter{nullptr};
      Color color{Color::Transparent};
      double opacity{0};
      double width{1};
      double miterlimit{4};
      LineCap cap{LineCap::Butt};
      LineJoin join{LineJoin::Miter};
      DashData dash;
  };
  
  class MarkerPosition
  {
  public:
      MarkerPosition(const LayoutMarker* marker, const Point& origin, double angle);
  
  public:
      const LayoutMarker* marker;
      Point origin;
      double angle;
  };
  
  using MarkerPositionList = std::vector<MarkerPosition>;
  
  class MarkerData
  {
  public:
      MarkerData() = default;
  
      void add(const LayoutMarker* marker, const Point& origin, double angle);
      void render(RenderState& state) const;
      void inflate(Rect& box) const;
  
  public:
      MarkerPositionList positions;
      double strokeWidth{1};
  };
  
  class LayoutShape : public LayoutObject
  {
  public:
      LayoutShape();
  
      void render(RenderState& state) const;
      Rect map(const Rect& rect) const;
      const Rect& fillBoundingBox() const;
      const Rect& strokeBoundingBox() const;
  
  public:
      Path path;
      Transform transform;
      FillData fillData;
      StrokeData strokeData;
      MarkerData markerData;
      Visibility visibility;
      WindRule clipRule;
      const LayoutMask* masker;
      const LayoutClipPath* clipper;
  
  private:
      mutable Rect m_fillBoundingBox{Rect::Invalid};
      mutable Rect m_strokeBoundingBox{Rect::Invalid};
  };
  
  enum class RenderMode
  {
      Display,
      Clipping
  };
  
  struct BlendInfo
  {
      const LayoutClipPath* clipper;
      const LayoutMask* masker;
      double opacity;
      Rect clip;
  };
  
  class RenderState
  {
  public:
      RenderState(const LayoutObject* object, RenderMode mode);
  
      void beginGroup(RenderState& state, const BlendInfo& info);
      void endGroup(RenderState& state, const BlendInfo& info);
  
      const LayoutObject* object() const { return m_object;}
      RenderMode mode() const { return m_mode; }
      const Rect& objectBoundingBox() const { return m_object->fillBoundingBox(); }
  
  public:
      std::shared_ptr<Canvas> canvas;
      Transform transform;
  
  private:
      const LayoutObject* m_object;
      RenderMode m_mode;
  };
  
  class ParseDocument;
  class StyledElement;
  class GeometryElement;
  
  class LayoutContext
  {
  public:
      LayoutContext(const ParseDocument* document, LayoutSymbol* root);
  
      Element* getElementById(const std::string& id) const;
      LayoutObject* getResourcesById(const std::string& id) const;
      LayoutObject* addToResourcesCache(const std::string& id, std::unique_ptr<LayoutObject> resources);
      LayoutMask* getMasker(const std::string& id);
      LayoutClipPath* getClipper(const std::string& id);
      LayoutMarker* getMarker(const std::string& id);
      LayoutObject* getPainter(const std::string& id);
  
      FillData fillData(const StyledElement* element);
      DashData dashData(const StyledElement* element);
      StrokeData strokeData(const StyledElement* element);
      MarkerData markerData(const GeometryElement* element, const Path& path);
  
      void addReference(const Element* element);
      void removeReference(const Element* element);
      bool hasReference(const Element* element) const;
  
  private:
      const ParseDocument* m_document;
      LayoutSymbol* m_root;
      std::map<std::string, LayoutObject*> m_resourcesCache;
      std::set<const Element*> m_references;
  };
  
  class LayoutBreaker
  {
  public:
      LayoutBreaker(LayoutContext* context, const Element* element);
      ~LayoutBreaker();
  
  private:
      LayoutContext* m_context;
      const Element* m_element;
  };
  
  } // namespace lunasvg
  
  #endif // LAYOUTCONTEXT_H
  