/*
    This file is part of Msc-generator.
    Copyright (C) 2008-2024 Zoltan Turanyi
    Distributed under GNU Affero General Public License.

    Msc-generator is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Msc-generator 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 Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Msc-generator.  If not, see <http://www.gnu.org/licenses/>.
*/
/** @file blockstyle.h Block specific style and context declarations.
* @ingroup libblock_files */

#ifndef BLOCK_STYLE_H
#define BLOCK_STYLE_H

#include <variant>
#include "style.h"

namespace block
{

/** The line type an arrow between blocks can take.
 * This is used to capture arrow symbols ->*/
enum class EArrowStyle : unsigned char
{
    SOLID,   ///<Solid line '->'
    DOTTED,  ///<Dotted line '>'
    DASHED,  ///<Dashed line '>>'
    DOUBLE   ///<Double line '=>'
};

/** The complete description of an arrow. Its style and whether it has
 * start & end arrowheads. Use it during parsing.*/
struct ArrowType
{
    EArrowStyle style; ///<The line style
    bool end;          ///<Do we have an arrowhead at the end?
    bool start;        ///<Do we have an arrowhead at the start?
    ArrowType() = default;
    constexpr ArrowType(EArrowStyle st, bool e, bool s) : style(st), end(e), start(s) {}
    ArrowType &swap() {std::swap(start, end); return *this;}
};

/** Enumeration listing all the box types in parsing.*/
enum class EBlockType : unsigned char
{
    Box,    ///<Classic box
    Boxcol, ///<Classic box with column oriented content
    Row,    ///<An invisible row of blocks, no margin and no line
    Column, ///<An invisible column of blocks, no margin and no line
    Cell,   ///<An invisible set of blocks, continuing the default alignment of the parent, no margin and no line
    Text,   ///<Text with normally no line around it
    Shape,  ///<A block having a specific shape, other than a box
    Space,  ///<Just empty space with a size. Breaks and indicators also have this type.
    Indicator, ///<An indicator
    Chart   ///<AN inlined chart
};
/** Shall we draw tracking rectangles */
constexpr bool HasArea(EBlockType t) { return t!=EBlockType::Space; }
constexpr bool Visible(EBlockType t) { return t!=EBlockType::Space && t!=EBlockType::Row && t!=EBlockType::Column && t!=EBlockType::Cell; }

/** Enumeration listing 4 directions. Use also as command types in parsing.*/
enum class EDirection : unsigned char
{
    Invalid,///<The invalid value. Used as a proxy for totally centered label position for the 'label.pos' attr.
    Above,  ///<Command to continue above the last block
    Below,  ///<Command to continue below the last block
    Left,   ///<Command to continue left of the last block
    Right   ///<Command to continue right of the last block
};

/** Return the opposite direction. */
constexpr EDirection Opposite(EDirection t) {
    return t==EDirection::Above ? EDirection::Below :
        t == EDirection::Below ? EDirection::Above :
        t==EDirection::Left ? EDirection::Right :
                                EDirection::Left;
}

/** True if the direction is Above or Below.*/
constexpr bool IsVertical(EDirection t)
{ return t==EDirection::Above || t==EDirection::Below; }

/** True if the direction is Left or Right.*/
constexpr bool IsHorizontal(EDirection t)
{ return t==EDirection::Left || t==EDirection::Right; }

/** True if the direction is Above or Left.*/
constexpr bool IsMin(EDirection t)
{ return t==EDirection::Above || t==EDirection::Left; }

/** True if the direction is Below or Right.*/
constexpr bool IsMax(EDirection t)
{ return t==EDirection::Below || t==EDirection::Right; }

enum class EOrientation
{
    Invalid,     ///<The invalid value. 
    Normal,      ///<Regular orientation, text is left-to-right.
    UpsideDown,  ///<The text is upside-down and go from right-to-left.
    Left,        ///<The text can be viewed from left, text goes down-to-up.
    Right,       ///<The text can be viewed from right, text goes up-to-down.
    Tangent,     ///<The text goes along the tangent of the arrow.
    TangentUpsideDown ///<The text goes along the tangent of the arrow, upside down.
};

/** True if the orientation is Tangent or TangentUpsideDown*/
constexpr bool IsTangent(EOrientation o) 
{ return o==EOrientation::Tangent || o==EOrientation::TangentUpsideDown; }

/** True if the orientation is Normal or Upside Down: if the label reads horizontal.
 * Note: different from what is horizontal for EDirection.*/
constexpr bool IsHorizontal(EOrientation t)     {
 return t== EOrientation::Normal || t== EOrientation::UpsideDown; }

/** True if the direction is Left or Right: if the label reads vertical.
 * Note: different from what is vertical for EDirection.*/
constexpr bool IsVertical(EOrientation t) {
 return t== EOrientation::Left || t== EOrientation::Right; }

struct AlignModifier
{
    bool major;
    EDirection dir;
};

class BlockBlock;
class BlockChart;

struct StringWithPosNumber : public StringWithPos
{
    double number = 0;
    using StringWithPos::StringWithPos;
    StringWithPosNumber() = default;
    StringWithPosNumber(StringWithPos &&sp, double num) :StringWithPos(std::move(sp)), number(num) {}
};
using StringWithPosNumberList = std::vector<StringWithPosNumber>;

/** Specifies one part of a block to align to.
 * Does not include whether horizontal or vertical.*/
struct EdgePos
{
    double pos;   ///<Whether the left/top (0.0), center (0.5) or right/bottom side (1.0) - or anything in between and outside
    bool margin;  ///<Whether we align to the margin (true) or to the visible edge (false).
    double offset;///<Pixel offset in addition to pos
    EdgePos() noexcept = default;
    EdgePos(double p, bool m, double o=0) : pos(p), margin(m), offset(o) {}
    bool operator==(const EdgePos &o) const { return pos==o.pos && margin==o.margin && offset==o.offset; }
};

/** Describes the method via which we specified an alignment requirement
 * and thereby its priority in case of conflicts.*/
enum class EAlignPrio : unsigned char
{
    Default,           ///<No alignment was specified, this is the hardcoded default
    Default_high,      ///<No alignment was specified, this is the hardcoded default + elevated priority
    Default_style,     ///<The alignment comes from the default style of the block (including any refinement style)
    Default_style_high,///<The alignment comes from the default style of the block (including any refinement style) + elevated priority
    Content,           ///<The alignment comes from the parent type (coming from the content style of the parent)
    Content_high,      ///<The alignment comes from the parent type (coming from the content style of the parent) + elevated priority
    Running_style,     ///<The user specified the alignment via the running style
    Running_style_high,///<The user specified the alignment via the running style + elevated priority
    Explicit,          ///<The user specified the alignment via an attribute on the block (or an explicit alignment modifier or the application of a style)
    Explicit_high,     ///<The user specified the alignment via an attribute on the block (or an explicit alignment modifier or the application of a style) + elevated priority
    Must               ///<Must be so, due to logic, eg all blocks larger than min size
};

/** Remove the _high modifiers from a priority */
constexpr EAlignPrio BasePriority(EAlignPrio p) { return EAlignPrio((unsigned char)(p)&254); }
/** Add a _high modifier to a priority, if not yet added. */
constexpr EAlignPrio HighPriority(EAlignPrio p) { return EAlignPrio((unsigned char)(p)|1); }

/** Formulate a human-readable text for an attribute value with this priority.*/
constexpr std::string_view PriorityText(EAlignPrio p)
{
    switch (BasePriority(p)) {
    case EAlignPrio::Default: return "default setting";
    case EAlignPrio::Default_style: return "attribute value coming from the default style";
    case EAlignPrio::Content: return "attribute value coming from the content style of a parent";
    case EAlignPrio::Running_style: return "attribute coming from a 'use' command";
    case EAlignPrio::Explicit: return "attribute value or style applied directly to a block";
    case EAlignPrio::Must: return "mandatory setting";
    default: _ASSERT(0); return "INTERNAL ERROR";
    }
}

#define STRING_PARENT "parent"

/** Value of an alignment attribute. Can be either horizontal or vertical.*/
struct AlignTo
{
    StringWithPosList blocks; ///<Which blocks to align to.
    EdgePos     edge;         ///<Which edge of the block to align to.
    EAlignPrio  prio;         ///<The priority of the alignment - to break ties
    bool        justify=false; ///<True if our edge aligns to the edge of this box and we need to insert justification space. Set to true in AdjustAttributes()
    bool Parse(std::string_view text, EdgePos def, bool horizontal, EAlignPrio p,
               MscError *error, const FileLineCol *l, std::string_view whattoignore);
    bool IsSame(const AlignTo &o) const;
    bool IsParent() const { return blocks.size()==1 && blocks.front().name==STRING_PARENT; } ///<If user specified only a number (means relative to the parent)
};

OptAttr<std::array<AlignTo, 2>>
ParseCoord(std::string_view text, MscError *error, const FileLineCol *l, std::string_view whattoignore);

/** An alignment attribute value, which may or may not be set. */
struct OptAlignAttr
{
    bool is_set = false;   ///<True if the attribute is set. If false, the value is to be ignored.
    bool is_margin = false;///<True if our margin shall be aligned, false if our visible side. Ignored on Mid or if is_set is false.
    EStyleType set_from{}; ///<Whether set from a style or as an attribute (for better error messages)
    AlignTo value;         ///<The value to align to. Valid only if 'set' is true.
    std::string text{};    ///<Human readable text of the attribute, without its priority

