// 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 <mutex>
#include <queue>
#include <unordered_map>
#include <unordered_set>

#include "Bdd.hh"
#include "GraphClass.hh"
#include "LibertyClass.hh"
#include "Mode.hh"
#include "NetworkClass.hh"
#include "SdcClass.hh"
#include "StaState.hh"

namespace sta {

class SimObserver;

using SimValueMap = std::unordered_map<const Pin*, LogicValue>;
using EdgeDisabledCondSet = std::unordered_set<const Edge*>;
using EdgeTimingSenseMap = std::unordered_map<const Edge*, TimingSense>;
using EvalQueue = std::queue<const Instance*>;

// Propagate constants from constraints and netlist tie high/low
// connections thru gates.
class Sim : public StaState
{
public:
  Sim(StaState *sta);
  ~Sim() override;
  void copyState(const StaState *sta) override;
  void clear();
  void setMode(Mode *mode);
  // Set the observer for simulation value changes.
  void setObserver(SimObserver *observer);
  void ensureConstantsPropagated();
  void constantsInvalid();

  // Dertived by Sim from set_case_analysis or set_logic constant.
  LogicValue simValue(const Pin *pin) const;
  LogicValue simValue(const Vertex *vertex) const;
  // Constant zero/one from simulation.
  bool isConstant(const Pin *pin) const;
  bool isConstant(const Vertex *vertex) const;
  // Timing sense for the to_pin function after simplifying the
  // function based constants on the instance pins.
  TimingSense simTimingSense(const Edge *edge) const;
  // Edge is disabled by constants in condition (when) function.
  bool isDisabledCond(const Edge *edge) const;

  LogicValue evalExpr(const FuncExpr *expr,
                      const Instance *inst);
  // Timing sense for the function between from_pin and to_pin
  // after simplifying the function based constants on the pins.
  TimingSense functionSense(const Instance *inst,
                            const Pin *from_pin,
                            const Pin *to_pin);
  // Propagate liberty constant functions and pins tied high/low through
  // combinational logic and registers (ignores Sdc).
  // Used by OpenROAD/Restructure.cpp
  void findLogicConstants();

  // Edge cond (liberty "when") function is disabled (evals to zero).
  bool isDisabledCond(Edge *edge,
                      const Instance *inst,
                      const Pin *from_pin,
                      const Pin *to_pin);
  // isDisabledCond but also return the cond expression that causes
  // the disable.  This can differ from the edge cond expression
  // when the default timing edge is disabled by another edge between
  // the same pins that is enabled.
  void isDisabledCond(Edge *edge,
                      const Instance *inst,
                      const Pin *from_pin,
                      const Pin *to_pin,
                      // Return values.
                      bool &is_disabled,
                      FuncExpr *&disable_cond);
  // Edge mode function is disabled (evals zero).
  bool isDisabledMode(Edge *edge,
                      const Instance *inst);
  void isDisabledMode(Edge *edge,
                      const Instance *inst,
                      // Return values.
                      bool &is_disabled,
                      FuncExpr *&disable_cond);

  // Network edits.
  void deleteInstanceBefore(const Instance *inst);
  void makePinAfter(const Pin *pin);
  void deletePinBefore(const Pin *pin);
  void connectPinAfter(const Pin *pin);
  void disconnectPinBefore(const Pin *pin);
  void pinSetFuncAfter(const Pin *pin);
  const SimValueMap &simValues() const { return sim_value_map_; }

protected:
  void ensureConstantFuncPins();
  void recordConstPinFunc(const Pin *pin);
  void seedConstants();
  void seedInvalidConstants();
  void propagateConstants(bool thru_sequentials);
  void setConstraintConstPins(const LogicValueMap &pin_value_map);
  void setConstFuncPins();
  LogicValue pinConstFuncValue(const Pin *pin);
  void enqueueConstantPinInputs();
  void setSimValue(const Pin *pin,
                   LogicValue value);
  void setPinValue(const Pin *pin,
                   LogicValue value);
  void setSimTimingSense(Edge *edge,
                         TimingSense sense);
  void setIsDisabledCond(Edge *edge,
                         bool disabled);
  void enqueue(const Instance *inst);
  void evalInstance(const Instance *inst,
                    bool thru_sequentials);
  LogicValue clockGateOutValue(const Instance *inst);
  TimingSense functionSense(const FuncExpr *expr,
                            const Pin *input_pin,
                            const Instance *inst);
  void functionSense(const FuncExpr *expr,
                     const Pin *input_pin,
                     const Instance *inst,
                     // return values
                     TimingSense &sense,
                     LogicValue &value) const;
  void clearSimValues();

  void findDisabledEdges();
  void findDisabledEdges(const Instance *inst);
  void findDisabledEdges(const Instance *inst,
                         const Pin *pin,
                         Vertex *vertex);
  
  void removePropagatedValue(const Pin *pin);
  void propagateFromInvalidDrvrsToLoads();
  void propagateToInvalidLoads();
  void propagateDrvrToLoad(const Pin *drvr_pin,
                           const Pin *load_pin);
  DdNode *funcBddSim(const FuncExpr *expr,
                     const Instance *inst);

  Mode *mode_{nullptr};
  SimObserver *observer_{nullptr};
  bool valid_{false};
  bool incremental_{false};
  // Constants propagated by Sim.cc
  SimValueMap sim_value_map_;
  EdgeDisabledCondSet edge_disabled_cond_set_;
  EdgeTimingSenseMap edge_timing_sense_map_;
  // Cache of pins that have constant functions (tie high and tie low
  // cell instances).
  PinSet const_func_pins_;
  bool const_func_pins_valid_{false};
  // Instances that require incremental constant propagation.
  InstanceSet invalid_insts_;
  // Driver pins waiting to propagate constant to loads.
  PinSet invalid_drvr_pins_;
  // Load pins that waiting for the driver constant to propagate.
  PinSet invalid_load_pins_;
  EvalQueue eval_queue_;
  InstanceSet instances_to_annotate_;
  Bdd bdd_;
  mutable std::mutex bdd_lock_;
};

// Abstract base class for Sim value change observer.
class SimObserver : public StaState
{
public:
  SimObserver(StaState *sta);
  virtual void valueChangeAfter(const Pin *pin) = 0;
  virtual void faninEdgesChangeAfter(const Pin *pin) = 0;
  virtual void fanoutEdgesChangeAfter(const Pin *pin) = 0;
};

} // namespace sta
