// OpenSTA, Static Timing Analyzer
// Copyright (c) 2026, Parallax Software, Inc.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program 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 General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// 
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
// 
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 
// This notice may not be removed or altered from any source distribution.

#pragma once

#include "GraphClass.hh"
#include "SdcClass.hh"
#include "SearchClass.hh"
#include "StaState.hh"

namespace sta {

class StaState;
class PathEndVisitor;

class VisitPathEnds : public StaState
{
public:
  VisitPathEnds(const StaState *sta);
  // All scenes, unfiltered.
  void visitPathEnds(Vertex *vertex,
                     PathEndVisitor *visitor);
  void visitPathEnds(Vertex *vertex,
                     const SceneSet &scenes,
                     const MinMaxAll *min_max,
                     bool filtered,
                     PathEndVisitor *visitor);
  bool checkEdgeEnabled(const Edge *edge,
                        const Mode *mode) const;

protected:
  void visitClkedPathEnds(const Pin *pin,
                          Vertex *vertex,
                          const SceneSet &scenes,
                          const MinMaxAll *min_max,
                          bool filtered,
                          PathEndVisitor *visitor,
                          bool &is_constrained);
  virtual void visitCheckEnd(const Pin *pin,
                             Vertex *vertex,
                             Path *path,
                             const RiseFall *end_rf,
                             bool filtered,
                             PathEndVisitor *visitor,
                             bool &is_constrained);
  void visitCheckEndUnclked(const Pin *pin,
                            Vertex *vertex,
                            Path *path,
                            const RiseFall *end_rf,
                            bool filtered,
                            PathEndVisitor *visitor,
                            bool &is_constrained);
  void visitOutputDelayEnd(const Pin *pin,
                           Path *path,
                           const RiseFall *end_rf,
                           bool filtered,
                           PathEndVisitor *visitor,
                           bool &is_constrained);
  virtual void visitOutputDelayEnd1(OutputDelay *output_delay,
                                    const Pin *pin,
                                    Path *path,
                                    const RiseFall *end_rf,
                                    const ClockEdge *tgt_clk_edge,
                                    Path *ref_path,
                                    const MinMax *min_max,
                                    PathEndVisitor *visitor,
                                    bool &is_constrained);
  virtual void visitGatedClkEnd(const Pin *pin,
                                Vertex *vertex,
                                Path *path,
                                const RiseFall *end_rf,
                                bool filtered,
                                PathEndVisitor *visitor,
                                bool &is_constrained);
  float clockGatingMargin(const Clock *clk,
                          const Pin *clk_pin,
                          const Pin *enable_pin,
                          const RiseFall *enable_rf,
                          const SetupHold *setup_hold,
                          const Sdc *sdc);
  void visitDataCheckEnd(const Pin *pin,
                         Path *path,
                         const RiseFall *end_rf,
                         bool filtered,
                         PathEndVisitor *visitor,
                         bool &is_constrained);
  bool visitDataCheckEnd1(DataCheck *check,
                          const Pin *pin,
                          Path *path,
                          const Clock *src_clk,
                          const RiseFall *end_rf,
                          const MinMax *min_max,
                          const Pin *from_pin,
                          Vertex *from_vertex,
                          const RiseFall *from_rf,
                          bool filtered,
                          PathEndVisitor *visitor,
                          bool &is_constrained);
  virtual void visitUnconstrainedPathEnds(const Pin *pin,
                                          Vertex *vertex,
                                          const SceneSet &scenes,
                                          const MinMaxAll *min_max,
                                          bool filtered,
                                          PathEndVisitor *visitor);
  bool falsePathTo(Path *path,
                   const Pin *pin,
                   const RiseFall *rf,
                   const MinMax *min_max);
  PathDelay *pathDelayTo(Path *path,
                         const Pin *pin,
                         const RiseFall *rf,
                         const MinMax *min_max);
  ExceptionPath *exceptionTo(const Path *path,
                             const Pin *pin,
                             const RiseFall *rf,
                             const ClockEdge *clk_edge,
                             const MinMax *min_max) const;
};

// Abstract base class used by visitPathEnds to visit vertex path ends.
class PathEndVisitor
{
public:
  virtual ~PathEndVisitor() = default;
  virtual PathEndVisitor *copy() const = 0;
  // Begin visiting the path ends for a vertex / path_index.
  virtual void vertexBegin(Vertex *) {}
  // Visit a path end.  path_end is only valid during the call.
  virtual void visit(PathEnd *path_end) = 0;
  // End visiting the path ends for a vertex / path_index.
  virtual void vertexEnd(Vertex *) {}
};

} // namespace sta