    void SetText(std::string &&attrname);
    /** Returns true if we are actually set to a non-empty value.*/
    bool IsSetToNonEmpty() const { return is_set && value.blocks.size()>0; }
    /** Returns true if we are explicitly cleared.*/
    bool IsSetToEmpty() const { return is_set && value.blocks.size()==0; }
};


bool CshHintGraphicCallbackForHAlignment(Canvas *canvas, CshHintGraphicParam p, CshHintStore &hs);
bool CshHintGraphicCallbackForVAlignment(Canvas *canvas, CshHintGraphicParam p, CshHintStore &hs);
bool CshHintGraphicCallbackForVia(Canvas *canvas, CshHintGraphicParam p, CshHintStore &hs);
bool CshHintGraphicCallbackForContentX(Canvas *canvas, CshHintGraphicParam p, CshHintStore &hs);
bool CshHintGraphicCallbackForContentY(Canvas *canvas, CshHintGraphicParam p, CshHintStore &hs);
bool CshHintGraphicCallbackForRouting(Canvas *canvas, CshHintGraphicParam p, CshHintStore &hs);
bool CshHintGraphicCallbackForLabelPos(Canvas *canvas, CshHintGraphicParam p, CshHintStore &hs);
bool CshHintGraphicCallbackForLabelAlign(Canvas *canvas, CshHintGraphicParam p, CshHintStore &hs);
bool CshHintGraphicCallbackForLabelOrient(Canvas *canvas, CshHintGraphicParam p, CshHintStore &hs);

class BlockStyle;

/** Contains all alignment attributes of a block.*/
struct AlignmentAttr
{
    static const char * const align_attr_names_descr[];
    static const char * const xalign_attr_values_descr[];
    static const char * const yalign_attr_values_descr[];
    static const double positions[];
    static void SideValuesAfterAt(Csh &csh, bool y, std::string_view prefix);
    OptAlignAttr Top;     ///<How the top of the block shall be aligned
    OptAlignAttr Bottom;  ///<How the bottom of the block shall be aligned
    OptAlignAttr Left;    ///<How the left of the block shall be aligned
    OptAlignAttr Right;   ///<How the right of the block shall be aligned
    OptAlignAttr XCenter; ///<How the center of the block shall be aligned
    OptAlignAttr YMiddle; ///<How the middle of the block shall be aligned
    /** Set all values to unset. */
    void Empty() noexcept { Top.is_set = Bottom.is_set = Left.is_set = Right.is_set = XCenter.is_set = YMiddle.is_set = false; }
    bool IsEmpty() const noexcept { return !(Top.is_set || Bottom.is_set || Left.is_set || Right.is_set || XCenter.is_set || YMiddle.is_set); }
    AlignmentAttr() noexcept { Empty(); }
    /** Make all incomplete parts complete. We do not do anything, this has no default.*/
    void MakeCompleteButText() noexcept {}
    AlignmentAttr &MergeByDimension(const AlignmentAttr &toadd);
    AlignmentAttr &MergeByDimension(const AlignmentAttr &toadd, const BlockStyle &s);
    bool AddAttribute(const Attribute &a, EStyleType t, Chart *, EAlignPrio prio);
    static void AttributeNames(Csh &csh, std::string_view group_descr);
    static bool AttributeValues(std::string_view attr, Csh &csh);
    static int AttributeNameDir(std::string_view aname);
    void SetTexts();
    void SetAllPriority(EAlignPrio p) noexcept { for (int i = 0; i<6; i++) Get(i).value.prio = p; }
    /** Get the attribute governing left (y false) or top (y true) alignment.*/
    OptAlignAttr &GetMin(bool y) noexcept { return y ? Top : Left; }
    /** Get the attribute governing center (y false) or middle (y true) alignment.*/
    OptAlignAttr &GetMid(bool y) noexcept { return y ? YMiddle : XCenter; }
    /** Get the attribute governing right (y false) or bottom (y true) alignment.*/
    OptAlignAttr &GetMax(bool y) noexcept { return y ? Bottom : Right; }
    /** Get one of the attributes: 0=left, 1=top, 2=xcenter, 3=ymiddle, 4=right, 5=bottom.
     * Useful to cycle through the attributes.
     * Note that (0,1) are mins, (2,3) are mids, (4,5) are maxes. Also even are x coords, odd are y coords.*/
    OptAlignAttr &Get(unsigned u) noexcept { return u/2==2 ? GetMax(bool(u&1)) : u/2 ? GetMid(bool(u&1)) : GetMin(bool(u&1)); }
    /** Get the attribute governing left (y false) or top (y true) alignment.*/
    const OptAlignAttr &GetMin(bool y) const noexcept { return y ? Top : Left; }
    /** Get the attribute governing center (y false) or middle (y true) alignment.*/
    const OptAlignAttr &GetMid(bool y) const noexcept { return y ? YMiddle : XCenter; }
    /** Get the attribute governing right (y false) or bottom (y true) alignment.*/
    const OptAlignAttr &GetMax(bool y) const noexcept { return y ? Bottom : Right; }
    /** Get one of the attributes: 0=left, 1=top, 2=xcenter, 3=ymiddle, 4=right, 5=bottom.
     * Useful to cycle through the attributes. */
    const OptAlignAttr &Get(unsigned u) const noexcept { return u/2==2 ? GetMax(bool(u&1)) : u/2 ? GetMid(bool(u&1)) : GetMin(bool(u&1)); }
};

/** Ways to align the content of an element inside the element
 * in case the element is larger than its content.*/
enum class EInternalAlignType : unsigned char
{
    Min=1, ///<Left or top side
    Mid,   ///<Centerline
    Spread,///<Add margins to spread from min to max
    Exact, ///<Our size must be exactly the same as the content
    Max    ///<Right or bottom side
};

/** Describes how to internally align content in a block. */
struct InternalAlignment
{
    EInternalAlignType align;    ///<How the children shall be aligned within the parent
    EAlignPrio         prio;     ///<The priority of the alignment - to break ties
    FileLineCol        file_pos; ///<The actual position of the attribute
};

/** A size attribute (for width and height) with a priority
 * And perhaps a list of blocks to take as value.*/
struct SizeAttr {
    double size;                      ///<Pixel size specified by the user. If 'blocks' is non-empty, it is a multiplier of the size of the bounding box of those blocks.
    EAlignPrio prio;                  ///<The priority of this attribute (style, explicit, etc.)
    bool other_dim_of_blocks = false; ///<If set than the size specified here (e.g., for width) shall be taken from othe other dimension (that is, height) of the blocks listed.
    StringWithPosList blocks = {};    ///<If this is non-empty, use the specific double value as a multiplier
};

/** An optional size attribute (for width and height) with a priority */
using OptSizeAttr = std::optional<SizeAttr>;

/** Describes how to interpret the 'size' attribute. */
enum class ELabelMode
{
    INVALID,
    ENLARGE,  ///<If the label does not fit into the proscribed size, make the block larger
    SCALE,    ///<If the label does not fit into the proscribed size, make the text smaller, but keep aspect ratio
    SCALE_2D, ///<If the label does not fit into the proscribed size, make the text smaller, maybe differently in the two dimensions
};

/** Describes, how arrows are laid out. */
enum class EArrowRouting
{
    Invalid,
    Horizontal,///<Strictly horizontal
    Vertical,  ///<Strictly vertical
    Grid,      ///<Either Horizontal or Vertical
    Straight,  ///<Straight, disregard any blocks in the way
    Polygon,   ///<Polygon with potential rounded or beveled corners
    Curvy,     ///<Smooth line
    Manhattan  ///<Only 90 degree edges
};

/** Further differentiator for BlockStyles of type EStyleType::STYLE to express
 * how do we use the style.*/
enum class EBlockStyleType
{
    Unspecified, ///<No change, or not relevant
    Generic,     ///<This is a generic style defined by the user that can be applied here or there.
    Content,     ///<This is a content style applied as a default to the running style
    Running      ///<This is a running style
};

/** Specifies if the style applies to a block or an arrow*/
enum class EBlockStyleTarget
{
    Any,
    Block,
    Arrow
};

/** Style for Block charts.
 * Note that we use EStyleType::OPTION for the running style. */
class BlockStyle : public SimpleStyleWithArrow<TwoArrowHeads>
{
    bool f_label;           ///<Flag specifying if this style has labels, label.pos, label.align, label.orient
    bool f_alignment;       ///<Flag specifying if this style has alignment attributes "top", "bottom" etc,
    bool f_content;         ///<Flag specifying if this style has attributes governing children alignment "content.*" and "allow_routing" and "indicator"
    bool f_margin;          ///<Flag specifying if this style has margin attributes "margin.*" "imargin.*", "width", "height", "size" and "multi.*"
    bool f_routing;         ///<Flag specifying if this style has 'routing' attribute
    bool f_marker;          ///<Flag specifying if this style has 'marker' attributes
public:
    EBlockStyleType            block_style_type;      ///<If type==EStyleType::STYLE, this specifies exactly what kind of style we are. Should be set to Unspecified if type!=STYLE
    EBlockStyleTarget          block_style_target;    ///<What kind of element the style applies to
    OptAttrLine<std::string>   label;                 ///<The label of this element
    AlignmentAttr              alignment;             ///<Our alignment
    OptAttr<InternalAlignment> XChildAlign;           ///<Describes how our content shall be aligned within us horizontally
    OptAttr<InternalAlignment> YChildAlign;           ///<Describes how our content shall be aligned within us vertically
    OptAttr<bool>              content_margin;        ///<If true, we respect the margin of the content when trying to encircle them. Else not.
    OptAttr<double>            min_margin[2];         ///<Margin on the left ([0]) and top ([1]) side outside the block
    OptAttr<double>            max_margin[2];         ///<Margin on the left ([0]) and top ([1]) side outside the block
    OptAttr<double>            min_imargin[2];        ///<Margin on the left ([0]) and top ([1]) side inside the block
    OptAttr<double>            max_imargin[2];        ///<Margin on the left ([0]) and top ([1]) side inside the block
    OptAttr<double>            label_imargin;         ///<Margin between the label and the content of the block
    OptAttr<bool>              allow_arrows;          ///<True if we allow arrows to cross us
    OptSizeAttr                size[2];               ///<Specific width ([0]) and height ([1])
    OptAttr<double>            aspect_ratio;          ///<Specifies the aspect ratio: width/height. E.g., 3:2 will be 1.5.
    OptAttrLine<ELabelMode>    label_mode;            ///<How to interpret the size attribute for label-only blocks
    OptAttr<double>            multi_offset[2];       ///<Specifies what offset to apply with the "multi" keyword
    OptAttrLine<EDirection>    label_pos;             ///<Position of the label of a box relative to its content
    OptAttr<double>            label_align;           ///<left/center/right alignment of label
    OptAttr<EOrientation>      label_orient;          ///<How to orient the label. Above is normal...
    OptAttr<EArrowRouting>     routing;               ///<Routing method of an arrow
    OptAttr<StringWithPosList> route_cross;           ///<Blocks arrows can freely route across
    OptAttr<StringWithPosList> route_cross_parent;    ///<Blocks arrows can freely route across, including their children
    OptAttr<double>            distance;              ///<Distance to be kept by arrows from blocks
    OptAttr<StringWithPosNumberList>
                               distance_per_block;    ///<Distance to be kept by arrows from specific blocks
    OptAttr<double>            route_factor;          ///<The strategy of selecting between paths. 0=length only, 1=turns only.
    OptAttr<double>            route_arrow_distance;  ///<The minimum distance to keep to other arrows. If zero, don't de-overlap this arrow.
    OptAttr<bool>              route_block_others;    ///<If true, we add our path to blocking arrows laid out subsequent to us in the same de-overlap group. Only if we were de-overlapped
    OptAttr<bool>              route_linearextend;    ///<If true, we end at a block, get de-overlapped and our new end is not inside the block, but it was before the de-overlapping and our linear extension crosses the block - we linearextend us up until the block.
    OptAttr<bool>              route_clip_block;      ///<If true, any arrow at the end of the block clips the arrow & we do de-overlapping.
    OptAttr<bool>              routing_try_harder;    ///<If true, We do an exhaustive search on best layout at de-overlapping.
    OptAttr<bool>              routing_allow_joint_layout; ///<If true, we allow laying out jointly with other arrows if the initial layout was equally the same.
    OneArrowHead               marker;                ///<If true and we have de-overlapped this arrow its path will be added to the blocking parts of its peers.
	StringWithPosList          draw_time;             ///<If not empty, the value of the 'draw_before' or 'draw_after' attribute(s). A block(s), we need to be drawn before/after.
	bool                       draw_time_is_before;   ///<Tells us if 'draw_time' is before or after
    OptAttr<bool>              indicator;             ///<If collapsed blocks show an indicator or not

