// 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.

#include "Clock.hh"

#include <algorithm>
#include <cstdlib>

#include "ContainerHelpers.hh"
#include "Error.hh"
#include "Format.hh"
#include "Graph.hh"
#include "MinMax.hh"
#include "Network.hh"
#include "Sdc.hh"
#include "StringUtil.hh"
#include "TimingRole.hh"
#include "Transition.hh"

namespace sta {

static bool
isPowerOfTwo(int i);

Clock::Clock(std::string_view name,
             int index,
             const Network *network) :
  name_(name),
  pins_(network),
  leaf_pins_(network),
  index_(index)
{
  makeClkEdges();
}

void
Clock::initClk(const PinSet &pins,
               bool add_to_pins,
               float period,
               const FloatSeq &waveform,
               std::string_view comment,
               const Network *network)
{
  is_generated_ = false;
  setPins(pins, network);
  add_to_pins_ = add_to_pins;
  waveform_ = waveform;
  waveform_valid_ = true;
  period_ = period;
  setClkEdgeTimes();
  setComment(comment);
}

bool
Clock::isVirtual() const
{
  return pins_.empty();
}

void
Clock::setPins(const PinSet &pins,
               const Network *network)
{
  pins_ = pins;
  makeLeafPins(network);
}

void
Clock::makeLeafPins(const Network *network)
{
  leaf_pins_.clear();
  for (const Pin *pin : pins_)
    findLeafDriverPins(pin, network, &leaf_pins_);
}

void
Clock::setMasterClk(Clock *master)
{
  master_clk_ = master;
  waveform_valid_ = false;
}

void
Clock::makeClkEdges()
{
  for (const RiseFall *rf : RiseFall::range()) {
    clk_edges_[rf->index()] = new ClockEdge(this, rf);
  }
}

Clock::~Clock()
{
  for (size_t rf_index : RiseFall::rangeIndex())
    delete clk_edges_[rf_index];
}

void
Clock::addPin(const Pin *pin)
{
  pins_.insert(pin);
  leaf_pins_.insert(pin);
}

void
Clock::deletePin(const Pin *pin)
{
  pins_.erase(pin);
}

void
Clock::setAddToPins(bool add_to_pins)
{
  add_to_pins_ = add_to_pins;
}

void
Clock::setClkEdgeTimes()
{
  setClkEdgeTime(RiseFall::rise());
  setClkEdgeTime(RiseFall::fall());
}

void
Clock::setClkEdgeTime(const RiseFall *rf)
{
  float time = waveform_[rf->index()];
  clk_edges_[rf->index()]->setTime(time);
}

const Pin *
Clock::defaultPin() const
{
  auto itr = leaf_pins_.begin();
  if (itr != leaf_pins_.end())
    return *itr;
  else
    return nullptr;
}

ClockEdge *
Clock::edge(const RiseFall *rf) const
{
  return clk_edges_[rf->index()];
}

void
Clock::setIsPropagated(bool propagated)
{
  is_propagated_ = propagated;
}

void
Clock::slew(const RiseFall *rf,
            const MinMax *min_max,
            // Return values.
            float &slew,
            bool &exists) const
{
  slews_.value(rf, min_max, slew, exists);
}

float
Clock::slew(const RiseFall *rf,
            const MinMax *min_max) const
{
  float slew;
  bool exists;
  slews_.value(rf, min_max, slew, exists);
  if (!exists)
    slew = 0.0;
  return slew;
}

void
Clock::setSlew(const RiseFallBoth *rf,
               const MinMaxAll *min_max,
               float slew)
{
  slews_.setValue(rf, min_max, slew);
}

void
Clock::setSlew(const RiseFall *rf,
               const MinMax *min_max,
               float slew)
{
  slews_.setValue(rf, min_max, slew);
}

void
Clock::removeSlew()
{
  slews_.clear();
}

void
Clock::setSlewLimit(const RiseFallBoth *rf,
                    PathClkOrData clk_data,
                    const MinMax *min_max,
                    float slew)
{
  slew_limits_[static_cast<size_t>(clk_data)].setValue(rf, min_max, slew);
}

void
Clock::slewLimit(const RiseFall *rf,
                 PathClkOrData clk_data,
                 const MinMax *min_max,
                 // Return values.
                 float &slew,
                 bool &exists) const
{
  slew_limits_[static_cast<size_t>(clk_data)].value(rf, min_max, slew, exists);
}

void
Clock::uncertainty(const SetupHold *setup_hold,
                   // Return values.
                   float &uncertainty,
                   bool &exists) const
{
  uncertainties_.value(setup_hold, uncertainty, exists);
}

void
Clock::setUncertainty(const SetupHoldAll *setup_hold,
                      float uncertainty)
{
  uncertainties_.setValue(setup_hold, uncertainty);
}

void
Clock::setUncertainty(const SetupHold *setup_hold,
                      float uncertainty)
{
  uncertainties_.setValue(setup_hold, uncertainty);
}

void
Clock::removeUncertainty(const SetupHoldAll *setup_hold)
{
  uncertainties_.removeValue(setup_hold);
}

void
Clock::waveformInvalid()
{
  waveform_valid_ = false;
}

////////////////////////////////////////////////////////////////

void
Clock::initGeneratedClk(const PinSet &pins,
                        bool add_to_pins,
                        Pin *src_pin,
                        Clock *master_clk,
                        int divide_by,
                        int multiply_by,
                        float duty_cycle,
                        bool invert,
                        bool combinational,
                        const IntSeq &edges,
                        const FloatSeq &edge_shifts,
                        bool is_propagated,
                        std::string_view comment,
                        const Network *network)
{
  is_generated_ = true;
  setPins(pins, network);
  add_to_pins_ = add_to_pins;
  src_pin_ = src_pin;
  master_clk_ = master_clk;
  master_clk_infered_ = false;
  waveform_valid_ = false;
  divide_by_ = divide_by;
  multiply_by_ = multiply_by;
  duty_cycle_ = duty_cycle;
  invert_ = invert;
  combinational_ = combinational;
  is_propagated_ = is_propagated;
  setComment(comment);

  edges_ = edges;
  edge_shifts_ = edge_shifts;
}

void
Clock::setInferedMasterClk(Clock *master_clk)
{
  master_clk_ = master_clk;
  master_clk_infered_ = true;
  waveform_valid_ = false;
}

bool
Clock::isGenerated() const
{
  return is_generated_;
}

bool
Clock::isGeneratedWithPropagatedMaster() const
{
  return is_generated_
    && master_clk_
    // Insertion is zero if the master clock is ideal.
    && master_clk_->isPropagated();
}

void
Clock::generate(const Clock *src_clk)
{
  waveform_.clear();
  if (divide_by_ == 1.0) {
    period_ = src_clk->period();
    const FloatSeq &src_wave = src_clk->waveform();
    waveform_.push_back(src_wave[0]);
    waveform_.push_back(src_wave[1]);
  }
  else if (divide_by_ > 1) {
    if (isPowerOfTwo(divide_by_)) {
      period_ = src_clk->period() * divide_by_;
      const FloatSeq &src_wave = src_clk->waveform();
      float rise = src_wave[0];
      waveform_.push_back(rise);
      waveform_.push_back(rise + period_ / 2);
    }
    else
      generateScaledClk(src_clk, static_cast<float>(divide_by_));
  }
  else if (multiply_by_ >= 1)
    generateScaledClk(src_clk, 1.0F / multiply_by_);
  else if (!edges_.empty())
    generateEdgesClk(src_clk);

  if (invert_) {
    float first_time = waveform_[0];
    float offset = (first_time >= period_) ? period_ : 0.0F;
    size_t edge_count = waveform_.size();
    for (size_t i = 0; i < edge_count - 1; i++)
      waveform_[i] = waveform_[i + 1] - offset;
    waveform_[edge_count - 1] = first_time - offset + period_;
  }
  setClkEdgeTimes();
  waveform_valid_ = true;
}

void
Clock::generateScaledClk(const Clock *src_clk,
                         float scale)
{
  period_ = src_clk->period() * scale;
  if (duty_cycle_ != 0.0) {
    float rise = src_clk->waveform()[0] * scale;
    waveform_.push_back(rise);
    waveform_.push_back(rise + period_ * duty_cycle_ / 100.0F);
  }
  else {
    for (float time : src_clk->waveform())
      waveform_.push_back(time * scale);
  }
}

void
Clock::generateEdgesClk(const Clock *src_clk)
{
  // The create_generated_clock tcl cmd and Sta::makeClock
  // enforce this restriction.
  if (edges_.size() == 3) {
    const FloatSeq &src_wave = src_clk->waveform();
    size_t src_size = src_wave.size();
    int src_size_int = static_cast<int>(src_size);
    float src_period = src_clk->period();

    int edge0_1 = edges_[0] - 1;
    div_t edge0_div = std::div(edge0_1, src_size_int);
    float rise = src_wave[edge0_div.rem]
      + static_cast<float>(edge0_div.quot) * src_period;
    if (!edge_shifts_.empty())
      rise += edge_shifts_[0];
    waveform_.push_back(rise);

    int edge1_1 = edges_[1] - 1;
    div_t edge1_div = std::div(edge1_1, src_size_int);
    float fall = src_wave[edge1_div.rem]
      + static_cast<float>(edge1_div.quot) * src_period;
    if (!edge_shifts_.empty())
      fall += edge_shifts_[1];
    waveform_.push_back(fall);

    int edge2_1 = edges_[2] - 1;
    div_t edge2_div = std::div(edge2_1, src_size_int);
    period_ = src_wave[edge2_div.rem]
      + static_cast<float>(edge2_div.quot) * src_period - rise;
    if (!edge_shifts_.empty())
      period_ += edge_shifts_[2];
  }
  else
    criticalError(244, "generated clock edges size is not three.");
}

static bool
isPowerOfTwo(int i)
{
  return (i & (i - 1)) == 0;
}

const RiseFall *
Clock::masterClkEdgeTr(const RiseFall *rf) const
{
  int edge_index = (rf == RiseFall::rise()) ? 0 : 1;
  return (edges_[edge_index] - 1) % 2 
    ? RiseFall::fall()
    : RiseFall::rise();
}

void
Clock::srcPinVertices(VertexSet &src_vertices,
                      const Network *network,
                      Graph *graph)
{
  if (network->isHierarchical(src_pin_)) {
    // Use the clocks on a non-hierarchical pin on the same net.
    PinSet leaf_pins(network);
    findLeafDriverPins(src_pin_, network, &leaf_pins);
    for (const Pin *pin : leaf_pins) {
      Vertex *vertex, *bidirect_drvr_vertex;
      graph->pinVertices(pin, vertex, bidirect_drvr_vertex);
      if (vertex)
        src_vertices.insert(vertex);
      if (bidirect_drvr_vertex)
        src_vertices.insert(bidirect_drvr_vertex);
    }
  }
  else {
    Vertex *vertex = graph->pinDrvrVertex(src_pin_);
    src_vertices.insert(vertex);
  }
}

bool
Clock::isDivideByOneCombinational() const
{
  return combinational_
    && divide_by_ == 1
    && multiply_by_ == 0
    && edge_shifts_.empty();
}

////////////////////////////////////////////////////////////////

ClockEdge::ClockEdge(Clock *clock,
                     const RiseFall *rf) :
  clock_(clock),
  rf_(rf),
  name_(sta::format("{} {}", clock_->name(), rf_->shortName())),
  index_(clock_->index() * RiseFall::index_count + rf_->index())
{
}

void
ClockEdge::setTime(float time)
{
  time_ = time;
}

ClockEdge *
ClockEdge::opposite() const
{
  return clock_->edge(rf_->opposite());
}

float
ClockEdge::pulseWidth() const
{
  ClockEdge *opp_clk_edge = opposite();
  float width = opp_clk_edge->time() - time_;
  if (width < 0.0)
    width += clock_->period();
  return width;
}

////////////////////////////////////////////////////////////////

int
clkCmp(const Clock *clk1,
       const Clock *clk2)
{
  if (clk1 == nullptr && clk2)
    return -1;
  else if (clk1 == nullptr && clk2 == nullptr)
    return 0;
  else if (clk1 && clk2 == nullptr)
    return 1;
  else {
    int index1 = clk1->index();
    int index2 = clk2->index();
    if (index1 < index2)
      return -1;
    else if (index1 == index2)
      return 0;
    else
      return 1;
  }
}

int
clkEdgeCmp(const ClockEdge *clk_edge1,
           const ClockEdge *clk_edge2)
{
  if (clk_edge1 == nullptr && clk_edge2)
    return -1;
  else if (clk_edge1 == nullptr && clk_edge2 == nullptr)
    return 0;
  else if (clk_edge1 && clk_edge2 == nullptr)
    return 1;
  else {
    int index1 = clk_edge1->index();
    int index2 = clk_edge2->index();
    if (index1 == index2)
      return 0;
    else if (index1 < index2)
      return -1;
    else
      return 1;
  }
}

bool
clkEdgeLess(const ClockEdge *clk_edge1,
            const ClockEdge *clk_edge2)
{
  return clkEdgeCmp(clk_edge1, clk_edge2) < 0;
}

////////////////////////////////////////////////////////////////

InterClockUncertainty::InterClockUncertainty(const Clock *src,
                                             const Clock *target) :
  src_(src),
  target_(target)
{
}

bool
InterClockUncertainty::empty() const
{
  return uncertainties_[RiseFall::riseIndex()].empty()
    && uncertainties_[RiseFall::fallIndex()].empty();
}

void
InterClockUncertainty::uncertainty(const RiseFall *src_rf,
                                   const RiseFall *tgt_rf,
                                   const SetupHold *setup_hold,
                                   float &uncertainty,
                                   bool &exists) const
{
  uncertainties_[src_rf->index()].value(tgt_rf, setup_hold,
                                        uncertainty, exists);
}

void
InterClockUncertainty::setUncertainty(const RiseFallBoth *src_rf,
                                      const RiseFallBoth *tgt_rf,
                                      const SetupHoldAll *setup_hold,
                                      float uncertainty)
{
  for (auto src_rf_index : src_rf->rangeIndex())
    uncertainties_[src_rf_index].setValue(tgt_rf, setup_hold, uncertainty);
}

void
InterClockUncertainty::removeUncertainty(const RiseFallBoth *src_rf,
                                         const RiseFallBoth *tgt_rf,
                                         const SetupHoldAll *setup_hold)
{
  for (auto src_rf_index : src_rf->rangeIndex())
    uncertainties_[src_rf_index].removeValue(tgt_rf, setup_hold);
}

const RiseFallMinMax *
InterClockUncertainty::uncertainties(const RiseFall *src_rf) const
{
  return &uncertainties_[src_rf->index()];
}

bool
InterClockUncertaintyLess::operator()(const InterClockUncertainty *inter1,
                                      const InterClockUncertainty *inter2)const
{
  return inter1->src()->index() < inter2->src()->index()
    || (inter1->src() == inter2->src()
        && inter1->target()->index() < inter2->target()->index());
}

////////////////////////////////////////////////////////////////

bool
ClockIndexLess::operator()(const Clock *clk1,
                           const Clock *clk2) const
{
  return (clk1 == nullptr && clk2)
    || (clk1 && clk2
        && clk1->index() < clk2->index());
}

ClockSeq
sortByName(ClockSet *set)
{
  ClockSeq clks;
  for (Clock *clk : *set)
    clks.push_back(clk);
  sort(clks, ClockNameLess());
  return clks;
}

////////////////////////////////////////////////////////////////

bool
ClockSetLess::operator()(const ClockSet *set1,
                         const ClockSet *set2) const
{
  return sta::compare(set1, set2) < 0;
}

int
compare(const ClockSet *set1,
        const ClockSet *set2)
{
  return sta::compare(set1, set2, ClockIndexLess());
}

} // namespace sta
