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

module Qwik
  class CSVTokenizer
    def self.csv_split(source)
      status = :IN_FIELD
      csv = []
      last = ""
      csv << last

      while !source.empty?
	case status

	when :IN_FIELD
	  case source
	  when /\A'/
	    source = $'
	    last << "'"
	    status = :IN_QFIELD
	  when /\A,/
	    source = $'
	    last = ""
	    csv << last
#	  when /\A(\\|\s)/
	  when /\A(\\)/
	    source = $'
#	  when /\A([^,'\\\s]*)/ # anything else
	  when /\A([^,'\\]*)/ # anything else
	    source = $'
	    last << $1
	  end

	when :IN_QFIELD
	  case source
	  when /\A'/
	    source = $'
	    last << "'"
	    status = :IN_FIELD
	  when /\A(\\)/
	    source = $'
	    last << $1
	    status = :IN_ESCAPE
	  when /\A([^'\\]*)/ # anything else
	    source = $'
	    last << $1
	  end

	when :IN_ESCAPE
	  if /\A(.)/ =~ source
	    source = $'
	    last << $1
	  end
	  status = :IN_QFIELD

	end
      end

      csv = csv.map {|a|
	a.strip
      }

      csv
    end
  end
end

if $0 == __FILE__
  require "test/unit"
  $debug = true
end

if $debug
  class TC_CSV < Test::Unit::TestCase
    def assert(e, s)
      assert_equal(e, Qwik::CSVTokenizer.csv_split(s))
    end

    def test_csv
      # test basic
      assert(["1"], "1")
      assert(["1", ""], "1,")
      assert(["1", "2"], "1,2")
      assert(["1", "", "3"], "1,,3")

      # test from plugin
      assert(["a", "b"], "a,b")
      assert(["'a'", "b"], "'a',b")
      assert(["a", "b"], "a, b")
      assert(["a", "", "b"], "a,,b")

      # test space
      assert(["1"], " 1")
      assert(["1"], "1 ")
      assert(["1", "2"], "1, 2")
      assert(["1", "2"], "1,\n2")
      assert(["1", "2"], "1,\t2")
      assert(["1", "2"], "1,\\2")

      # test escape
      assert(["\"1\"\""], '"1""')

      # test IN_QFIELD
      assert(["'1'"], "'1'")
      assert(["'1 '"], "'1 '")
      assert(["'1,2'"], "'1,2'")
      assert(["\"1\""], '"1"')
      assert(["\"1\"", "\"2\""], '"1","2"')
      assert(["\"1", "2\""], '"1,2"')

      # test with space
      assert(["'a b'"], "'a b'")
      assert(["a b"], "a b")
    end
  end
end
