/*
 * Copyright (C) 2014 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.builder.compiling;

import static com.android.SdkConstants.ATTR_NAME;
import static com.android.SdkConstants.ATTR_TRANSLATABLE;
import static com.android.SdkConstants.ATTR_TYPE;
import static com.android.SdkConstants.TAG_ITEM;
import static com.android.SdkConstants.TAG_RESOURCES;
import static com.android.SdkConstants.VALUE_FALSE;
import static com.google.common.base.Preconditions.checkNotNull;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.builder.internal.ClassFieldImpl;
import com.android.builder.model.ClassField;
import com.android.ide.common.xml.XmlPrettyPrinter;
import com.android.resources.ResourceType;
import com.android.utils.XmlUtils;
import com.google.common.base.Charsets;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.io.Files;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 * Class able to generate a res value file in an Android project.
 */
public class ResValueGenerator {

    public static final String RES_VALUE_FILENAME_XML = "generated.xml";

    private static final List<ResourceType> RESOURCES_WITH_TAGS = ImmutableList.of(
            ResourceType.ARRAY,
            ResourceType.ATTR,
            ResourceType.BOOL,
            ResourceType.COLOR,
            ResourceType.DECLARE_STYLEABLE,
            ResourceType.DIMEN,
            ResourceType.FRACTION,
            ResourceType.INTEGER,
            ResourceType.PLURALS,
            ResourceType.STRING,
            ResourceType.STYLE);

    private final File mGenFolder;

    private final List<ClassField> mFields = Lists.newArrayList();
    private List<Object> mItems = Lists.newArrayList();

    /**
     * Creates a generator
     * @param genFolder the gen folder of the project
     */
    public ResValueGenerator(@NonNull File genFolder) {
        mGenFolder = checkNotNull(genFolder);
    }

    public ResValueGenerator addResource(
            @NonNull String type, @NonNull String name, @NonNull String value) {
        mFields.add(new ClassFieldImpl(type, name, value));
        return this;
    }

    public ResValueGenerator addItems(@Nullable Collection<Object> items) {
        if (items != null) {
            mItems.addAll(items);
        }
        return this;
    }

    /**
     * Returns a File representing where the BuildConfig class will be.
     */
    public File getFolderPath() {
        return new File(mGenFolder, "values");
    }

    /**
     * Generates the resource files
     */
    public void generate() throws IOException, ParserConfigurationException {
        File pkgFolder = getFolderPath();
        if (!pkgFolder.isDirectory()) {
            if (!pkgFolder.mkdirs()) {
                throw new RuntimeException("Failed to create " + pkgFolder.getAbsolutePath());
            }
        }

        File resFile = new File(pkgFolder, RES_VALUE_FILENAME_XML);

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setValidating(false);
        factory.setIgnoringComments(true);
        DocumentBuilder builder;

        builder = factory.newDocumentBuilder();
        Document document = builder.newDocument();

        Node rootNode = document.createElement(TAG_RESOURCES);
        document.appendChild(rootNode);

        rootNode.appendChild(document.createTextNode("\n"));
        rootNode.appendChild(document.createComment("Automatically generated file. DO NOT MODIFY"));
        rootNode.appendChild(document.createTextNode("\n\n"));

        for (Object item : mItems) {
            if (item instanceof ClassField) {
                ClassField field = (ClassField)item;

                ResourceType type = ResourceType.getEnum(field.getType());
                boolean hasResourceTag = (type != null && RESOURCES_WITH_TAGS.contains(type));

                Node itemNode = document.createElement(hasResourceTag ? field.getType() : TAG_ITEM);
                Attr nameAttr = document.createAttribute(ATTR_NAME);

                nameAttr.setValue(field.getName());
                itemNode.getAttributes().setNamedItem(nameAttr);

                if (!hasResourceTag) {
                    Attr typeAttr = document.createAttribute(ATTR_TYPE);
                    typeAttr.setValue(field.getType());
                    itemNode.getAttributes().setNamedItem(typeAttr);
                }

                if (type == ResourceType.STRING) {
                    Attr translatable = document.createAttribute(ATTR_TRANSLATABLE);
                    translatable.setValue(VALUE_FALSE);
                    itemNode.getAttributes().setNamedItem(translatable);
                }

                if (!field.getValue().isEmpty()) {
                    itemNode.appendChild(document.createTextNode(field.getValue()));
                }

                rootNode.appendChild(itemNode);
            } else if (item instanceof String) {
                rootNode.appendChild(document.createTextNode("\n"));
                rootNode.appendChild(document.createComment((String) item));
                rootNode.appendChild(document.createTextNode("\n"));
            }
        }

        String content;
        try {
            content = XmlPrettyPrinter.prettyPrint(document, true);
        } catch (Throwable t) {
            content = XmlUtils.toXml(document);
        }

        Files.write(content, resFile, Charsets.UTF_8);
    }
}
