require "forwardable"
class GraphViz

  class DOTScriptData
    attr_accessor :type
    def initialize(type = nil)
      @data = []
      @separator = ""
      @type = type
    end

    def append(data)
      @data << data
    end
    alias :<< :append

    def add_attribute(name, value)
      @data << @separator << name << " = " << value
      @separator = determine_separator
    end

    def to_str
      case @type
        when "graph_attr" then "#{@data.join}#{@separator}"
        when "node_attr" then "node[#{@data.join(' ')}];"
        when "edge_attr" then "edge[#{@data.join(' ')}];"
        else raise ArgumentError, "Wrong type: #{@type}."
      end
    end
    alias :to_s :to_str

    def empty?
      @data.empty?
    end

    private

    def determine_separator
      case @type
        when "graph_attr"             then ";\n"
        when "node_attr", "edge_attr" then ","
        else raise ArgumentError, "Wrong type: #{@type}."
      end
    end

  end

  class DOTScript
    extend Forwardable

    def_delegators :@script, :end_with?

    def initialize
      @script = String.new
    end

    def append(line)
      @script << assure_ends_with(line.to_s,"\n")

      self
    end
    alias :<< :append

    def prepend(line)
      @script = assure_ends_with(line.to_s,"\n") + @script

      self
    end

    def make_subgraph(name)
      prepend(assure_ends_with("subgraph #{name}"," {"))
    end

    def add_type(type, data)
      return self if data.empty?

      case type
      when "graph_attr"
        append_statement("  " + data)
      when "node_attr"
        append_statement("  node [" + data + "]")
      when "edge_attr"
        append_statement("  edge [" + data + "]")
      else
        raise ArgumentError,
          "Unknown type: #{type}." <<
          "Possible: 'graph_attr','node_attr','edge_attr'"
      end

      self
    end

    def to_str
      @script
    end
    alias :to_s :to_str

    private

    def assure_ends_with(str,ending="\n")
      str.to_s.end_with?("\n") ? str : str + ending
    end

    def append_statement(statement)
      append(assure_ends_with(statement, ";\n"))
    end

  end
end
