/*
    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 gvstyle.h Graph specific style and context declarations.
* @ingroup libgvgen_files */

#ifndef GVSTYLE_H
#define GVSTYLE_H

#include "style.h"

namespace graph {

/** Type defining edge operators */
struct GraphEdgeType 
{
    enum {
        SOLID, DOUBLE, DOTTED, DASHED, SOLID2 /*->>*/, DOUBLE2 /*==>*/
    } type; ///<The style of the edge (solid, dotted, etc.)
    enum {
        NO_ARROW, FWD, BACK, BIDIR
    } dir; ///<The directionality of the edge
    std::string_view AsText() const; 
};



/** A structure containing descriptions for Graphviz attributes.*/
struct DotAttributeDescription
{
    const char *name;        ///<The name of the attribute
    const char *used_by;     ///<A string of 'GCSEN' characters to indicate what type of element this attribute applies to: Graphs, Clusters, Subgraphs, Edges and/or Nodes.
    const char *type;        ///<What is the type of this attribute
    const char *def;         ///<Human readable text telling what this attr is.
    const char *minimum;     ///<The minimum value
    const char *notes;       ///<Some notes, like 'dot only'
    const char *description; ///<A more detailed description.
};

/** A list of Graphviz attributes, with description info. We use this to generate hint tooltips.*/
extern const DotAttributeDescription DotAttributes[];

/** Generates the tooltip text for a graphviz attribute. */
std::string DotAttributeDescriptionText(const DotAttributeDescription *aa);

/** Is this attribute an extension added by msc-generator?
 * (In this case we do not add it to graphviz */
bool IsDotAttributeAnExtension(const DotAttributeDescription *);

/** One attribute mentioned in a graph input file.*/
struct GraphAttribute
{
    std::string value;                    ///<The value of the attribute
    FileLineCol linenum_attr;             ///<Location of the name of the attribute in the input file
    FileLineCol linenum_value;            ///<Location of the value of the attribute in the input file
    const DotAttributeDescription *descr; ///<The actual attribute (its name, too). 
    Attribute GenerateAttribute(std::string_view name, bool attempt_number) const;
};

/** A struct holding a comma-separated style attribute */
struct ParsedStyle
{
    std::string line;
    std::string fill;
    std::set<std::string, std::less<>> others;
    ParsedStyle() = default;
    void Parse(std::string_view);
    operator string() const;
};

/** Style for graphviz graphs.*/
class GraphStyle : public Style
{
    friend class GraphCsh;
public:
    /** What kind of an object we can have an attribute for.
     * These need to have these specific numbers, to match 
     * the global constant 'attr_types_code' defined in gvstyle.cpp. */
    enum EGraphElementType { 
        GRAPH = 0,    ///<A full graph. These attributes will also added to cluster subgraphs.
        CLUSTER = 1,  ///<A visible subgraph.
        SUBGRAPH = 2, ///<A non-visible subgraph. Actually only the 'rank' attribute applies to such subgraphs.
        NODE = 3,     ///<A node of the graph.
        EDGE = 4,     ///<An edge of the graph.
        ANY = 5,      ///<Generic style holding all and any type of attribute
    };
protected: 
    /** Find an attribute based on its name. */
    static const DotAttributeDescription *FindThisAttribute(std::string_view);
    /** Return true, if an attribute applies to an object using a given layout. */
    static bool DoesThisAttributeApply(const DotAttributeDescription *aa, EGraphElementType type, std::string_view layout);
public:
    const EGraphElementType element_type; ///<What kind of object are we a style for
    GraphStyle(EStyleType tt, EGraphElementType t=ANY) : Style(tt), element_type(t) {} 
    GraphStyle(const GraphStyle&) = default;
    GraphStyle(GraphStyle&&) = default;
    GraphStyle &operator=(const GraphStyle&) = default;
    GraphStyle &operator=(GraphStyle&&) = default;
    std::map<std::string, GraphAttribute> attributes; ///<List of attributes 
    void Empty() override { attributes.clear(); }
    bool IsEmpty() const noexcept override { return attributes.empty() && Style::IsEmpty(); }
    void MakeCompleteButText() override {}
    Style &operator +=(const Style &toadd) override;
    bool AddAttribute(const Attribute &a, Chart *) override;
    const GraphAttribute *FindInErrorText(const std::string &text) const;
    bool DoIAcceptUnqualifiedColorAttr() const override { return false; }
    void AttributeNames(Csh &csh) const override { DotAttributeNames(element_type, {}, csh); }
    bool AttributeValues(std::string_view attr, Csh & csh) const override { return DotAttributeValues(element_type, attr, {}, csh); }
    static void DotAttributeNames(EGraphElementType type, std::string_view layout, Csh &csh);
    static bool DotAttributeValues(EGraphElementType type, std::string_view attr, std::string_view layout, Csh &csh);
    static bool CheckBoolValue(const char *value, bool &result);
    static void BooleanValueError(const Attribute &a, MscError &error);
};

/** Context for Graphs. Just a styleset and colors, really.*/
class GraphContext : public ContextBase<GraphStyle>
{
public:
    OptAttr<bool> pedantic;  ///<Determines if parsing is pedantic (digraph warns on unidrected arrow and vice versa)
    /** 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.*/
    GraphContext(bool f, EContextParse p, EContextCreate t, const FileLineCol &l) 
        : ContextBase<GraphStyle>(f, p, EContextCreate::CLEAR, l), pedantic(false)
    { switch (t) {
        default: _ASSERT(0); FALLTHROUGH;
        case EContextCreate::PLAIN: Plain(); break;
        case EContextCreate::EMPTY: Empty(); break; 
        case EContextCreate::CLEAR: pedantic.reset();}}
    /** 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.*/
    GraphContext(const GraphContext &o, EContextParse p, const FileLineCol &l) :
        ContextBase<GraphStyle>(o, p, l), 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 GraphContext &o);
    /** Semi move operator: move only user-defined context content. If o is full, we overwrite us, else just merge.*/
    void ApplyContextContent(GraphContext &&o);
    void Empty() override;
    void Plain() override;
    /** Set the font in the "edge", "graph" and "node" styles.*/
    void SetDefaultFont(const std::string& face, Chart* chart, const FileLineColRange& l);
};

} //namespace graph

#endif //GVSTYLE_H
