
require 'tickly'

class Tracksperanto::Import::NukeScript < Tracksperanto::Import::Base
  
  def self.human_name
    "Nuke .nk script file with Tracker, Reconcile3D, Transform2D, PlanarTracker and CornerPin nodes"
  end
  
  def self.distinct_file_ext
    ".nk"
  end
  
  def self.known_snags
    "The only supported nodes that we can extract tracks from are Transform2D, Reconcile3D, " +
    "PlanarTracker and Tracker (we support Nuke 5 up to and including 8)"
  end
  
  def each
    parser = Tickly::NodeProcessor.new
    
    parser.add_node_handler_class(Tracker3)
    parser.add_node_handler_class(Reconcile3D)
    parser.add_node_handler_class(PlanarTracker1_0)
    parser.add_node_handler_class(PlanarTracker)
    parser.add_node_handler_class(Tracker4)
    parser.add_node_handler_class(CornerPin2D)
    parser.add_node_handler_class(Transform)
    
    parser.parse(Bychar.wrap(@io)) do | node |
      node.trackers.each do | t |
        report_progress("Scavenging tracker #{t.name}")
        yield t
      end
    end
  end
  
  private
  
  class Tracker3
    include Tracksperanto::ZipTuples
    attr_reader :trackers

    def initialize(options)
      @trackers = []
      point_channels.each do | point_name |
        next unless options[point_name]
        point_channel = options[point_name]
        
        curves = extract_curves_from_channel(point_channel)
        
        # We must always have 2 anim curves
        next unless curves.length == 2
        
        full_name = [options["name"], point_name].join('_')
        tracker = package_tracker(full_name, curves[0], curves[1])
        
        @trackers << tracker
      end
    end
    
    def package_tracker(full_name, xcurve, ycurve)
      frame_x_and_y = zip_curve_tuples(xcurve, ycurve)
      Tracksperanto::Tracker.new(:name => full_name) do | t |
        frame_x_and_y.each do | (f, x, y) |
            t.keyframe!(:frame => (f -1), :abs_x => x, :abs_y => y) 
        end
      end
    end
    
    def extract_curves_from_channel(point_channel)
      # First element is the :c curly identifier
      point_channel[1..-1].map do | curve_argument | 
        if curve_argument[1] == "curve"
          Tickly::Curve.new(curve_argument)
        else
          nil
        end
      end.compact
    end
    
    def point_channels
      %w( track1 track2 track3 track4 )
    end
    
  end
  
  class Reconcile3D < Tracker3
    def point_channels
      %w( output)
    end
  end
  
  class Transform < Tracker3
    def point_channels
      %w( translate center )
    end
  end
  
  # Planar tracker in Nuke 6
  class PlanarTracker1_0 < Tracker3
    def point_channels
      %w( outputBottomLeft outputBottomRight outputTopLeft outputTopRight)
    end
  end
  
  # Planar tracker in Nuke 7
  class PlanarTracker < PlanarTracker1_0
    def point_channels
      %w( outputBottomLeft outputBottomRight outputTopLeft outputTopRight)
    end
  end
  
  class CornerPin2D < Tracker3
    def point_channels
      %w( to1 to2 to3 to4 )
    end
  end
  
  class Tracker4 < Tracker3
    
    def initialize(options)
      
      @name = options["name"]
      @trackers = []
      tracks = options["tracks"]
      
      return unless tracks # For linked Tracker nodes that have no tracks
      
      preamble = tracks[1]
      headers = tracks[2]
      values = tracks[3]
      
      # When this was written, this was the order of the columns in the table:
      # le("e", "name", "track_x", "track_y", "offset_x", "offset_y", "T", "R", "S", "error", 
      #  "error_min", "error_max", "pattern_x", "pattern_y", "pattern_r", "pattern_t", "search_x",
      # "search_y", "search_r", "search_t", "key_track", "key_search_x", "key_search_y", "key_search_r",
      # "key_search_t", "key_track_x", "key_track_y", "key_track_r", "key_track_t", "key_centre_offset_x", "key_centre_offset_y")
      tracker_rows = values[0]
      
      # The 0 element is the :c symbol
      tracker_rows[1..-1].each do | row |
        row_content = row[0]
        
        # For offsets see above
        point_name = row_content[2]
        begin
          x_curve = Tickly::Curve.new(row_content[3])
          y_curve = Tickly::Curve.new(row_content[4])
          full_name = [options["name"], point_name].join('_')
          tracker = package_tracker(full_name, x_curve, y_curve)
          @trackers << tracker
        rescue Tickly::Curve::InvalidCurveError
          # $stderr.puts "Failed to recover a tracker, it probably contained expressions"
        end
      end
    end
  end
end
