/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * 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 org.auraframework.impl.source.file;

import java.nio.file.Path;
import java.util.EnumMap;
import java.util.Map;

import org.apache.log4j.Logger;
import org.auraframework.Aura;
import org.auraframework.def.DefDescriptor;
import org.auraframework.def.IncludeDef;
import org.auraframework.def.LibraryDef;
import org.auraframework.impl.system.DefDescriptorImpl;
import org.auraframework.system.SourceListener;
import org.auraframework.system.SourceListener.SourceMonitorEvent;
import org.auraframework.util.FileChangeEvent;
import org.auraframework.util.FileListener;

/**
 * Used by {@link FileSourceLoader} to monitor and notify when file has changed. When a file does change, it notifies
 * its listener to clear cache of specific descriptor.
 */
public class FileSourceListener implements FileListener{

    private static final Logger LOG = Logger.getLogger(FileSourceListener.class);
    private static final EnumMap<DefDescriptor.DefType, String> extensions = new EnumMap<>(
            DefDescriptor.DefType.class);

    static {
        extensions.put(DefDescriptor.DefType.APPLICATION, ".app");
        extensions.put(DefDescriptor.DefType.COMPONENT, ".cmp");
        extensions.put(DefDescriptor.DefType.EVENT, ".evt");
        extensions.put(DefDescriptor.DefType.LIBRARY, ".lib");
        extensions.put(DefDescriptor.DefType.INTERFACE, ".intf");
        extensions.put(DefDescriptor.DefType.STYLE, ".css");
        extensions.put(DefDescriptor.DefType.LAYOUTS, "Layouts.xml");
        extensions.put(DefDescriptor.DefType.NAMESPACE, ".xml");
        extensions.put(DefDescriptor.DefType.TESTSUITE, "Test.js");
        extensions.put(DefDescriptor.DefType.CONTROLLER, "Controller.js");
        extensions.put(DefDescriptor.DefType.RENDERER, "Renderer.js");
        extensions.put(DefDescriptor.DefType.PROVIDER, "Provider.js");
        extensions.put(DefDescriptor.DefType.HELPER, "Helper.js");
        extensions.put(DefDescriptor.DefType.MODEL, "Model.js");
        extensions.put(DefDescriptor.DefType.RESOURCE, "Resource.css");
        extensions.put(DefDescriptor.DefType.RESOURCE, "Resource.js");
        extensions.put(DefDescriptor.DefType.DOCUMENTATION, ".auradoc");
        extensions.put(DefDescriptor.DefType.DESIGN, ".design");
        extensions.put(DefDescriptor.DefType.SVG, ".svg");
    }

    @Override
    public void fileCreated(FileChangeEvent event) throws Exception {
        notifySourceChanges(event, SourceMonitorEvent.CREATED);
    }

    @Override
    public void fileDeleted(FileChangeEvent event) throws Exception {
        notifySourceChanges(event, SourceMonitorEvent.DELETED);
    }

    @Override
    public void fileChanged(FileChangeEvent event) throws Exception {
        notifySourceChanges(event, SourceMonitorEvent.CHANGED);
    }

    public void onSourceChanged(DefDescriptor<?> defDescriptor, SourceListener.SourceMonitorEvent smEvent,
            String filePath) {
        Aura.getDefinitionService().onSourceChanged(defDescriptor, smEvent, filePath);
    }

    private void notifySourceChanges(FileChangeEvent event, SourceListener.SourceMonitorEvent smEvent) {
        Path path = event.getPath();
        String filePath = path.toString();
        LOG.info("File " + filePath + " changed due to: " + smEvent);

        DefDescriptor<?> defDescriptor = getDefDescriptor(filePath);
        onSourceChanged(defDescriptor, smEvent, filePath);
    }

    private DefDescriptor<?> getDefDescriptor(String filePath) {
        filePath = filePath.replaceAll("\\\\", "/");
        String paths[] = filePath.split("/");
        String namespace = paths[paths.length - 3];
        String name = paths[paths.length - 2];
        String extension = filePath.substring(filePath.lastIndexOf("."));

        for (Map.Entry<DefDescriptor.DefType, String> entry : extensions.entrySet()) {
            String ext = entry.getValue();
            if (filePath.endsWith(ext)) {
                DefDescriptor.DefType defType = entry.getKey();

                String qname;
                if (extension.equalsIgnoreCase(".css")) {
                    qname = String.format("css://%s.%s", namespace, name);
                } else if (extension.equalsIgnoreCase(".js")) {
                    qname = String.format("js://%s.%s", namespace, name);
                } else {
                    qname = String.format("markup://%s:%s", namespace, name);
                }

                return DefDescriptorImpl.getInstance(qname, defType.getPrimaryInterface());
            }
        }
        // if no match for a js file so far, treat it as an IncludeDef
        if (extension.equalsIgnoreCase(".js")) {
            DefDescriptor<LibraryDef> library = DefDescriptorImpl.getInstance(
                    String.format("markup://%s:%s", namespace, name), LibraryDef.class);
            String fileName = paths[paths.length - 1];
            String resourceName = fileName.substring(0, fileName.length() - extension.length());
            return DefDescriptorImpl.getInstance(String.format("js://%s.%s", namespace, resourceName),
                    IncludeDef.class, library);
        }
        return null;
    }
}

