# Copyright (C) 2003-2005 Kouichirou Eto, All rights reserved.

$LOAD_PATH << "../../lib" unless $LOAD_PATH.include?("../../lib")
require "qwik/tokenizer"

module Qwik
  class SmilGenerator
    def initialize
      @width, @height = 320, 240
      @time_table = []
    end

    def parse(str)
      url = nil
      @width, @height = 320, 240
      @time_table = []
      tokens = tokenize(str)
      tokens.each {|token|
	case token[0]
	when :dl
	  case token[1]
	  when "url"    then url     = token[2]
	  when "width"  then @width  = token[2]
	  when "height" then @height = token[2]
	  else
	    raise "unknown param type"
	  end
	when :table
	  time_begin = Time.at_smil(token[1])
	  time_end   = Time.at_smil(token[2])
	  title = token[3]
	  @time_table << [url, time_begin, time_end, title]
	else
	  raise "unknown type"
	end
      }
      [@width, @height, @time_table] # for test
    end

    def tokenize(str)
      tokenizer = TextTokenizer.new
      tokens = tokenizer.tokenize(str)
      tokens
    end

    def generate_smil
      time = 0.0
      par = []
      @time_table.each {|url, time_begin, time_end, title|
	duration = time_end.to_f - time_begin.to_f
	par << [:video, {:region=>"v", :begin=>time.to_s+"s",
	    :src=>url, :"clip-begin"=>time_begin.to_smil,
	    :"clip-end"=>time_end.to_smil}]
	time += duration
      }
      [:smil, {:xmlns=>"http://www.w3.org/2001/SMIL20/Language",
	  :"xmlns:rn"=>"http://features.real.com/2001/SMIL20/Extensions"},
	[:head,
	  [:layout,
	    [:"root-layout", {:width=>@width, :height=>@height}],
	    [:region, {:id=>"v", :fit=>"meet"}]]],
	[:body, [:par, *par]]
      ]
    end

    def generate_html
      table = [:table]
      table << [:tr,
	[:th, "IN"],
	[:th, "OUT"],
	[:th, "MSG"]
      ]
      @time_table.each {|url, time_begin, time_end, title|
	table << [:tr, 
	  [:td, time_begin.to_smil],
	  [:td, time_end.to_smil],
	  [:td, title]]
      }
      table
    end
  end
end

class Time
  def Time.at_smil(arg)
    raise "arg must be String" unless arg.is_a?(String)
    hour, min, sec, frame = Time.parse_smil(arg)
    return Time.gm(1970, 1, 1, hour, min, sec, frame_to_usec(frame))
  end

  def to_smil
    str = self.strftime("%H:%M:%S")
    frame = self.frame
    str += "."+sprintf("%02d", frame) if 0 < frame
    str
  end

  def frame
    Time.usec_to_frame(self.usec)
  end

  def Time.frame_to_usec(frame)
    (1000000.0 * frame.to_f / 30.0).to_i
  end

  def Time.usec_to_frame(usec)
    (30.0 * usec.to_f / 1000000.0 + 0.5).to_i	# half adjust
  end

  def Time.parse_smil(str)
    hour = min = sec = frame = 0
    if str.include?(".")
      str, frame = str.split(".", 2)
      frame = with_range(frame.to_i, 0, 29)
    end
    ar = str.split(/:/)
    sec = with_range(ar.pop.to_i, 0, 59)
    min = with_range(ar.pop.to_i, 0, 59)
    hour = with_range(ar.pop.to_i, 0, 999999) # without max
    [hour, min, sec, frame]
  end

  def Time.with_range(n, min, max)
    n = min if n < min
    n = max if max < n
    n
  end
end

if $0 == __FILE__
  require "test/unit"
  require "qwik/wabisabi-get"
  require "qwik/qp"
  $debug = true
end

if $debug
  class TC_Smil < Test::Unit::TestCase
    def test_smil_generator
      gen = Qwik::SmilGenerator.new

      str = ":url:rtsp://example.com/test/1.files/t.rm
