require 'my_chart/type'

module MyChart
  module Dsl
    module Draw

      MyChart::Type.each_sym do |chart_cmd|

        define_method chart_cmd do |*arg|
          chart_config = ChartCmdARGV.new *arg

          chart_id = "#{chart_cmd}__#{chart_config.data_id}".to_sym
          klass = MyChart::Type.concrete chart_cmd
          grp_data = grouped chart_config

          charts[chart_id] = klass.new grp_data, id: chart_id, w: chart_config.w, h: chart_config.h, name: chart_config.name
        end
      end

      def charts
        @charts ||= {}
      end

      def grouped cfg = nil
        @grouped ||= {}

        return @grouped unless cfg

        x = get_x cfg.from
        grp_m = check_overwrite_group_method cfg.x
        xy = (@grouped[[cfg.x, cfg.from]] ||= (x.group_by &grp_m))
        xy = (@grouped[[cfg.x, cfg.keys, cfg.from]] ||= (xy.complete_keys cfg.keys)) if cfg.keys
        xy = xy.sort(cfg) if cfg.asc or cfg.desc
        xy = xy.limit(cfg) if cfg.first or cfg.last
        return xy unless cfg.y

        grp_m = check_overwrite_group_method cfg.y
        @grouped[[cfg.x, cfg.y, cfg.keys, cfg.from]] ||= xy.group_by(&grp_m)
      end

      def check_overwrite_group_method method_id
        group_by_methods[method_id] || method_id
      end

      class ChartCmdARGV

        attr_reader :opt, :x, :y

        def initialize *arg
          return if arg.empty?
          @opt = (arg[-1].kind_of? Hash) ? arg[-1] : {}
          @x = arg[0] if arg[0].kind_of? Symbol
          @y = arg[1] if arg[1] and arg[1].kind_of? Symbol
        end

        def method_missing name, *arg
          return opt[name] if [:w, :h, :name, :from, :keys, :asc, :desc, :first, :last].include? name
          super
        end

        def sort
          order = (asc and :asc) or (desc and :desc)
          [order, 'by', (asc or desc)].join('_') if order
        end

        def limit
          lm = (first and :first) or (last and :last)
          [lm, (first or last)].join('_') if lm
        end

        def data_id
          [x,
           y ? y : "no_y",
           keys ? "keys_#{keys.hash}" : "no_keys",
           from ? "from_#{from}" : "from_all",
           sort,
           limit
          ].compact.join '__'
        end
      end

    end
  end
end