#!/usr/bin/ruby

require 'pathname'
require 'open3'

# find-like list of files on ARGF
# put in a temporary directory (parent is ., overridable by TMPDIR)
# burn, normalize and purge options can be set in the environment
# variables MKCD_(BURN|NORMALIZE|PURGE) respectively

class MKCD
  # megabytes
  CDR_SIZE = 838860800
  TMPDIR = "."

  attr_accessor :options
  def initialize tracks, parent = TMPDIR
    @tracks = tracks.map {|t| Pathname.new t}
    @tmpdir = Pathname.new backtick("mktemp", "-p", parent, "-d").chomp

    @options = {}
    @options[:burn] = env_arg :burn, false
    @options[:normalize] = env_arg :normalize, true
    @options[:purge] = env_arg :purge, false
  end

  def make
    [:burn,:normalize,:purge].each {|x| puts "#{x}: #{options[x]}"}
    i = 0
    while i < @tracks.size
      decode @tracks[i]
      i += 1
    end
    normalize if @options[:normalize]
    burn
    `rm -rf #{tmpdir}` if @options[:purge] and options[:burn]
    # only purge if burn took place!
  end

  def env_arg arg, d = false
    case ENV["MKCD_#{arg.to_s.upcase}"]
      when "true","on","yes"
        true
      when "false","off","no"
        false
      else
        d
    end
  end

  def backtick *args
    out = nil
    Open3.popen3(*args) do |i,o,e|
      i.close
      e.read
      out = o.read
    end
    out
  end

  def filetype f
    backtick "file", "-bi", f.to_s
  end

  def decode src
    @songindex ||= 0
    dst = @tmpdir + "%0.2d" % @songindex
    @songindex += 1
    srcs, dsts, dstw = src.to_s, dst.to_s, dst.to_s + ".wav"

    puts "decoding: #{src.basename} > #{dst.basename}"

    ft = filetype(src.to_s)
    case ft
      when %r{text/plain}
        @tracks += src.readlines.map {|t| Pathname.new t.chomp}
        @songindex -= 1
      when %r{audio/x-wav}, %r{cdr: data}, %r{Sun/NeXT audio}
        backtick "cp", srcs, dsts
      when %r{audio/flac}
        backtick "flac", "-d", "-o", dstw, srcs
      when %r{audio/mpeg}
        backtick "mpg123", "--wav", dstw, srcs
      when %r{application/ogg}
        backtick "oggdec", "-o", dstw, srcs
      else
        raise "Unknown format: #{src} is #{ft}"
    end

    chksize
  end

  def chksize
    du = backtick("du", "-sb", @tmpdir).split[0]
    raise "CDR capacity exceeded!" if du.to_i > CDR_SIZE
  end

  def normalize
    system "normalize-audio -m #{@tmpdir}/*"
  end

  def burn
    args = "cdrecord -pad -eject -dao -dummy -v -audio".split(" ")
    args << @tmpdir + "*"
    puts "COMMAND TO BURN"
    puts "#{args.join " "}"

    if @options[:burn]
      puts "burning now"
      system *args 
    end
  end
end

mk = MKCD.new ARGV

mk.make