@@ -971,16 +971,75 @@
 orImpl;%0A
+import org.auraframework.impl.source.DescriptorFileMapper;%0A
 import o
@@ -1450,16 +1450,45 @@
 istener 
+extends DescriptorFileMapper 
 implemen
@@ -1502,16 +1502,17 @@
 Listener
+ 
 %7B%0A%0A    p
@@ -1591,1437 +1591,8 @@
 ss);
-%0A    private static final EnumMap%3CDefDescriptor.DefType, String%3E extensions = new EnumMap%3C%3E(%0A            DefDescriptor.DefType.class);%0A%0A    static %7B%0A        extensions.put(DefDescriptor.DefType.APPLICATION, %22.app%22);%0A        extensions.put(DefDescriptor.DefType.COMPONENT, %22.cmp%22);%0A        extensions.put(DefDescriptor.DefType.EVENT, %22.evt%22);%0A        extensions.put(DefDescriptor.DefType.LIBRARY, %22.lib%22);%0A        extensions.put(DefDescriptor.DefType.INTERFACE, %22.intf%22);%0A        extensions.put(DefDescriptor.DefType.STYLE, %22.css%22);%0A        extensions.put(DefDescriptor.DefType.LAYOUTS, %22Layouts.xml%22);%0A        extensions.put(DefDescriptor.DefType.NAMESPACE, %22.xml%22);%0A        extensions.put(DefDescriptor.DefType.TESTSUITE, %22Test.js%22);%0A        extensions.put(DefDescriptor.DefType.CONTROLLER, %22Controller.js%22);%0A        extensions.put(DefDescriptor.DefType.RENDERER, %22Renderer.js%22);%0A        extensions.put(DefDescriptor.DefType.PROVIDER, %22Provider.js%22);%0A        extensions.put(DefDescriptor.DefType.HELPER, %22Helper.js%22);%0A        extensions.put(DefDescriptor.DefType.MODEL, %22Model.js%22);%0A        extensions.put(DefDescriptor.DefType.RESOURCE, %22Resource.css%22);%0A        extensions.put(DefDescriptor.DefType.RESOURCE, %22Resource.js%22);%0A        extensions.put(DefDescriptor.DefType.DOCUMENTATION, %22.auradoc%22);%0A        extensions.put(DefDescriptor.DefType.DESIGN, %22.design%22);%0A        extensions.put(DefDescriptor.DefType.SVG, %22.svg%22);%0A    %7D
 %0A%0A  
@@ -2584,27 +2584,24 @@
 ptor = getDe
-fDe
 scriptor(fil
@@ -2676,1750 +2676,7 @@
    %7D
-%0A%0A    private DefDescriptor%3C?%3E getDefDescriptor(String filePath) %7B%0A        filePath = filePath.replaceAll(%22%5C%5C%5C%5C%22, %22/%22);%0A        String paths%5B%5D = filePath.split(%22/%22);%0A        String namespace = paths%5Bpaths.length - 3%5D;%0A        String name = paths%5Bpaths.length - 2%5D;%0A        String extension = filePath.substring(filePath.lastIndexOf(%22.%22));%0A%0A        for (Map.Entry%3CDefDescriptor.DefType, String%3E entry : extensions.entrySet()) %7B%0A            String ext = entry.getValue();%0A            if (filePath.endsWith(ext)) %7B%0A                DefDescriptor.DefType defType = entry.getKey();%0A%0A                String qname;%0A                if (extension.equalsIgnoreCase(%22.css%22)) %7B%0A                    qname = String.format(%22css://%25s.%25s%22, namespace, name);%0A                %7D else if (extension.equalsIgnoreCase(%22.js%22)) %7B%0A                    qname = String.format(%22js://%25s.%25s%22, namespace, name);%0A                %7D else %7B%0A                    qname = String.format(%22markup://%25s:%25s%22, namespace, name);%0A                %7D%0A%0A                return DefDescriptorImpl.getInstance(qname, defType.getPrimaryInterface());%0A            %7D%0A        %7D%0A        // if no match for a js file so far, treat it as an IncludeDef%0A        if (extension.equalsIgnoreCase(%22.js%22)) %7B%0A            DefDescriptor%3CLibraryDef%3E library = DefDescriptorImpl.getInstance(%0A                    String.format(%22markup://%25s:%25s%22, namespace, name), LibraryDef.class);%0A            String fileName = paths%5Bpaths.length - 1%5D;%0A            String resourceName = fileName.substring(0, fileName.length() - extension.length());%0A            return DefDescriptorImpl.getInstance(String.format(%22js://%25s.%25s%22, namespace, resourceName),%0A                    IncludeDef.class, library);%0A        %7D%0A        return null;%0A    %7D
 %0A%7D%0A

Clean up file source listener.