# frozen_string_literal: true

require_relative 'chart_axis'
require_relative 'chart_cells_range'
require_relative 'chart_legend'
require_relative 'chart_point'
require_relative 'display_labels_properties'
require_relative 'chart/chart_style_file'
require_relative 'chart/pivot_formats'
require_relative 'chart/plot_area'
require_relative 'chart/series'
require_relative 'chart/view_3d'

module OoxmlParser
  # Class for working with Chart data
  class Chart < OOXMLDocumentObject
    attr_accessor :type, :data, :grouping, :title, :legend, :display_labels, :axises, :alternate_content, :shape_properties
    # @return [Array, ValuedChild] array axis id
    attr_reader :axis_ids
    # @return [Array, Series] series of chart
    attr_accessor :series
    # @return [PivotFormats] list of pivot formats
    attr_accessor :pivot_formats
    # @return [PlotArea] plot area data
    attr_accessor :plot_area
    # @return [True, False] This element specifies that
    # each data marker in the series has a different color.
    # ECMA-376 (5th Edition). 21.2.2.227
    attr_reader :vary_colors
    # @return [View3D] properties of 3D view
    attr_accessor :view_3d
    # @return [ChartStyle] style of current chart
    attr_reader :style
    # @return [Relationships] relationships of chart
    attr_reader :relationships

    def initialize(parent: nil)
      @axis_ids = []
      @type = ''
      @data = []
      @grouping = nil
      @title = nil
      @legend = nil
      @axises = []
      @series = []
      super
    end

    # Parse properties of Chart
    # @param chart_prop_node [Nokogiri::XML:Element] node to parse
    # @return [void]
    def parse_properties(chart_prop_node)
      chart_prop_node.xpath('*').each do |chart_props_node_child|
        case chart_props_node_child.name
        when 'axId'
          @axis_ids << ValuedChild.new(:integer, parent: self).parse(chart_props_node_child)
        when 'grouping'
          @grouping = chart_props_node_child.attribute('val').value.to_sym
        when 'ser'
          @series << Series.new(parent: self).parse(chart_props_node_child)
          val = case @type
                when :point, :bubble
                  chart_props_node_child.xpath('c:yVal')[0]
                else
                  chart_props_node_child.xpath('c:val')[0]
                end
          next unless val
          next if val.xpath('c:numRef').empty?

          @data << ChartCellsRange.new(parent: self).parse(val.xpath('c:numRef').first)
        when 'dLbls'
          @display_labels = DisplayLabelsProperties.new(parent: self).parse(chart_props_node_child)
        when 'varyColors'
          @vary_colors = option_enabled?(chart_props_node_child)
        end
      end
    end

    extend Gem::Deprecate
    deprecate :data, 'series points interface', 2020, 1

    # Parse Chart data
    # @return [Chart] result of parsing
    def parse
      chart_xml = parse_xml(root_object.current_xml)
      chart_xml.xpath('*').each do |chart_node|
        case chart_node.name
        when 'chartSpace'
          parse_chart_space(chart_node)
        end
      end
      parse_relationships
      parse_style
      self
    end

    private

    # Parse 'chartSpace' node
    # @return [nil]
    def parse_chart_space(chart_space_node)
      chart_space_node.xpath('*').each do |chart_space_node_child|
        case chart_space_node_child.name
        when 'AlternateContent'
          @alternate_content = AlternateContent.new(parent: self).parse(chart_space_node_child)
        when 'spPr'
          @shape_properties = DocxShapeProperties.new(parent: self).parse(chart_space_node_child)
        when 'chart'
          chart_space_node_child.xpath('*').each do |chart_node_child|
            case chart_node_child.name
            when 'plotArea'
              chart_node_child.xpath('*').each do |plot_area_node_child|
                next unless type.empty?

                parse_chart_type(plot_area_node_child)
                parse_properties(plot_area_node_child)
              end
              parse_axis(chart_node_child)
              @plot_area = PlotArea.new(parent: self).parse(chart_node_child)
            when 'title'
              @title = ChartAxisTitle.new(parent: self).parse(chart_node_child)
            when 'legend'
              @legend = ChartLegend.new(parent: self).parse(chart_node_child)
            when 'view3D'
              @view_3d = View3D.new(parent: self).parse(chart_node_child)
            when 'pivotFmts'
              @pivot_formats = PivotFormats.new(parent: self).parse(chart_node_child)
            end
          end
        end
      end
    end

    # Parse chart type and properties
    # @return [nil]
    def parse_chart_type(plot_area_node_child)
      case plot_area_node_child.name
      when 'barChart'
        @type = :bar
        bar_dir_node = plot_area_node_child.xpath('c:barDir')
        @type = :column if bar_dir_node.first.attribute('val').value == 'col'
      when 'lineChart'
        @type = :line
      when 'areaChart'
        @type = :area
      when 'bubbleChart'
        @type = :bubble
      when 'doughnutChart'
        @type = :doughnut
      when 'pieChart'
        @type = :pie
      when 'scatterChart'
        @type = :point
      when 'radarChart'
        @type = :radar
      when 'stockChart'
        @type = :stock
      when 'surface3DChart'
        @type = :surface_3d
      when 'line3DChart'
        @type = :line_3d
      when 'bar3DChart'
        @type = :bar_3d
      when 'pie3DChart'
        @type = :pie_3d
      end
    end

    # Perform parsing of axis info
    # @param node [Nokogiri::XML:Element] node to parse
    # @return [void]
    def parse_axis(node)
      node.xpath('*').each do |node_child|
        case node_child.name
        when 'catAx', 'valAx'
          @axises << ChartAxis.new(parent: self).parse(node_child)
        end
      end
    end

    # Parse relationship of chart
    def parse_relationships
      file_name = File.basename(root_object.current_xml)
      relationship_file = "#{root_object.unpacked_folder}" \
                          '/word/charts/' \
                          "_rels/#{file_name}.rels"

      return unless File.exist?(relationship_file)

      @relationships = Relationships.new(parent: self)
                                    .parse_file(relationship_file)
    end

    def parse_style
      return unless @relationships

      chart_relationship = @relationships.target_by_type('chartStyle')
      return if chart_relationship.empty?

      chart_style_file = chart_relationship.first
      style_file = "#{root_object.unpacked_folder}" \
                   "/word/charts/#{chart_style_file}"

      @style = ChartStyleFile.new(parent: self).parse(style_file)
    end
  end
end
