/*
    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 csh.h The declaration of classes for Color Syntax Highlighting.
 * @ingroup libmscgen_files */

#ifndef MSCCSH_H
#define MSCCSH_H

#include "csh.h"

namespace msc {

class MscContext;

/** This message is given when a user specifies multiple asterisks in an arrow.*/
#define MULTIPLE_ASTERISK_ERROR_MSG "One arrow may be lost only once. Use a single asterisk ('*')."

/** A class collecting syntax coloring info and hints during a csh parse.
 *
 * A few words on how hints are collected.
 * Often we realize what is the type of hint in a low-level yacc rule, but know no specifics.
 * For example, we have yacc rues describing how an attribute looks like, that is "name = value".
 * In this rule we can realize that the cursor is inside value (or we match to a rule saying
 * "name =" and realize that the cursor is just after). Here we can conclude that the hint
 * will be a sort of attribute value. (So we pick EHintType of HINT_ATTRVALUE.) But we do not know
 * what values the attribute can take, because that depends on what arc we are parsing. So after we match
 * the low level rule, we set the hint type only and when processing the higher level rule for the
 * full arc, do we fill in the actual hints. In the meantime we set the hintStatus member to
 * HINT_LOCATED. Some cases we cannot fill in all the hints in one go and must go to an even higher
 * rule, then we use HINT_FILLING. Specifically for attribute values we also store the name
 * of the attribute in the low level rule into hintAttrName because it is needed in the high
 * level rule to select which attribute we are hinting the values of.*/
class MscCsh : public Csh
{
protected:
    /** @name Options, keywords, attributes to color (and hint)
    * @{ */
    std::set<std::string> mscgen_option_names;
    std::set<std::string> mscgen_attribute_names;
    void FillNamesHints() override;
    /** @}*/
    bool DoHintLocated(EHintSourceType hsource, std::string_view a_name) override;
    void BeforeYaccParse(std::string&& input, CharByteIndex first_pos, CharByteIndex cursor_pos) override;
    void AfterYaccParse() override;
public:
    /** @name The collected CSH info and hints
     * @{ */
    std::set<std::string>  MarkerNames;          ///<The names of markers defined so far
    unsigned               asteriskNo;           ///<Number of asterisks inside an arrow spec so far.
    bool                   addMarkersAtEnd;      ///<Set to true if at the end of the csh parsing we shall add the name of the markers to the hint list.
    bool                   hint_vertical_shapes; ///<Set true after the 'vertical' keyword, but false after a vertical shape keyword. Helps the vertrel_no_xpos yacc rule to determine if to hint vertical shapes or not.
    /** @}*/
    /** @name Input parameters 
     * @{  */
    EMscgenCompat mscgen_compat;           ///<How shall we approach mscgen compatibility.
    bool          pedantic_at_start;       ///<The value passed to ParseText(). We do not use it, but pass it to inlined charts, they may.
    /** @} */

    /** Initializes the Csh Object.*/
    explicit MscCsh(Csh::FileListProc proc, const LanguageCollection* languages);
    ~MscCsh() override = default;
    std::unique_ptr<Csh> Clone() const override { return std::make_unique<MscCsh>(*this); }
    void clear() override { MarkerNames.clear(); asteriskNo = 0; addMarkersAtEnd = false; }

    /** Clear all marker names and add the name of built-in markers.*/
    void ResetMarkers();
    /** Clean up after parsing a design library */
    void CleanupAfterDesignlib() override;

    /** @name Functions to add a CSH entry
     * @{  */
    void AddCSH_ErrorAfter_Mscgen(const CshPos&pos) { if (mscgen_compat != EMscgenCompat::FORCE_MSCGEN) AddCSH_ErrorAfter(pos, "This is mscgen syntax."); } ///<Notifies of an mscgen syntax, if not in compatibility mode
    void AddCSH_AttrName(const CshPos&, std::string_view name, EColorSyntaxType) override; ///<At pos there is either an option or attribute name (specified by the type). Search and color.
    void AddCSH_StyleOrAttrName(const CshPos&pos, std::string_view name) override; ///<At pos there is either an attribute name or a style. Decide and color.
    void AddCSH_EntityOrMarkerName(const CshPos&pos, std::string_view name); ///<At pos there is an entity or marker name. Search and color.
    void AddCSH_ExtvxposDesignatorName(const CshPos&pos, std::string_view name); ///<At pos there is an ext pos designator name. Search and color.
    void AddCSH_SymbolName(const CshPos&pos, std::string_view name); ///<At pos there is a symbol. Color it.
    void AddCSH_LeftRightCenterMarker(const CshPos&pos, std::string_view name); ///<At pos there is either left/right/center or a marker. Color it appropriately.
    /** @}*/
    void ParseText(std::string&& input, CharByteIndex first_char, CharByteIndex cursor_p, bool pedantic) override;
    void ParseDesignText(std::string&& input) override;
    void SwitchToMscgenCompatMode();

    /** @name Add specific items to the list of hints.
     * None of these sets hintSource, hintStatus, hintsForcedOnly
     * @{ */
    void AddEntitiesToHints() override;
    void AddOptionsToHints() override;
    void AddDesignOptionsToHints() override;
    void AddKeywordsToHints(bool includeParallel=true);
    void AddSymbolTypesToHints();
    void AddLeftRightCenterToHints();
    void AddSymbolPosToHints();
    void AddLeftRightHSpaceToHints();
    void AddVerticalTypesToHints();
    void AddLeftRightOfToHints();
    /** Add entities, keywords and option names to hint list.*/
    void AddLineBeginToHints() override { AddEntitiesToHints(); AddKeywordsToHints(false); AddOptionsToHints(); AllowAnything(); }
    void AddDesignLineBeginToHints() override;
    void AddLineBeginAndParallelToHints() { AddEntitiesToHints(); AddKeywordsToHints(true); AddOptionsToHints(); AllowAnything(); }
    void AddVertXPosSyntaxNonSelectableToHints(bool include_at);
    void AddArrowSpecifiersToHints();
    void AddStartBeforeEndAfterContentToHints();
    /** @}*/
};

bool CshHintGraphicCallbackForMscEntities(Canvas *canvas, CshHintGraphicParam /*p*/, CshHintStore &);
bool CshHintGraphicCallbackForMarkers(Canvas *canvas, CshHintGraphicParam /*p*/, CshHintStore &);

} //namespace

#endif
