require ‘shared_token_grammar’ require ‘dadl_grammar’ require ‘cadl_grammar’ require ‘adl’

module OpenEHR

module Parser
  grammar ADLGrammar

    include SharedToken
    include DADLGrammar
    include CADL

    rule archetype 
      arch_identification 
      spec:(arch_specialisation)?
      arch_concept
      lang:(arch_language)?
      desc:(arch_description)?
      arch_definition
      arch_invariant?
      arch_ontology {
        def archetype_id
          arch_identification.archetype_id
        end

        def adl_version
          arch_identification.adl_version
        end

        def concept
          arch_concept.value
        end

        def original_language
          lang.value.original_language unless lang.empty?
        end

        def translations
          lang.value.translations unless lang.empty?
        end

        def description
          desc.value unless desc.empty?
        end

        def definition
          arch_definition.value
        end

        def ontology
          arch_ontology.value
        end
      }
    end

    rule arch_identification
      head:arch_head id:V_ARCHETYPE_ID space {
        def archetype_id
          id.value
        end

        def adl_version
          head.value[:adl_version]
        end

        def is_controlled?
          head.value[:is_controlled?]
        end
      }
    end

    rule arch_head
      SYM_ARCHETYPE m:arch_meta_data {
        def value
          m.value
        end
      }
    / SYM_ARCHETYPE {
        def value
          Hash.new
        end
      }
    end

    rule arch_meta_data
      '(' arch_meta_data_items ')' space {
         def value
           arch_meta_data_items.value
         end
       }
    end

    rule arch_meta_data_items
      item:arch_meta_data_item other_item:(';' arch_meta_data_item)* {
        def value
          v = item.value
          other_item.elements.map {|i| i.arch_meta_data_item.value}
          v
        end
      }
    end

    rule arch_meta_data_item
      SYM_ADL_VERSION SYM_EQ ver:V_VERSION_STRING space {
        def value
          {:adl_version => ver.value}
        end
      }
    / SYM_IS_CONTROLED space {
        def value
          {:is_controled? => true} # if elements[0]
        end
      }
    end

    rule arch_specialisation
      SYM_SPECIALIZE arch_id:V_ARCHETYPE_ID space {
        def specialised?
          true if elements[0]
        end

        def archetype_id
          arch_id.value
        end
      }
    end

    rule arch_concept
      SYM_CONCEPT conc:V_LOCAL_TERM_CODE_REF space {
        def value
          conc.text_value[1..-2]
        end
      }
    end

    rule arch_language
      SYM_LANGUAGE lang:V_DADL_TEXT <ArchLanguage>
    end

    rule arch_description
      SYM_DESCRIPTION desc:V_DADL_TEXT space {
        def value
          params = desc.value
          details = { }
          params['details'].each do |lang, attrs|
            misuse = attrs['misuse']
            misuse = nil if misuse.nil? or misuse.empty?
            purpose = attrs['purpose'] || '__unknown__' # for backward compat.
            item =
              OpenEHR::RM::Common::Resource::ResourceDescriptionItem.new(
                :language => attrs['language'],
                :purpose => purpose,
                :keywords => attrs["keywords"],
                :use => attrs["use"],
                :misuse => misuse,
                :copyright => attrs['copyright'],
                :original_resource_uri => attrs['original_resource_uri'],
                :other_details => attrs['other_details'])
            details[lang] = item
          end
          oc = params['other_contributors']
          if oc.instance_of? Hash
            oc = oc.values
          end
          OpenEHR::RM::Common::Resource::ResourceDescription.new(
            :original_author => params['original_author'],
            :other_contributors => oc,
            :lifecycle_state => params['lifecycle_state'],
            :details => details,
            :resource_package_uri => params['archetype_package_uri'],
            :other_details => params['other_details'])
        end
      }
    end

    rule arch_definition
      SYM_DEFINITION definition:V_CADL_TEXT space {
        def value
          definition.value
        end
      }
    end

    rule arch_invariant
      SYM_INVARIANT V_ASSERTION_TEXT space
    end

    rule arch_ontology
      SYM_ONTOLOGY ontology:V_DADL_TEXT space {
        def value
          ao = ontology.value
          arc_term = Proc.new do |code, item|
            OpenEHR::AM::Archetype::Ontology::ArchetypeTerm.new(
                     :code => code, :items => item)
          end
          td = itemizer(ao['term_definitions'], arc_term)
          cd = ao['constraint_definitions']
          if cd
            cd = itemizer(cd , arc_term)
          end
          tb = ao['term_bindings'] || ao['term_binding']
          term_bind = Proc.new do |code, item|
            unless item.class == Array
              [item]
            else
              item
            end
          end
          if tb
            tb = itemizer(tb, term_bind)
          end
          cons_bind = Proc.new do |code, item|
            OpenEHR::RM::DataTypes::URI::DvUri.new(:value => item)
          end
          cb = ao['constraint_bindings'] || ao['constraint_binding']
          if cb
            cb = itemizer(cb, cons_bind)
          end
          OpenEHR::AM::Archetype::Ontology::ArchetypeOntology.new(
            :primary_language => ao['primary_language'],
            :languages_available => ao['languages_available'],
            :terminologies_available => ao['terminologies_available'],
            :term_definitions => td,
            :constraint_definitions => cd,
            :term_bindings => tb,
            :constraint_bindings => cb)
        end

         def itemizer(defs, itemize)
           defs.inject({ }) do |term_defs, langs|
             lang, items = langs
             terms = items['items'].inject({ }) do |items, term|
               code, item = term
               item = itemize.call code, item
               items.update Hash[code => item]
             end
             term_defs.update Hash[lang => terms]
           end
         end
      }
    end # of arch_ontology
  end # of ADLGrammar
end # of Parser

end # of openEHR