/*
 * Copyright 2013 the original author or authors.
 *
 * 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 leap.orm.sql;

import leap.core.*;
import leap.core.annotation.Inject;
import leap.core.annotation.M;
import leap.lang.Strings;
import leap.lang.io.FileChangeListenerAdaptor;
import leap.lang.io.FileChangeObserver;
import leap.lang.logging.Log;
import leap.lang.logging.LogFactory;
import leap.lang.resource.Resource;
import leap.lang.resource.Resources;

import java.io.File;
import java.util.HashSet;
import java.util.Set;

public class ClasspathSqlSource implements SqlSource {
	
	private static final Log log = LogFactory.get(ClasspathSqlSource.class);

    protected @Inject @M AppConfig      config;
    protected @Inject @M AppFileMonitor fileMonitor;
    protected @Inject @M SqlReader[]    readers;
	
	@Override
    public void loadSqlCommands(SqlConfigContext context) throws SqlConfigException, SqlClauseException {
		//load all sqls
		loadSqls(context, AppResources.get(config).search("sqls"));
		
		Resource dir = AppResources.getAppClasspathDirectory("sqls");
		if(dir.isFile()) {
            monitorSqls(context, dir.getFile());
		}
    }
	
	protected void loadSqls(SqlConfigContext context,AppResource[] resources){
		loadSqls(new LoadContext(context,false), resources);
	}
	
	protected void monitorSqls(SqlConfigContext context,File dir) {
		FileChangeObserver observer = new FileChangeObserver(dir);
		observer.addListener(new FileChangeListenerAdaptor(){
			@Override
            public void onFileCreate(FileChangeObserver observer, File file) {
				log.info("Sql file '" + file.getAbsolutePath() + "' was created, load it");
				Resource r = Resources.createFileResource(file);
				loadSqls(new LoadContext(context, true), new SimpleAppResource(r));
			}

			@Override
            public void onFileChange(FileChangeObserver observer, File file) {
				log.info("Sql file '" + file.getAbsolutePath() + "' was changed, reload it");
				Resource r = Resources.createFileResource(file);
				loadSqls(new LoadContext(context, true), new SimpleAppResource(r));
			}

			@Override
            public void onFileDelete(FileChangeObserver observer, File file) {
				log.info("Sql file '" + file.getAbsolutePath() + "' was deleted, do nothing");
			}
			
		});
		
		fileMonitor.addObserver(observer);
	}
	
	protected void loadSqls(SqlReaderContext context,AppResource... resources){
		for(int i=0;i<resources.length;i++){
            AppResource ar = resources[i];
			Resource resource = ar.getResource();
			
			if(resource.isReadable() && resource.exists()){
				try{
					String resourceUrl = resource.getURL().toString();
					
					if(context.getResourceUrls().contains(resourceUrl)){
						throw new AppConfigException("Cycle importing detected, please check your config : " + resourceUrl);
					}

					context.getResourceUrls().add(resourceUrl);

                    context.setDefaultOverride(ar.isDefaultOverride());

					for(SqlReader reader : readers) {
						if(reader.readSqlCommands(context, resource)) {
							break;
						}
					}

                    context.resetDefaultOverride();
				}catch(SqlConfigException e){
					throw e;
				}catch(Exception e){
					throw new SqlConfigException("Error loading sqls from 'classpath:" + resource.getClasspath() + "', msg : " + e.getMessage(),e);
				}
			}
		}
	}
	
	private final class LoadContext implements SqlReaderContext {
		private final Set<String> 	   resources = new HashSet<>();
		private final SqlConfigContext configContext;
        private final boolean          originalDefaultOverride;

		private boolean defaultOverride;
		
		private LoadContext(SqlConfigContext context, boolean defaultOverride){
			this.configContext   = context;
            this.originalDefaultOverride = defaultOverride;
			this.defaultOverride = defaultOverride;
		}
		
		@Override
        public SqlConfigContext getConfigContext() {
	        return configContext;
        }

		@Override
        public Set<String> getResourceUrls() {
	        return resources;
        }

		@Override
        public boolean isDefaultOverride() {
	        return defaultOverride;
        }

        @Override
        public void setDefaultOverride(boolean defaultOverride) {
            this.defaultOverride = defaultOverride;
        }

        @Override
        public void resetDefaultOverride() {
            this.defaultOverride = originalDefaultOverride;
        }

        public boolean acceptDbType(String dbType){
			return Strings.isEmpty(dbType) || configContext.getDb().getType().equalsIgnoreCase(dbType);
		}
	}
}
@@ -899,16 +899,43 @@
 actory;%0A
+import leap.lang.net.Urls;%0A
 import l
@@ -962,16 +962,16 @@
 source;%0A
-
 import l
@@ -1021,16 +1021,44 @@
 o.File;%0A
+import java.io.IOException;%0A
 import j
@@ -1521,72 +1521,489 @@
 qls%0A
-%09%09loadSqls(context, AppResources.get(config).search(%22sqls%22));%0A%09%09
+%0A%09%09AppResource resources%5B%5D = AppResources.get(config).search(%22sqls%22);%0A%0A%09%09loadSqls(context, resources);%0A%0A%09%09for(AppResource resource : resources)%7B%0A%09%09%09try %7B%0A%09%09%09%09if(Urls.isFileUrl(resource.getResource().getURL()))%7B%0A%09%09%09%09%09fileMonitor.addObserver(new FileChangeObserver(resource.getResource().getFile()));%0A                %7Delse if(Urls.isJarUrl(resource.getResource().getURL()))%7B%0A%09%09%09%09%09// TODO how to monitor jar file?%0A%09%09%09%09%7D%0A%09%09%09%7D catch (IOException e) %7B%0A%09%09%09%09e.printStackTrace();%0A%09%09%09%7D%0A%09%09%7D%0A%0A
 %0A%09%09R

add monitor dependency project sqls