    BlockStyle(EStyleType tt, EBlockStyleType btt, EBlockStyleTarget ttgt, EColorMeaning cm, bool a,
               bool t, bool l, bool f, bool s, bool nu, bool shp,
               bool lab, bool al, bool c, bool m, bool r, bool mkr); ///Control which components are used
    explicit BlockStyle(EStyleType tt, EBlockStyleTarget ttgt = EBlockStyleTarget::Any, EColorMeaning cm = EColorMeaning::NOHOW); //Has all the components, but is empty
    explicit BlockStyle(EBlockStyleType btt = EBlockStyleType::Generic, EBlockStyleTarget ttgt = EBlockStyleTarget::Any, EColorMeaning cm = EColorMeaning::NOHOW); //Has all the components, but is empty
    BlockStyle(const BlockStyle&) = default;
    BlockStyle(BlockStyle&&) = default;
    BlockStyle &operator=(const BlockStyle&) = default;
    BlockStyle &operator=(BlockStyle&&) = default;
    void Empty() override;
    bool IsEmpty() const noexcept override;
    void MakeCompleteButText() override;
    EAlignPrio PrioOnAttributeAdd() const;
    Style &operator +=(const Style &toadd) override;
    bool AddAttribute(const Attribute &a, Chart *) override;
    void AttributeNames(Csh &csh) const override;
    bool AttributeValues(std::string_view attr, Csh &csh) const override;
	void Push(const FileLineCol &l);
};

class BlockBlock;

/** This holds the parent block of the current context.
 * Alternatively, during parsing of clone action modifier lists it contains the name of the
 * (not yet existing) clone. */
struct Parent : std::variant<const BlockBlock*, std::string> {
    using std::variant<const BlockBlock*, std::string>::variant;
    using std::variant<const BlockBlock*, std::string>::operator=;
    Parent() noexcept : std::variant<const BlockBlock*, std::string>(nullptr) {}
    const BlockBlock* AsBlock() const { return std::holds_alternative<const BlockBlock*>(*this) ? std::get<const BlockBlock*>(*this) : nullptr; }
    std::string_view AsName() const { return std::holds_alternative<std::string>(*this) ? std::get<std::string>(*this) : std::string_view{}; }
    std::string_view GetName() const;
};

/** Context for Graphs. Just a style set and colors, really.*/
class BlockContext : public ContextBase<BlockStyle>
{
public:
    StyleCoW<BlockStyle> running_style_blocks; ///<The running style of this context. Added to all Blocks.
    StyleCoW<BlockStyle> running_style_arrows; ///<The running style of this context. Added to all Arrows.
    Parent parent;                   ///<The parent block of the current context or the name of the block we clone into
    FillAttr background;             ///<The fill of the main block. Added here so that it can be part of designs.
	OptAttr<bool> pedantic;          ///<If false, we allow block definitions via arrows

