/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed 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 com.android.ide.common.resources;

import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.resources.configuration.FolderConfiguration;
import com.android.resources.ResourceType;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * An android resource.
 *
 * This is a representation of the resource, not of its value(s). It gives access to all
 * the source files that generate this particular resource which then can be used to access
 * the actual value(s).
 */
public class ResourceItem implements Comparable<ResourceItem> {

    private static final Comparator<ResourceFile> sComparator = new Comparator<ResourceFile>() {
        @Override
        public int compare(ResourceFile file1, ResourceFile file2) {
            // get both FolderConfiguration and compare them
            FolderConfiguration fc1 = file1.getFolder().getConfiguration();
            FolderConfiguration fc2 = file2.getFolder().getConfiguration();

            return fc1.compareTo(fc2);
        }
    };

    private final String mName;

    /**
     * List of files generating this ResourceItem.
     */
    private final List<ResourceFile> mFiles = new ArrayList<ResourceFile>();

    /**
     * Constructs a new ResourceItem.
     * @param name the name of the resource as it appears in the XML and R.java files.
     */
    public ResourceItem(String name) {
        mName = name;
    }

    /**
     * Returns the name of the resource.
     */
    public final String getName() {
        return mName;
    }

    /**
     * Compares the {@link ResourceItem} to another.
     * @param other the ResourceItem to be compared to.
     */
    @Override
    public int compareTo(ResourceItem other) {
        return mName.compareTo(other.mName);
    }

    /**
     * Returns whether the resource is editable directly.
     * <p>
     * This is typically the case for resources that don't have alternate versions, or resources
     * of type {@link ResourceType#ID} that aren't declared inline.
     */
    public boolean isEditableDirectly() {
        return hasAlternates() == false;
    }

    /**
     * Returns whether the ID resource has been declared inline inside another resource XML file.
     * If the resource type is not {@link ResourceType#ID}, this will always return {@code false}.
     */
    public boolean isDeclaredInline() {
        return false;
    }

    /**
     * Returns a {@link ResourceValue} for this item based on the given configuration.
     * If the ResourceItem has several source files, one will be selected based on the config.
     * @param type the type of the resource. This is necessary because ResourceItem doesn't embed
     *     its type, but ResourceValue does.
     * @param referenceConfig the config of the resource item.
     * @param isFramework whether the resource is a framework value. Same as the type.
     * @return a ResourceValue or null if none match the config.
     */
    public ResourceValue getResourceValue(ResourceType type, FolderConfiguration referenceConfig,
            boolean isFramework) {
        // look for the best match for the given configuration
        // the match has to be of type ResourceFile since that's what the input list contains
        ResourceFile match = (ResourceFile) referenceConfig.findMatchingConfigurable(mFiles);

        if (match != null) {
            // get the value of this configured resource.
            return match.getValue(type, mName);
        }

        return null;
    }

    /**
     * Adds a new source file.
     * @param file the source file.
     */
    protected void add(ResourceFile file) {
        mFiles.add(file);
    }

    /**
     * Removes a file from the list of source files.
     * @param file the file to remove
     */
    protected void removeFile(ResourceFile file) {
        mFiles.remove(file);
    }

    /**
     * Returns {@code true} if the item has no source file.
     * @return true if the item has no source file.
     */
    protected boolean hasNoSourceFile() {
        return mFiles.isEmpty();
    }

    /**
     * Reset the item by emptying its source file list.
     */
    protected void reset() {
        mFiles.clear();
    }

    /**
     * Returns the sorted list of {@link ResourceItem} objects for this resource item.
     */
    public ResourceFile[] getSourceFileArray() {
        ArrayList<ResourceFile> list = new ArrayList<ResourceFile>();
        list.addAll(mFiles);

        Collections.sort(list, sComparator);

        return list.toArray(new ResourceFile[list.size()]);
    }

    /**
     * Returns the list of source file for this resource.
     */
    public List<ResourceFile> getSourceFileList() {
        return Collections.unmodifiableList(mFiles);
    }

    /**
     * Returns if the resource has at least one non-default version.
     *
     * @see ResourceFile#getConfiguration()
     * @see FolderConfiguration#isDefault()
     */
    public boolean hasAlternates() {
        for (ResourceFile file : mFiles) {
            if (file.getFolder().getConfiguration().isDefault() == false) {
                return true;
            }
        }

        return false;
    }

    /**
     * Returns whether the resource has a default version, with no qualifier.
     *
     * @see ResourceFile#getConfiguration()
     * @see FolderConfiguration#isDefault()
     */
    public boolean hasDefault() {
        for (ResourceFile file : mFiles) {
            if (file.getFolder().getConfiguration().isDefault()) {
                return true;
            }
        }

        // We only want to return false if there's no default and more than 0 items.
        return (mFiles.isEmpty());
    }

    /**
     * Returns the number of alternate versions for this resource.
     *
     * @see ResourceFile#getConfiguration()
     * @see FolderConfiguration#isDefault()
     */
    public int getAlternateCount() {
        int count = 0;
        for (ResourceFile file : mFiles) {
            if (file.getFolder().getConfiguration().isDefault() == false) {
                count++;
            }
        }

        return count;
    }

    /**
     * Returns a formatted string usable in an XML to use for the {@link ResourceItem}.
     * @param system Whether this is a system resource or a project resource.
     * @return a string in the format @[type]/[name]
     */
    public String getXmlString(ResourceType type, boolean system) {
        if (type == ResourceType.ID && isDeclaredInline()) {
            return (system ? "@android:" : "@+") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
        }

        return (system ? "@android:" : "@") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }

    @Override
    public String toString() {
        return "ResourceItem [mName=" + mName + ", mFiles=" + mFiles + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    }
}
