/*
 * 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.tools.lint.checks;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.android.builder.model.AndroidProject;
import com.android.tools.lint.detector.api.Detector;
import com.android.tools.lint.detector.api.Project;

import java.io.File;
import java.util.Arrays;

public class ResourcePrefixDetectorTest extends AbstractCheckTest {

    @Override
    protected Detector getDetector() {
        return new ResourcePrefixDetector();
    }

    public void testResourceFiles() throws Exception {
        assertEquals(""
            + "res/drawable-mdpi/frame.png: Error: Resource named 'frame' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_frame' ? [ResourceName]\n"
            + "res/layout/layout1.xml:2: Error: Resource named 'layout1' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_layout1' ? [ResourceName]\n"
            + "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
            + "^\n"
            + "res/menu/menu.xml:2: Error: Resource named 'menu' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_menu' ? [ResourceName]\n"
            + "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\" >\n"
            + "^\n"
            + "3 errors, 0 warnings\n",
            lintProject(
                "res/layout/layout1.xml",
                "res/menu/menu.xml",
                "res/layout/layout1.xml=>res/layout/unit_test_prefix_ok.xml",
                "res/drawable-mdpi/frame.png",
                "res/drawable-mdpi/frame.png=>res/drawable/unit_test_prefix_ok1.png",
                "res/drawable-mdpi/frame.png=>res/drawable/unit_test_prefix_ok2.9.png"
            ));
    }

    public void testValues() throws Exception {
        assertEquals(""
            + "res/values/customattr.xml:2: Error: Resource named 'ContentFrame' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_ContentFrame' ? [ResourceName]\n"
            + "    <declare-styleable name=\"ContentFrame\">\n"
            + "                       ~~~~~~~~~~~~~~~~~~~\n"
            + "res/values/customattr.xml:3: Error: Resource named 'content' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_content' ? [ResourceName]\n"
            + "        <attr name=\"content\" format=\"reference\" />\n"
            + "              ~~~~~~~~~~~~~~\n"
            + "res/values/customattr.xml:4: Error: Resource named 'contentId' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_contentId' ? [ResourceName]\n"
            + "        <attr name=\"contentId\" format=\"reference\" />\n"
            + "              ~~~~~~~~~~~~~~~~\n"
            + "res/layout/customattrlayout.xml:2: Error: Resource named 'customattrlayout' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_customattrlayout' ? [ResourceName]\n"
            + "<foo.bar.ContentFrame\n"
            + "^\n"
            + "4 errors, 0 warnings\n",

            lintProject(
                    "res/values/customattr.xml",
                    "res/layout/customattrlayout.xml",
                    "unusedR.java.txt=>gen/my/pkg/R.java",
                    "AndroidManifest.xml"));
    }

    public void testMultiProject() throws Exception {
        File master = getProjectDir("MasterProject",
                // Master project
                "multiproject/main-manifest.xml=>AndroidManifest.xml",
                "multiproject/main.properties=>project.properties",
                "multiproject/MainCode.java.txt=>src/foo/main/MainCode.java"
        );
        File library = getProjectDir("LibraryProject",
                // Library project
                "multiproject/library-manifest.xml=>AndroidManifest.xml",
                "multiproject/library.properties=>project.properties",
                "multiproject/LibraryCode.java.txt=>src/foo/library/LibraryCode.java",
                "multiproject/strings.xml=>res/values/strings.xml"
        );
        assertEquals(""
            + "LibraryProject/res/values/strings.xml:4: Error: Resource named 'app_name' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_app_name' ? [ResourceName]\n"
            + "    <string name=\"app_name\">LibraryProject</string>\n"
            + "            ~~~~~~~~~~~~~~~\n"
            + "LibraryProject/res/values/strings.xml:5: Error: Resource named 'string1' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_string1' ? [ResourceName]\n"
            + "    <string name=\"string1\">String 1</string>\n"
            + "            ~~~~~~~~~~~~~~\n"
            + "LibraryProject/res/values/strings.xml:6: Error: Resource named 'string2' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_string2' ? [ResourceName]\n"
            + "    <string name=\"string2\">String 2</string>\n"
            + "            ~~~~~~~~~~~~~~\n"
            + "LibraryProject/res/values/strings.xml:7: Error: Resource named 'string3' does not start with the project's resource prefix 'unit_test_prefix_'; rename to 'unit_test_prefix_string3' ? [ResourceName]\n"
            + "    <string name=\"string3\">String 3</string>\n"
            + "            ~~~~~~~~~~~~~~\n"
            + "4 errors, 0 warnings\n",

            checkLint(Arrays.asList(master, library)).replace("/TESTROOT/",""));
    }

    public void testSuppressGeneratedRs() throws Exception {
        assertEquals("No warnings.",
                lintProject(
                        copy("res/layout/layout1.xml", "res/raw/blend.bc")
                ));

    }

    public void testAndroidPrefix() throws Exception {
        // Regression test for
        // 208973: Lint check for resource prefix doesn't ignore android: attributes
        assertEquals("No warnings.",
                lintProject(xml("res/values/values.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n"
                        + "  <declare-styleable name=\"Unit_test_prefix_MyView\">\n"
                        + "    <attr name=\"android:textColor\"/>\n"
                        + "    <attr name=\"unit_test_prefix_myAttribute\" format=\"reference\"/>\n"
                        + "  </declare-styleable>\n"
                        + "</resources>\n")
                ));
    }

    public void testStyleableName() throws Exception {
        assertEquals("No warnings.",
                lintProject(xml("res/values/values.xml", ""
                        + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                        + "<resources xmlns:tools=\"http://schemas.android.com/tools\">\n"
                        + "  <declare-styleable name=\"Unit_test_prefixMyView\"/>\n"
                        + "</resources>\n")
                ));
    }
    // TODO: Test suppressing root level tag

    @Override
    protected TestLintClient createClient() {
        return new TestLintClient() {
            @NonNull
            @Override
            protected Project createProject(@NonNull File dir, @NonNull File referenceDir) {
                return new Project(this, dir, referenceDir) {
                    @Override
                    public boolean isGradleProject() {
                        return true;
                    }

                    @Nullable
                    @Override
                    public AndroidProject getGradleProjectModel() {
                        AndroidProject project = mock(AndroidProject.class);
                        when(project.getResourcePrefix()).thenReturn("unit_test_prefix_");
                        return project;
                    }
                };
            }
        };
    }
}
