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

namespace eval sta {

define_cmd_args "write_path_spice" { -path_args path_args\
                                       -spice_file spice_file\
                                       -lib_subckt_file lib_subckts_file\
                                       -model_file model_file\
                                       -power power\
                                       -ground ground\
                                       [-simulator hspice|ngspice|xyce]}

proc write_path_spice { args } {
  parse_key_args "write_path_spice" args \
    keys {-spice_file -lib_subckt_file -model_file \
            -power -ground -path_args -simulator} \
    flags {}

  if { [info exists keys(-spice_file)] } {
    set spice_file [file nativename $keys(-spice_file)]
    set spice_dir [file dirname $spice_file]
    if { ![file exists $spice_dir] } {
      sta_error 1920 "Directory $spice_dir not found."
    }
    if { ![file isdirectory $spice_dir] } {
      sta_error 1921 "$spice_dir is not a directory."
    }
    if { ![file writable $spice_dir] } {
      sta_error 1922 "Cannot write in $spice_dir."
    }
  } else {
    sta_error 1923 "No -spice_file specified."
  }

  if { [info exists keys(-lib_subckt_file)] } {
    set lib_subckt_file [file nativename $keys(-lib_subckt_file)]
    if { ![file readable $lib_subckt_file] } {
      sta_error 1924 "-lib_subckt_file $lib_subckt_file is not readable."
    }
  } else {
    sta_error 1925 "No -lib_subckt_file specified."
  }

  if { [info exists keys(-model_file)] } {
    set model_file [file nativename $keys(-model_file)]
    if { ![file readable $model_file] } {
      sta_error 1926 "-model_file $model_file is not readable."
    }
  } else {
    sta_error 1927 "No -model_file specified."
  }

  if { [info exists keys(-power)] } {
    set power $keys(-power)
  } else {
    sta_error 1928 "No -power specified."
  }

  if { [info exists keys(-ground)] } {
    set ground $keys(-ground)
  } else {
    sta_error 1929 "No -ground specified."
  }

  set ckt_sim [parse_ckt_sim_key keys]

  if { ![info exists keys(-path_args)] } {
    sta_error 1930 "No -path_args specified."
  }
  set path_args $keys(-path_args)
  set path_ends [eval [concat find_timing_paths $path_args]]
  if { $path_ends == {} } {
    sta_error 1931 "No paths found for -path_args $path_args."
  } else {
    set path_index 1
    foreach path_end $path_ends {
      set path [$path_end path]
      set path_file "${spice_file}_$path_index"
      set spice_file1 ${path_file}.sp
      set subckt_file ${path_file}.subckt
      write_path_spice_cmd $path $spice_file1 $subckt_file \
        $lib_subckt_file $model_file $power $ground $ckt_sim
      incr path_index
    }
  }
}

set ::ckt_sims {hspice ngspice xyce}

proc parse_ckt_sim_key { keys_var } {
  upvar 1 $keys_var keys
  global ckt_sims

  set ckt_sim "ngspice"
  if { [info exists keys(-simulator)] } {
    set ckt_sim [file nativename $keys(-simulator)]
    if { [lsearch $ckt_sims $ckt_sim] == -1 } {
      sta_error 1910 "Unknown circuit simulator"
    }
  }
  return $ckt_sim
}

################################################################

define_cmd_args "write_gate_spice" \
  { -gates {{instance input_port driver_port edge [delay]}...}\
      -spice_filename spice_filename\
      -lib_subckt_file lib_subckts_file\
      -model_file model_file\
      -power power\
      -ground ground\
      [-simulator hspice|ngspice|xyce]\
      [-scene scene]\
      [-min] [-max]}

proc write_gate_spice { args } {
  parse_key_args "write_gate_spice" args \
    keys {-gates -spice_filename -lib_subckt_file -model_file \
            -power -ground -simulator -scene -corner}\
    flags {-measure_stmts -min -max}

  if { [info exists keys(-gates)] } {
    set gates $keys(-gates)
  } else {
    sta_error 1932 "Missing -gates argument."
  }
  if { [info exists keys(-spice_filename)] } {
    set spice_file [file nativename $keys(-spice_filename)]
    set spice_dir [file dirname $spice_file]
    if { ![file writable $spice_dir] } {
      sta_error 1903 "Cannot write $spice_dir."
    }
  } else {
    sta_error 1904 "No -spice_filename specified."
  }

  if { [info exists keys(-lib_subckt_file)] } {
    set lib_subckt_file [file nativename $keys(-lib_subckt_file)]
    if { ![file readable $lib_subckt_file] } {
      sta_error 1905 "-lib_subckt_file $lib_subckt_file is not readable."
    }
  } else {
    sta_error 1906 "No -lib_subckt_file specified."
  }

  if { [info exists keys(-model_file)] } {
    set model_file [file nativename $keys(-model_file)]
    if { ![file readable $model_file] } {
      sta_error 1907 "-model_file $model_file is not readable."
    }
  } else {
    sta_error 1908 "No -model_file specified."
  }

  if { [info exists keys(-power)] } {
    set power $keys(-power)
  } else {
    sta_error 1909 "No -power specified."
  }

  if { [info exists keys(-ground)] } {
    set ground $keys(-ground)
  } else {
    sta_error 1915 "No -ground specified."
  }

  set ckt_sim [parse_ckt_sim_key keys]

  set scene [parse_scene keys]
  set min_max [parse_min_max_flags flags]
  check_argc_eq0 "write_gate_spice" $args

  set spice_dir [file dirname $spice_file]
  set spice_root [file rootname [file tail $spice_file]]
  set subckt_file [file join $spice_dir "$spice_root.subckt"]
  write_gate_spice_cmd $gates $spice_file $subckt_file \
    $lib_subckt_file $model_file $power $ground $ckt_sim \
    $scene $min_max
}

################################################################

# plot_pins defaults to input_pin, driver_pina and load pins for each driver.
define_cmd_args "write_gate_gnuplot" \
  { -gates {{instance input_port driver_port edge [delay]}...}\
      -plot_pins plot_pins\
      -plot_basename plot_basename\
      [-scene scene] [-min] [-max]}

proc write_gate_gnuplot { args } {
  parse_key_args "write_gate_gnuplot" args \
    keys {-gates -plot_pins -plot_basename -spice_waveforms -corner -scene} \
    flags {-min -max}

  if { [info exists keys(-gates)] } {
    set gates $keys(-gates)
  } else {
    sta_error 1933 "Missing -gates argument."
  }
  if { [info exists keys(-plot_pins)] } {
    set plot_pins [get_port_pins_error "-plot_pins" $keys(-plot_pins)]
  } else {
    set plot_pins {}
    set plot_all_loads 0
    set gate_idx 0
    foreach gate $gates {
      set in_pin [parse_gate_in_pin $gate]
      set drvr_pin [parse_gate_drvr_pin $gate]
      lappend plot_pins $in_pin
      lappend plot_pins $drvr_pin
      # Only plot driver loads.
      if { $plot_all_loads || $gate_idx == 0 } {
        set pin_iter [$drvr_pin connected_pin_iterator]
        while { [$pin_iter has_next] } {
          set pin [$pin_iter next]
          if { [$pin is_load] } {
            lappend plot_pins $pin
          }
        }
        $pin_iter finish
      }
      incr gate_idx
    }
  }

  if { [info exists keys(-plot_basename)] } {
    set plot_base [file nativename $keys(-plot_basename)]
    set plot_dir [file dirname $plot_base]
    if { ![file writable $plot_dir] } {
      sta_error 1913 "Cannot write $plot_dir."
    }
  } else {
    sta_error 1914 "No -plot_basename specified."
  }
  set gnuplot_filename "${plot_base}.gnuplot"
  set csv_filename "${plot_base}.csv"

  set sim_wave_filename ""
  if { [info exists keys(-spice_waveforms)] } {
    set sim_wave_filename $keys(-spice_waveforms)
  }

  set scene [parse_scene keys]
  set min_max [parse_min_max_flags flags]

  write_gate_gnuplot_cmd $gates $plot_pins $sim_wave_filename \
    $gnuplot_filename $csv_filename $scene $min_max
}

proc parse_gate_drvr_pin { gate_arg } {
  lassign $gate_arg inst_name in_port_name in_rf drvr_port_name drvr_rf
  set inst [get_instance_error "instance" $inst_name]
  set drvr_pin [$inst find_pin $drvr_port_name]
  return $drvr_pin
}

proc parse_gate_in_pin { gate_arg } {
  lassign $gate_arg inst_name in_port_name in_rf drvr_port_name drvr_rf
  set inst [get_instance_error "instance" $inst_name]
  set in_pin [$inst find_pin $in_port_name]
  return $in_pin
}

# sta namespace end.
}
