/*
    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 steps.h The declaration for the FlowChart steps, branches, etc.
* @ingroup libflow_files */

#ifndef FLOW_STEPS_H
#define FLOW_STEPS_H

#include "element.h"
#include "flowstyle.h"

namespace flow {

class FlowChart;
class StepList;

class FlowElement : public Element
{
public:
    FlowChart * const chart;
    StyleCoW<FlowStyle> style;
    std::string label;
    Label parsed_label;
    double label_sx, label_dx, label_cx, label_y;
    FlowElement(FlowChart *c) : chart(c) {}
    virtual void AddRefName(std::string_view name) = 0;
    /** Applies an Attribute to us.
     * We accept no attributes here.
     * @param [in] a The attribute to add.
     * @return true if we recognized the attribute as ours.*/
    virtual bool AddAttribute(const Attribute &a);
    /** Add the attribute names we take to `csh`.*/
    static void AttributeNames(Csh &);
    /** Add a list of possible attribute value names to `csh` for attribute `attr`.*/
    static bool AttributeValues(std::string_view /*attr*/, Csh &/*csh*/);
    virtual FlowElement *AddAttributeList(AttributeList *al);
    /** Adds to a steplist, returns true if we continue, false if gotoed away. */
    virtual bool AddToStepList(size_t col, std::unique_ptr<FlowElement> &&self) = 0;
    virtual void PreLayout(Canvas &c);
    virtual void FlowPostPosProcess(Canvas &c);
    virtual void Draw(Canvas&) {}
};

typedef UPtrList<FlowElement> FElementList;

class Step;
class Arrow;

/* The flowchart constists of steps and arrows.
 * Steps can be
 * - Start
 * - End (stop)
 * - An action (box)
 * - A condition (if)
 * These only differ in appearance, any or all of them can branch
 * The steps are arranged in columns (from left to right by default)
 * A Node is a step plus branches.
 */

struct NodeRef {
    ptrdiff_t col = -1;
    size_t element;
    constexpr bool IsInvalid() const noexcept { return col<0; }
    constexpr bool operator == (const NodeRef&o) const noexcept { return col==o.col && (col==-1 || element==o.element); }
};

/** A Step potentially with branches.
 * One branch is special: the continuation one.*/
class FlowNode {
public:
    std::unique_ptr<Step> step;
    std::unique_ptr<Arrow> next;
    std::vector<std::unique_ptr<Arrow>> branches;
    void FillInSources() const;
    void Route();
    virtual void FlowPostPosProcess(Canvas &);
    void Draw(Canvas &canvas);
};

/** A list of Nodes: basically steps following each other.*/
class StepList
{
public:
    size_t id;//zero-based ID to be used in permutations
    EDir dir;   //Which direction do we lay out
    XY   xyPos; //Startpos
    Block size; //Laid out size
    std::vector<FlowNode> nodes;
    void Layout(EDir d);
    void ShiftNodesTo(XY pos);
};

struct Pos {
    ptrdiff_t elem; //The column number (for x pos) or the element number in the column (for y pos)
    int side; //+1 for the bottom/right (or top/left if sdir/ndir are INV), -1 for opposite, 0 for center
};

/** Represents a turn of an arrow. */
struct KneePos
{
    Pos col, element;
    KneePos(int c, int c_side, int e, int e_side) noexcept
        : col{c, c_side}, element{e, e_side} {}
    KneePos(const NodeRef &r, int c_side=0, int e_side=0) noexcept
        : col{r.col, c_side}, element(r.element, e_side) {}
};

/** Represents an arrow. */
class Arrow : public FlowElement
{
public:
    NodeRef source, target;
    ELogicalDir requested_source_port_side;
    ELogicalDir decided_source_port_side, decided_target_port_side;
    std::vector<KneePos> turns;
    Path path;
    TwoArrowHeadsInstance arrowheads;
    Arrow(FlowChart *f);
    virtual void AddRefName(std::string_view) override { _ASSERT(0); }
    virtual bool IsBranch() const noexcept { return false; };
    FlowElement *AddAttributeList(AttributeList *al) override;
    /** Add the attribute names we take to `csh`.*/
    static void AttributeNames(Csh &);
    /** Add a list of possible attribute value names to `csh` for attribute `attr`.*/
    static bool AttributeValues(std::string_view/*attr*/, Csh &/*csh*/);
    bool AddToStepList(size_t, std::unique_ptr<FlowElement>&&) override { _ASSERT(0); return false; }
    void Route();
    void FlowPostPosProcess(Canvas &) override;
    void Draw(Canvas &c) override;
};

/** Represents one step of a flowchart.
 * An action (box) a condition (if) or star/stop*/
class Step : public FlowElement
{
public:
    enum Type {BOX, IF, START, STOP} type;
    struct KeywordHelper {
        Type type;
        str_view keyword;
        bool must_have_label;
    };
    std::unique_ptr<Arrow> prev_arrow; //The arrow leading to us
    XY center;
    Block outer_edge;
    std::string refname;
    NodeRef my_location;
    Contour clip_by_arrows;
    Step(FlowChart *f, Type t);
    void AddRefName(std::string_view name) override { refname = name; }
    /** Add the attribute names we take to `csh`.*/
    static void AttributeNames(Csh &);
    /** Add a list of possible attribute value names to `csh` for attribute `attr`.*/
    static bool AttributeValues(std::string_view/*attr*/, Csh &/*csh*/);
    bool AddToStepList(size_t col, std::unique_ptr<FlowElement> &&self) override;
    void PreLayout(Canvas & canvas) override;  //creates outline in Area at pos(0,0)
    virtual void ShiftBy(const XY &xy) override { Element::ShiftBy(xy); center += xy; outer_edge.Shift(xy); }
    void ShiftTo(const XY &xy) { ShiftBy(xy-center); }
    XY GetSidePoint(EDir d) const; ///Gets the xy coordinate of our north, south east or west point. To start/end arrows at.
    void Draw(Canvas &c) override;
};

class Repeat : public Arrow
{
public:
    FElementList instructions;
    Repeat(FlowChart *f, FElementList *fl) : Arrow(f) { if (fl) { instructions = std::move(*fl); delete fl; } }
    void AddRefName(std::string_view name) override { if (instructions.size())  instructions.front()->AddRefName(name); }
    bool AddToStepList(size_t col, std::unique_ptr<FlowElement> &&self) override;
    void PreLayout(Canvas & canvas) override;
};

class Branch : public Arrow
{
public:
    FElementList instructions;
    Branch(FlowChart *f) : Arrow(f) {}
    bool IsBranch() const noexcept override { return true; }
    Branch *AddLabel(std::string_view l) { label = l; return this; }
    Branch *AddSteps(FElementList *l) { if (l) { instructions.Append(l); delete l; } return this; }
    Branch *AddStep(FlowElement *l) { if (l) { instructions.Append(l); } return this; }
    bool AddToStepList(size_t col, std::unique_ptr<FlowElement> &&self) override;
    void PreLayout(Canvas & canvas) override;

};

class Goto : public Arrow
{
public:
    const std::string target_str;
    Goto(FlowChart *f, std::string_view step) : Arrow(f), target_str(step) {}
    bool AddToStepList(size_t col, std::unique_ptr<FlowElement> &&self) override;
};





}; //namespace

#endif //FLOW_STEPS_H

