# encoding: UTF-8
begin
  puts "###DZW-LEVEL-DJ1250-20260920"
  m = Sketchup.active_model
  bk = 'E:/도진팩토리/3D스캔및티칭시스템/_자동화/autosave/BACKUP_before_level_dj1250.skp'
  m.save_copy(bk)
  puts "BACKUP=#{bk}"
  ents = lambda { |s| s.is_a?(Sketchup::Group) ? s.entities : s.definition.entities }
  cov = nil
  m.entities.each do |e|
    next unless e.is_a?(Sketchup::Group) || e.is_a?(Sketchup::ComponentInstance)
    cov = e if (e.respond_to?(:name) ? e.name.to_s : '') == '하부프레임커버'
  end
  tgt = nil
  if cov
    ents.call(cov).each do |s|
      next unless s.is_a?(Sketchup::Group) || s.is_a?(Sketchup::ComponentInstance)
      tgt = s if (s.respond_to?(:name) ? s.name.to_s : '') == 'DJ1250-윈치받침'
    end
  end
  if tgt.nil?
    puts "STOP: DJ1250-윈치받침 미발견"
  else
    wt = cov.transformation * tgt.transformation
    # 최상단 수평계열 최대면 = 윈치 결합면
    best = nil
    ff = nil
    ff = lambda do |en, t|
      en.grep(Sketchup::Face).each do |fa|
        vp = fa.outer_loop.vertices.map { |v| v.position.transform(t) }
        next if vp.length < 3
        n = (vp[1] - vp[0]).cross(vp[2] - vp[0])
        next if n.length.to_f < 1.0e-12
        n.normalize!
        next if n.z.abs < 0.7
        bb = Geom::BoundingBox.new
        vp.each { |p| bb.add(p) }
        ex = [bb.width.to_mm, bb.height.to_mm, bb.depth.to_mm].sort
        a = ex[1] * ex[2]
        best = [a, n, bb] if best.nil? || a > best[0] || (a > best[0] * 0.95 && bb.max.z.to_mm > best[2].max.z.to_mm)
      end
      (en.grep(Sketchup::Group) + en.grep(Sketchup::ComponentInstance)).each { |q| ff.call(ents.call(q), t * q.transformation) }
    end
    ff.call(ents.call(tgt), wt)
    if best.nil?
      puts "STOP: 수평계열 기준면 미검출"
    else
      a0, n0, bb0 = best
      nz = n0.z < 0 ? -1.0 : 1.0
      nx = n0.x * nz
      ny = n0.y * nz
      tilt0 = Math.acos([[n0.z.abs, 1.0].min, -1.0].max) * 180.0 / Math::PI
      puts "기준면(윈치 결합면): 면적~#{a0.round(0)}mm2 법선=[#{n0.x.round(5)},#{n0.y.round(5)},#{n0.z.round(5)}] 기울기=#{tilt0.round(4)}deg Z=[#{bb0.min.z.to_mm.round(1)},#{bb0.max.z.to_mm.round(1)}]"
      if ny.abs > 0.005
        puts "STOP: Y성분(#{ny.round(5)})이 있어 단일 Y축 회전으로 수평 불가 -> 변경 없음"
      else
        ang = Math.atan2(nx, n0.z.abs) * 180.0 / Math::PI
        pvx = (bb0.min.x.to_mm + bb0.max.x.to_mm) / 2.0
        pvz = (bb0.min.z.to_mm + bb0.max.z.to_mm) / 2.0
        halfx = (bb0.max.x.to_mm - bb0.min.x.to_mm) / 2.0
        mvmax = halfx * Math.tan(ang.abs * Math::PI / 180.0)
        puts "검산: Y축 회전 #{ang.round(4)}deg, 피벗=윈치면 중심(X#{pvx.round(1)}, Z#{pvz.round(1)}), 양끝 최대 상하 이동 ±#{mvmax.round(1)}mm"
        if ang.abs > 10.0 || mvmax > 40.0
          puts "STOP: 회전/이동량 과다 -> 변경 없음"
        else
          piv = Geom::Point3d.new(pvx.mm, 0, pvz.mm)
          rw = Geom::Transformation.rotation(piv, Geom::Vector3d.new(0, 1, 0), ang * Math::PI / 180.0)
          before = []
          cp = nil
          cp = lambda do |en, t|
            en.grep(Sketchup::Edge).each { |ed| ed.vertices.each { |v| before << v.position.transform(t) } }
            (en.grep(Sketchup::Group) + en.grep(Sketchup::ComponentInstance)).each { |q| cp.call(ents.call(q), t * q.transformation) }
          end
          cp.call(ents.call(tgt), wt)
          bx = before.map { |p| p.x.to_mm }.minmax
          bz = before.map { |p| p.z.to_mm }.minmax
          m.start_operation('DJ1250-윈치받침 수평 정렬', true)
          tgt.transform!(cov.transformation.inverse * rw * cov.transformation)
          m.commit_operation
          wt2 = cov.transformation * tgt.transformation
          best2 = nil
          ff2 = nil
          ff2 = lambda do |en, t|
            en.grep(Sketchup::Face).each do |fa|
              vp = fa.outer_loop.vertices.map { |v| v.position.transform(t) }
              next if vp.length < 3
              n = (vp[1] - vp[0]).cross(vp[2] - vp[0])
              next if n.length.to_f < 1.0e-12
              n.normalize!
              next if n.z.abs < 0.7
              bb = Geom::BoundingBox.new
              vp.each { |p| bb.add(p) }
              ex = [bb.width.to_mm, bb.height.to_mm, bb.depth.to_mm].sort
              a = ex[1] * ex[2]
              best2 = [a, n, bb] if best2.nil? || a > best2[0]
            end
            (en.grep(Sketchup::Group) + en.grep(Sketchup::ComponentInstance)).each { |q| ff2.call(ents.call(q), t * q.transformation) }
          end
          ff2.call(ents.call(tgt), wt2)
          after = []
          cp2 = nil
          cp2 = lambda do |en, t|
            en.grep(Sketchup::Edge).each { |ed| ed.vertices.each { |v| after << v.position.transform(t) } }
            (en.grep(Sketchup::Group) + en.grep(Sketchup::ComponentInstance)).each { |q| cp2.call(ents.call(q), t * q.transformation) }
          end
          cp2.call(ents.call(tgt), wt2)
          ax = after.map { |p| p.x.to_mm }.minmax
          az = after.map { |p| p.z.to_mm }.minmax
          t2 = best2 ? Math.acos([[best2[1].z.abs, 1.0].min, -1.0].max) * 180.0 / Math::PI : nil
          puts "== 결과 수평기울기 #{tilt0.round(4)} -> #{t2 ? t2.round(4) : 'n/a'}deg (목표 0.000)"
          puts "   월드 X #{bx.map { |v| v.round(1) }.inspect} -> #{ax.map { |v| v.round(1) }.inspect} / Z #{bz.map { |v| v.round(1) }.inspect} -> #{az.map { |v| v.round(1) }.inspect}"
          puts "   scale=[#{tgt.transformation.xscale.round(5)},#{tgt.transformation.yscale.round(5)},#{tgt.transformation.zscale.round(5)}] (이번 단계에서 스케일 정규화는 미실시)"
          vw = m.active_view
          cam0 = vw.camera
          begin
            c = cov.bounds.center
            [['iso', Geom::Point3d.new(c.x - 3500.mm, c.y - 4500.mm, c.z + 2200.mm)],
             ['side', Geom::Point3d.new(c.x, c.y - 6000.mm, c.z + 300.mm)]].each do |tag, eye|
              vw.camera = Sketchup::Camera.new(eye, c, Geom::Vector3d.new(0, 0, 1))
              vw.zoom_extents
              f = "E:/도진팩토리/3D스캔및티칭시스템/_자동화/inbox/_level_#{tag}.png"
              vw.write_image(f, 1600, 1000, true)
              puts "   캡처 #{tag}=#{f} 존재=#{File.exist?(f)}"
            end
          rescue Exception => e3
            puts "   캡처 실패 #{e3.message}"
          ensure
            vw.camera = cam0
          end
          ok = m.save
          puts "SAVE=#{ok.inspect} modified?=#{m.modified?.inspect}"
          puts "DONE"
        end
      end
    end
  end
rescue Exception => e
  puts "EXC #{e.class}: #{e.message}"
  puts e.backtrace.first(6).join(" | ")
end
nil
