# encoding: UTF-8
# del5 — 그룹#2 안의 SWD_06_b_t6_36x140 단부 거싯 삭제 (안전판)
#
# del3/del4 는 같은 지점에서 SketchUp 을 2번 죽였다.
#   크래시: 0xC0000005, SketchUp!0069b240, 널포인터 읽기(주소 0x40), 두 번 모두 동일 주소.
#   차이점: del1/del2(생존)는 "최상위" 그룹을 지웠고,
#           del3/del4(사망)는 "그룹#2 하위" 그룹을 지운 뒤
#           곧바로 parent.entities 를 다시 조회했다.
#   -> 자식 erase! 직후 부모 그룹의 entities 컬렉션을 만지는 것이 사망 원인.
#
# 이 안전판이 바꾼 것 (3가지만):
#   1) erase! 직후 parent.entities 재조회 금지. 커밋 뒤에 parent 를 새로 찾아서 검증한다.
#   2) 삭제 전 save_copy 로 현재 메모리 상태를 디스크에 먼저 남긴다(진짜 백업).
#   3) 하트비트를 디스크 파일에 즉시 flush. 또 죽어도 어디서 죽었는지 남는다.
#
# 실행 방법: 이 파일을 inbox 로 옮기면 로더가 1초 안에 실행한다.

require 'fileutils'

HB = 'E:/도진팩토리/3D스캔및티칭시스템/_자동화/inbox/_보류/_del5_heartbeat.txt'
def hb(msg)
  begin
    File.open(HB, 'a:UTF-8') { |f| f.write("[#{Time.now.strftime('%H:%M:%S')}] #{msg}\n"); f.flush }
  rescue Exception
    nil
  end
end
def r1(v); (v * 25.4).round(1); end

m = Sketchup.active_model
hb("START path=#{m.path} modified=#{m.modified?}")
out = []

parent = m.entities.grep(Sketchup::Group).find { |g| g.definition.name == "그룹#2" }
if parent.nil?
  out << "그룹#2 없음 -> 중단, 삭제 0건"
  hb("ABORT 그룹#2 없음")
else
  tgt = parent.entities.grep(Sketchup::Group).select { |s| s.name == "SWD_06_b_t6_36x140" }
  out << "그룹#2 하위 그룹 #{parent.entities.grep(Sketchup::Group).length}개 / 삭제대상 #{tgt.length}개"
  tgt.each_with_index do |s, i|
    b = s.bounds
    out << "  (#{i + 1}) #{s.name} bbox (#{r1(b.min.x)},#{r1(b.min.y)},#{r1(b.min.z)})..(#{r1(b.max.x)},#{r1(b.max.y)},#{r1(b.max.z)}) faces=#{s.entities.grep(Sketchup::Face).length}"
  end
  # .volume / .transformation.to_a 는 의도적으로 호출하지 않는다 (불필요·미검증)

  if tgt.empty?
    out << "SWD_06_b_t6_36x140 없음 (9/17 원본이거나 이미 삭제됨) -> 중단, 삭제 0건"
    hb("ABORT 대상없음")
  else
    # --- 1) 삭제 전 현재 메모리 상태를 디스크에 백업 ---
    bak = nil
    begin
      base = m.path.to_s.empty? ? 'untitled' : File.basename(m.path, '.skp')
      dir  = m.path.to_s.empty? ? 'E:/도진팩토리/3D스캔및티칭시스템/_자동화/autosave' : File.dirname(m.path)
      bak  = File.join(dir, "#{base}.bak_#{Time.now.strftime('%Y%m%d_%H%M%S')}_del5직전.skp")
      m.save_copy(bak)
      out << "사전 백업 OK -> #{bak}"
      hb("BACKUP OK #{bak}")
    rescue Exception => e
      out << "사전 백업 실패 #{e.class}: #{e.message} -> 삭제 중단"
      hb("BACKUP FAIL #{e.class}: #{e.message}")
      tgt = []
    end

    unless tgt.empty?
      # --- 2) 단일 operation, erase! 만. 부모 컬렉션 재조회 절대 금지 ---
      n = 0
      begin
        m.start_operation("SWD_06_b 단부거싯 삭제", true)
        tgt.each_with_index do |s, i|
          nm = s.name
          hb("ERASE #{i + 1}/#{tgt.length} 시작 #{nm}")
          s.erase!
          n += 1
          hb("ERASE #{i + 1}/#{tgt.length} 완료 #{nm}")   # parent.entities 조회 안 함
        end
        m.commit_operation
        hb("COMMIT OK n=#{n}")
        out << "삭제 #{n}개 완료 (단일 operation -> Ctrl+Z 1회로 복원)"
      rescue Exception => ex
        m.commit_operation rescue nil   # abort_operation 금지(1차 크래시 패턴)
        out << "ERR #{ex.class}: #{ex.message} (커밋으로 마감, #{n}개 삭제됨)"
        hb("ERR #{ex.class}: #{ex.message}")
      end

      # --- 3) 커밋 이후에 parent 를 "새로" 찾아서 검증 ---
      begin
        p2 = m.entities.grep(Sketchup::Group).find { |g| g.definition.name == "그룹#2" }
        if p2
          names = p2.entities.grep(Sketchup::Group).map { |s| s.name }
          out << "== 삭제 후 그룹#2 구성 (#{names.length}개) =="
          names.each { |nm| out << "   - #{nm.inspect}" }
          out << "SWD_06_b 잔여 = #{names.count { |nm| nm == 'SWD_06_b_t6_36x140' }}개"
        else
          out << "검증: 그룹#2 를 다시 찾지 못함"
        end
        out << "최상위 SWD_06_a 보존 = #{m.entities.grep(Sketchup::Group).count { |g| g.name == 'SWD_06_a_t6_36x140' }}개 (1이어야 정상)"
        hb("VERIFY DONE")
      rescue Exception => e
        out << "검증 실패 #{e.class}: #{e.message}"
        hb("VERIFY FAIL #{e.class}: #{e.message}")
      end
    end
  end
end

puts out.join("\n")
hb("END")
"DEL5"