,00,05.05,msg1
,12:10,12:15.15,msg2"

      # test_tokenize
      tokens = gen.tokenize(str)
      assert_equal([[:dl, "url", "rtsp://example.com/test/1.files/t.rm"],
		     [:table, "00", "05.05", "msg1"],
		     [:table, "12:10", "12:15.15", "msg2"]], tokens)

      # test parse
      width, height, time_table = gen.parse(str)
      assert_equal(320, width)
      assert_equal(240, height)
      assert_equal([["rtsp://example.com/test/1.files/t.rm",
		       Time.at_smil("00"), Time.at_smil("05.05"), "msg1"],
		     ["rtsp://example.com/test/1.files/t.rm",
		       Time.at_smil("12:10"), Time.at_smil("12:15.15"),"msg2"]],
		   time_table)

      # test generate_smil
      w = gen.generate_smil
      assert_equal([:head, [:layout,
		       [:"root-layout", {:height=>240, :width=>320}],
		       [:region, {:id=>"v", :fit=>"meet"}]]],
		   w.get_path("//head"))
      assert_equal([:video,
		     {:"clip-end"=>"00:00:05.05",
		       :begin=>"0.0s",
		       :region=>"v",
		       :src=>"rtsp://example.com/test/1.files/t.rm",
		       :"clip-begin"=>"00:00:00"}],
		   w.get_path("//video"))
      assert_equal([:video,
		     {:"clip-end"=>"00:12:15.15",
		       :begin=>"5.166666s",
		       :region=>"v",
		       :src=>"rtsp://example.com/test/1.files/t.rm",
		       :"clip-begin"=>"00:12:10"}],
		   w.get_path("//video[2]"))

      # test generate_html
      w = gen.generate_html
      assert_equal([:table,
		     [:tr, [:th, "IN"], [:th, "OUT"], [:th, "MSG"]],
		     [:tr, [:td, "00:00:00"], [:td, "00:00:05.05"],
		       [:td, "msg1"]],
		     [:tr, [:td, "00:12:10"], [:td, "00:12:15.15"],
		       [:td, "msg2"]]],
		   w)
    end

    def test_smil_time
      # test with range
      assert_equal(0,  Time.with_range(-1, 0, 29))
      assert_equal(29, Time.with_range(30, 0, 29))

      # test frame, usec
      assert_equal(500000, Time.frame_to_usec(15))
      assert_equal(15, Time.usec_to_frame(500000))
      (0..30).each {|n|
	usec = Time.frame_to_usec(n)
	frame = Time.usec_to_frame(usec)
	assert_equal(n, frame)
      }

      # test parse_smil
      assert_equal([ 0,  0, 12,  0], Time.parse_smil("12"))
      assert_equal([ 0, 12, 34,  0], Time.parse_smil("12:34"))
      assert_equal([12, 34, 56,  0], Time.parse_smil("12:34:56"))
      assert_equal([12, 34, 56, 15], Time.parse_smil("12:34:56.15"))
      assert_equal([ 0, 12, 34, 15], Time.parse_smil("12:34.15"))

      # test at_smil
      t = Time.at_smil("12")
      assert_equal(12.0, t.to_f)
      assert_equal(Time.at(12), t) # same
      t = Time.at_smil("12.15")
      assert_equal(12.5, t.to_f)
      assert_equal(Time.at(12.5), t) # same
      # should test with range...
      assert_equal(12.333333, Time.at_smil("12.10").to_f)
      assert_equal(45296.5, Time.at_smil("12:34:56.15").to_f)

      # test to_smil
      assert_equal("12:34:56.15", Time.at_smil("12:34:56.15").to_smil)
      assert_equal("12:34:56", Time.at_smil("12:34:56").to_smil)
      assert_equal("00:12:34", Time.at_smil("12:34").to_smil)
      assert_equal("00:00:12.15", Time.at_smil("12.15").to_smil)
      assert_equal("00:00:12.05", Time.at_smil("12.05").to_smil)
    end
  end
end