    /** This constructor is used to create an empty context or one set to plain.
     * Used an initializing the first context at parse, when starting to record
     * a design, storing a procedure or moving a newly defined design to the design
     * store.
     * @param [in] f If true, the context contains a value for all styles and attributes (Full)
     * @param [in] p Tells us what components to observe and how to behave during parsing.
     * @param [in] t Tells us with what content to create the context. It should not be 'COPY
     * @param [in] l The first character of the context in the input file.*/
    BlockContext(bool f, EContextParse p, EContextCreate t, const FileLineCol &l) :
        ContextBase<BlockStyle>(f, p, EContextCreate::CLEAR, l),
        running_style_blocks(std::make_unique<BlockStyle>(EStyleType::STYLE, EBlockStyleType::Running, EBlockStyleTarget::Block, EColorMeaning::FILL, false,
                                                          true, true, true, true, true, true, true, true, true, true, false, false)), //no arrow, routing, marker
        running_style_arrows(std::make_unique<BlockStyle>(EStyleType::STYLE, EBlockStyleType::Running, EBlockStyleTarget::Arrow, EColorMeaning::LINE_ARROW_TEXT, true,
                                                          true, true, false, false, true, false, true, false, false, false, true, true)) //just arrow, text, line and number, label, routing and marker
    { switch (t) {
        default: _ASSERT(0); FALLTHROUGH;
        case EContextCreate::PLAIN: Plain(); break;
        case EContextCreate::EMPTY: Empty(); break;
        case EContextCreate::CLEAR: background.Empty();}}
    /** This constructor is used when a context needs to be duplicated due to
     * the opening of a new scope. You can change the parse mode, e.g., when
     * parsing the not-selected branch of an ifthenelse.*/
    BlockContext(const BlockContext &o, EContextParse p, const FileLineCol &l) :
        ContextBase<BlockStyle>(o, p, l),
        running_style_blocks(o.running_style_blocks), running_style_arrows(o.running_style_arrows),
        parent(o.parent), background(o.background), pedantic(o.pedantic) {}
    /** Semi copy operator: copy only user-defined context content. If o is full, we overwrite us, else just merge.*/
    void ApplyContextContent(const BlockContext &o);
    /** Semi move operator: move only user-defined context content. If o is full, we overwrite us, else just merge.*/
    void ApplyContextContent(BlockContext &&o);
    void Empty() override;
    void Plain() override;
    std::string_view GetPrefix() const { return parent.GetName(); }
};


/** Removes from a vector. A shorthand. Returns true if something has been removed.*/
template <typename Element, typename Pred>
bool remove_if(std::vector<Element> &v, Pred p)
{
    if (v.size()<1) return false;
    const auto i = std::remove_if(v.begin(), v.end(), p);
    const bool ret = i!=v.end();
    v.erase(i, v.end());
    return ret;
}

/** Removes from a vector. A shorthand. Returns true if something has been removed.*/
template <typename Element, typename Value>
bool remove(std::vector<Element> &v, Value val)
{
    if (v.size()<1) return false;
    const auto i = std::remove(v.begin(), v.end(), val);
    const bool ret = i!=v.end();
    v.erase(i, v.end());
    return ret;
}

/** Removes from a vector. A shorthand. Returns true if something has been removed.
 * If 'keep_last' is true, we keep the last one of equal elements, else the first.
 * We do not assume any a priori ordering of elements.*/
template <typename Element, typename Pred>
bool remove_duplicates(std::vector<Element> &v, Pred p, bool keep_last=false)
{
    bool ret = false;
    for (size_t i = 0; i+1<v.size(); i++)
        for (size_t j = i+1; j<v.size(); j++)
            if (!p(v[i], v[j]))
                continue;
            else if (keep_last) {
                ret = true;
                //erase the element with smaller index
                v.erase(v.begin()+i);
                i--; //will be incremented by outer loop
                break; //cancel inner loop
            } else {
                ret = true;
                v.erase(v.begin()+j);
                j--; //will be incremented by inner loop
            }
    return ret;
}




}; //namespace

#endif //BLOCK_STYLE_H
