/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package org.apache.flex.compiler.internal.abc;

import org.apache.flex.abc.semantics.Metadata;
import org.apache.flex.abc.semantics.Trait;
import org.apache.flex.abc.visitors.IMetadataVisitor;
import org.apache.flex.abc.visitors.ITraitVisitor;
import org.apache.flex.compiler.constants.IMetaAttributeConstants;
import org.apache.flex.compiler.definitions.IDefinition;
import org.apache.flex.compiler.definitions.IFunctionDefinition;
import org.apache.flex.compiler.definitions.metadata.IMetaTag;
import org.apache.flex.compiler.definitions.metadata.IMetaTagAttribute;
import org.apache.flex.compiler.internal.definitions.ClassDefinition;
import org.apache.flex.compiler.internal.definitions.DefinitionBase;
import org.apache.flex.compiler.internal.definitions.FunctionDefinition;
import org.apache.flex.compiler.internal.definitions.metadata.MetaTag;
import org.apache.flex.compiler.internal.definitions.metadata.MetaTagAttribute;

/**
 * Collect all the metadata and attributes in this trait.
 */
class CollectMetadataTraitVisitor implements ITraitVisitor
{
    CollectMetadataTraitVisitor(final DefinitionBase definition)
    {
        assert definition != null;
        this.definition = definition;
    }

    private final DefinitionBase definition;
    private IMetaTag[] metaTags;

    /**
     * Visiting all the metadata associated with this trait. The {@code count}
     * is used to determine size of MetaTag array.
     */
    @Override
    public IMetadataVisitor visitMetadata(final int count)
    {
        assert metaTags == null : "Can not re-use the collector.";
        metaTags = new IMetaTag[count];
        return new IMetadataVisitor()
        {
            int currentIndex = 0;

            @Override
            public void visit(Metadata metadata)
            {
                metaTags[currentIndex++] = createMetaTag(definition, metadata);
            }
        };
    }

    @Override
    public void visitStart()
    {
    }

    private IMetaTag findMetaTag(String name)
    {
        for (IMetaTag tag : metaTags)
        {
            if (tag.getTagName().equals(name))
                return tag;
        }
        return null;
    }
    
    private static void setNameLocation(IDefinition def, IMetaTag gotoDefTag)
    {
        String posString = gotoDefTag.getAttributeValue(IMetaAttributeConstants.NAME_GOTODEFINITIONHELP_POS);
        int nameStart = Integer.parseInt(posString);

        DefinitionBase constructorDef = (DefinitionBase)def;
        int nameEnd = nameStart + def.getBaseName().length();
        constructorDef.setNameLocation(nameStart, nameEnd);
    }
    
    /**
     * Store the meta tags in the definition object.
     */
    @Override
    public void visitEnd()
    {
        if (metaTags == null)
            metaTags = new IMetaTag[0];
        if (definition instanceof ClassDefinition)
        {
            ClassDefinition classDef = (ClassDefinition)definition;
            IFunctionDefinition constructor = classDef.getConstructor();
            if (constructor != null)
            {
                IMetaTag ctorGotoDefHelpTag = findMetaTag(IMetaAttributeConstants.ATTRIBUTE_GOTODEFINITION_CTOR_HELP);
                if (ctorGotoDefHelpTag != null)
                    setNameLocation(constructor, ctorGotoDefHelpTag);
            }
        }
        IMetaTag gotoDefHelpTag = findMetaTag(IMetaAttributeConstants.ATTRIBUTE_GOTODEFINITIONHELP);
        if (gotoDefHelpTag != null)
            setNameLocation(definition, gotoDefHelpTag);
        definition.setMetaTags(metaTags);
    }

    @Override
    public void visitAttribute(String attr_name, Object attr_value)
    {
        if (definition instanceof FunctionDefinition)
        {
            if (attr_name.equals(Trait.TRAIT_FINAL) && (Boolean)attr_value)
                definition.setFinal();
            else if (attr_name.equals(Trait.TRAIT_OVERRIDE) && (Boolean)attr_value)
                definition.setOverride();
        }
    }

    /**
     * Create a {@link IMetaTag} object from {@link Metadata}.
     * 
     * @param metadata metadata object
     * @return IMetaTag object
     */
    private static IMetaTag createMetaTag(final IDefinition definition, final Metadata metadata)
    {
        final String[] keys = metadata.getKeys();
        final String[] values = metadata.getValues();
        
        assert keys.length == values.length;
        final int attributesCount = keys.length;
        final IMetaTagAttribute[] attributes = new IMetaTagAttribute[attributesCount];
        for (int i = 0; i < attributesCount; i++)
            attributes[i] = new MetaTagAttribute(keys[i], values[i]);
        
        return new MetaTag(definition, metadata.getName(), attributes);
    }
}
