module Dispel
  class StyleMap
    attr_accessor :lines, :foreground, :background

    def initialize(lines)
      @lines = Array.new(lines)
    end

    def add(style, line, columns)
      @lines[line] ||= []
      @lines[line] << [style, columns]
    end

    def prepend(style, line, columns)
      @lines[line] ||= []
      @lines[line].unshift [style, columns]
    end

    def flatten
      @lines.map do |styles|
        next unless styles

        # change to style at start and recalculate one after the end
        points_of_change = styles.map{|s,c| c.min ? [c.min, c.max + 1] : nil }.flatten.compact.uniq
        next if points_of_change.empty?

        flat = []

        points_of_change.each do |point|
          flat[point] = :normal # set default
          styles.each do |style, columns|
            next unless columns.include?(point)
            flat[point] = style
          end
        end

        flat
      end
    end

    def left_pad!(offset)
      @lines.compact.each do |styles|
        next unless styles
        styles.map! do |style, columns|
          [style, (columns.first + offset)..(columns.last + offset)]
        end
      end
    end

    def invert!
      map = {:reverse => :normal, :normal => :reverse}
      @lines.compact.each do |styles|
        styles.map! do |style, columns|
          [map[style] || style, columns]
        end
      end
    end

    def +(other)
      lines = self.lines + other.lines
      new = StyleMap.new(0)
      new.lines = lines
      new
    end

    def slice!(*args)
      sliced = lines.slice!(*args)
      new = StyleMap.new(0)
      new.lines = sliced
      new
    end

    def shift
      slice!(0, 1)
    end

    def pop
      slice!(-1, 1)
    end

    def self.styled(content, styles)
      styles ||= []
      content = content.dup

      build = []
      build << [:normal]

      buffered = ''
      styles.each do |style|
        if style
          build[-1] << buffered
          buffered = ''

          # set new style
          build << [style]
        end
        buffered << (content.slice!(0,1) || '')
      end
      build[-1] << buffered + content
      build
    end

    def self.single_line_reversed(columns)
      map = StyleMap.new(1)
      map.add(:reverse, 0, 0...columns)
      map
    end
  end
end
