/*
 * Copyright 2017 Rod MacKenzie.
 *
 * 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.github.rodm.teamcity.gradle.scripts.server;

import jetbrains.buildServer.controllers.admin.projects.EditProjectTab;
import jetbrains.buildServer.serverSide.BuildTypeTemplate;
import jetbrains.buildServer.serverSide.SBuildFeatureDescriptor;
import jetbrains.buildServer.serverSide.SBuildType;
import jetbrains.buildServer.serverSide.SProject;
import jetbrains.buildServer.web.openapi.PagePlaces;
import jetbrains.buildServer.web.openapi.PluginDescriptor;
import org.jetbrains.annotations.NotNull;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import static com.github.rodm.teamcity.gradle.scripts.GradleInitScriptsPlugin.FEATURE_TYPE;
import static com.github.rodm.teamcity.gradle.scripts.GradleInitScriptsPlugin.INIT_SCRIPT_NAME;
import static com.github.rodm.teamcity.gradle.scripts.GradleInitScriptsPlugin.PLUGIN_NAME;

public class GradleInitScriptsPage extends EditProjectTab {

    private static String TITLE = "Gradle Init Scripts";

    private GradleScriptsManager scriptsManager;

    GradleInitScriptsPage(@NotNull final PagePlaces pagePlaces,
                          @NotNull final PluginDescriptor descriptor,
                          @NotNull final GradleScriptsManager scriptsManager)
    {
        super(pagePlaces, PLUGIN_NAME, descriptor.getPluginResourcesPath("projectPage.jsp"), TITLE);
        this.scriptsManager = scriptsManager;
        addCssFile("/css/admin/buildTypeForm.css");
        addJsFile(descriptor.getPluginResourcesPath("initScripts.js"));
    }

    @Override
    public void fillModel(@NotNull Map<String, Object> model, @NotNull HttpServletRequest request) {
        super.fillModel(model, request);
        final SProject project = getProject(request);
        if (project != null) {
            String fileName = request.getParameter("file");
            if (fileName != null) {
                String fileContent = scriptsManager.findScript(project, fileName);
                if (fileContent != null) {
                    model.put("fileName", fileName);
                    model.put("fileContent", fileContent);
                }
            }
            Map<SProject, List<String>> scripts = scriptsManager.getScriptNames(project);
            model.put("scripts", scripts);
            Map<String, ScriptUsage> usage = getScriptsUsage(scripts);
            model.put("usage", usage);
        }
    }

    @NotNull
    @Override
    public String getTabTitle(@NotNull final HttpServletRequest request) {
        final SProject project = getProject(request);
        String result = TITLE;
        if (project != null) {
            int count =  scriptsManager.getScriptsCount(project);
            if (count > 0) {
                result = TITLE + " (" + count + ")";
            }
        }
        return result;
    }

    private Map<String, ScriptUsage> getScriptsUsage(Map<SProject, List<String>> scripts) {
        Map<String, ScriptUsage> usage = new LinkedHashMap<>();
        for (Map.Entry<SProject, List<String>> entry : scripts.entrySet()) {
            for (String script : entry.getValue()) {
                usage.put(script, new ScriptUsage());
            }
        }

        for (Map.Entry<SProject, List<String>> entry : scripts.entrySet()) {
            SProject project = entry.getKey();
            addScriptUsageForSubProjects(project, usage, Collections.emptyList());
            addScriptUsageForProject(project, usage, Collections.emptyList());
        }
        return usage;
    }

    private void addScriptUsageForSubProjects(SProject parent, Map<String, ScriptUsage> usage, List<String> subProjectScripts) {
        for (SProject project : parent.getOwnProjects()) {
            List<String> scripts = getProjectScripts(project);
            scripts.addAll(subProjectScripts);
            addScriptUsageForSubProjects(project, usage, scripts);
            addScriptUsageForProject(project, usage, scripts);
        }
    }

    private void addScriptUsageForProject(SProject project, Map<String, ScriptUsage> usage, List<String> subProjectScripts) {
        for (SBuildType buildType : project.getOwnBuildTypes()) {
            for (SBuildFeatureDescriptor feature : buildType.getBuildFeaturesOfType(FEATURE_TYPE)) {
                Map<String, String> parameters = feature.getParameters();
                String scriptName = parameters.get(INIT_SCRIPT_NAME);
                if (!subProjectScripts.contains(scriptName) && usage.get(scriptName) != null) {
                    usage.get(scriptName).addBuildType(buildType);
                }
            }
        }
        for (BuildTypeTemplate buildTemplate : project.getOwnBuildTypeTemplates()) {
            for (SBuildFeatureDescriptor feature : buildTemplate.getBuildFeaturesOfType(FEATURE_TYPE)) {
                Map<String, String> parameters = feature.getParameters();
                String scriptName = parameters.get(INIT_SCRIPT_NAME);
                if (!subProjectScripts.contains(scriptName) && usage.get(scriptName) != null) {
                    usage.get(scriptName).addBuildTemplate(buildTemplate);
                }
            }
        }
    }

    private List<String> getProjectScripts(SProject project) {
        return scriptsManager.getScriptNames(project).getOrDefault(project, new ArrayList<>());
    }
}

@@ -1180,38 +1180,8 @@
 st;%0A
-import java.util.Collections;%0A
 impo
@@ -3711,32 +3711,79 @@
 s.entrySet()) %7B%0A
+            SProject project = entry.getKey();%0A
             for 
@@ -3881,153 +3881,8 @@
     
-        %7D%0A        %7D%0A%0A        for (Map.Entry%3CSProject, List%3CString%3E%3E entry : scripts.entrySet()) %7B%0A            SProject project = entry.getKey();%0A
     
@@ -3910,32 +3910,40 @@
 eForSubProjects(
+script, 
 project, usage, 
@@ -3944,114 +3944,24 @@
 sage
-, Collections.emptyList());%0A            addScriptUsageForProject(project, usage, Collections.emptyList());
+);%0A            %7D
 %0A   
@@ -4042,16 +4042,29 @@
 rojects(
+String name, 
 SProject
@@ -4106,40 +4106,8 @@
 sage
-, List%3CString%3E subProjectScripts
 ) %7B%0A
@@ -4240,24 +4240,29 @@
         
+if (!
 scripts.
 addAll(s
@@ -4257,35 +4257,30 @@
 pts.
-addAll(subProjectScripts);%0A
+contains(name)) %7B%0A    
     
@@ -4316,16 +4316,22 @@
 rojects(
+name, 
 project,
@@ -4336,32 +4336,43 @@
 t, usage
-, scripts
 );%0A    
+        %7D%0A        %7D%0A
         
@@ -4400,43 +4400,29 @@
 ect(
-project, usage, scripts);%0A        %7D
+name, parent, usage);
 %0A   
@@ -4467,16 +4467,29 @@
 Project(
+String name, 
 SProject
@@ -4532,40 +4532,8 @@
 sage
-, List%3CString%3E subProjectScripts
 ) %7B%0A
@@ -4867,80 +4867,31 @@
 if (
-!subProjectScripts.contains(scriptName) && usage.get(scriptName) != null
+scriptName.equals(name)
 ) %7B%0A
@@ -5357,80 +5357,31 @@
 if (
-!subProjectScripts.contains(scriptName) && usage.get(scriptName) != null
+scriptName.equals(name)
 ) %7B%0A

Correct usage to not show build types or templates from sibling projects