#
# ActiveFacts Compositions, Metamodel aspect to create a textual summary of a composition and its Composites
#
# Copyright (c) 2015 Clifford Heath. Read the LICENSE file.
#
require "activefacts/metamodel"
require "activefacts/compositions/names"
require "activefacts/compositions/constraints"
require "activefacts/generator"

module ActiveFacts
  module Generators
    class Summary
      def self.options
        {
        }
      end

      def self.compatibility
        [1, nil]
      end

      def initialize constellation, composition, options = {}
        @constellation = constellation
        @composition = composition
        @options = options
      end

      def generate
        @composition.summary
      end
    end
    publish_generator Summary, "Succinctly display the full structure of any composition"
  end

  module Metamodel
    class Composition
      def summary
        classify_constraints

        vn = (v = constellation.Vocabulary.values[0]) ? v.version_number : nil
        version_str = vn ? " version #{vn}" : ''

        "Summary of #{name}#{version_str}\n" +
        all_composite.
        sort_by{|composite| composite.mapping.name}.
        flat_map do |composite|
          composite.summary
        end*''
      end
    end

    class Composite
      def summary
        indices = self.all_indices_by_rank
        fks = {}
        fk_count = 0

        (
          [mapping.name+"\n"] +
          mapping.
          all_leaf.
          reject{|leaf| leaf.is_a?(Absorption) && leaf.forward_mapping}.
          flat_map do |leaf|

            # Build a display of the names in this absorption path, with FK and optional indicators
            path_names = leaf.path.map do |component|
                is_mandatory = true
                is_unique = true
                case component
                when Absorption
                  is_mandatory = component.parent_role.is_mandatory
                  is_unique = component.parent_role.is_unique
                when Indicator
                  is_mandatory = false
                end

                # if all_foreign_key.detect{|fk| fk.all_foreign_key_field.detect{|fkf| fkf.component == leaf}}
                if component.is_a?(Mapping) && component.foreign_key && leaf.all_foreign_key_field.size > 0
                  fk_number = (fks[component.foreign_key] ||= (fk_count += 1))
                  "[F#{fk_number}:#{component.name}"
                elsif component == leaf && leaf.all_foreign_key_field.size > 0
                  "#{component.name}]"
                else
                  component.name
                end +
                  (is_mandatory ? '' : '?') + (is_unique ? '' : '*')
              end*'->'

            # Build a symbolic representation of the index participation of this leaf
            pos = 0
            indexing = indices.inject([]) do |a, index|
              # An index can be both Primary and Natural. Otherwise we show if it's Unique
              type_str = ''
              type_str << 'P' if index == primary_index
              type_str << 'N' if index == natural_index
              type_str = 'U' if type_str == '' && index.is_unique
              pos += 1
              if part = index.position_in_index(leaf)
                a << "#{type_str}#{pos}" + (index.all_index_field.size > 1 ? ".#{part}" : "")
              end
              a
            end
            if indexing.empty?
              indexing = ''
            else
              indexing = "[#{indexing*','}]"
            end

            column_name = leaf.column_name.capwords*' '
            ["\t#{path_names}#{indexing} as #{column_name.inspect}\n"] +
            leaf.all_leaf_constraint.map{|leaf_constraint| "\t\t### #{leaf_constraint.leaf_constraint.describe}\n"}.sort
          end +
          all_local_constraint.map do |local_constraint|
            "\t### #{local_constraint.local_constraint.describe}\n"
          end.sort +
          all_spanning_constraint.map do |spanning_constraint|
            "### #{spanning_constraint.spanning_constraint.describe}\n"
          end.sort

        )*''
      end
    end
  end
end
