package me.chyxion.tigon.shiro;

import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.servlet.Filter;
import org.slf4j.LoggerFactory;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import javax.annotation.PostConstruct;
import me.chyxion.tigon.webmvc.JSONViewConfig;
import me.chyxion.tigon.webmvc.util.ResponseTool;
import me.chyxion.tigon.webmvc.exception.ExceptionMessage;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * @version 0.0.1
 * @since 0.0.1
 * @author Shaun Chyxion <br>
 * chyxion@163.com <br>
 * Apr 7, 2015 4:44:04 PM
 */
public class AuthConfigFactory {
    private static final Logger log = 
        LoggerFactory.getLogger(AuthConfigFactory.class);
    @Autowired(required = false)
    private AuthConfig authConfig;
    @Autowired
    private ResponseTool responseTool;
    @Autowired
    private JSONViewConfig viewConfig;
    @Autowired(required = false)
    private ExceptionMessage exceptionMessage;

    private UserFilterExt userFilter;

    /**
     * @return filters
     */
    public Map<String, Filter> filters() {
        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
        filters.put(AuthConfig.FILTER_USER, userFilter);
        if (authConfig != null) {
            authConfig.customizeFilters(filters);
        }
        log.info("Auth Filters [{}].", filters);
        return filters;
    }

    /**
     * @return filter routings
     */
    public Map<String, String> filterRoutings() {
        Map<String, String> routings = new LinkedHashMap<String, String>();
        routings.put("/assets/**", AuthConfig.FILTER_ANON);
        InputStream authIn = AuthConfigFactory.class
            .getResourceAsStream("/shiro/auth.properties");
        if (authIn != null) {
            Properties props = new OrderedProperties();
            try {
                props.load(authIn);
            }
            catch (IOException e) {
                throw new IllegalStateException(
                    "Load Auth Config Error Caused", e);
            }
            finally {
                if (authIn != null) {
                    try {
                        authIn.close();
                    }
                    catch (IOException e) {
                        // ignore
                    }
                }
            }
            for (String name : props.stringPropertyNames()) {
                routings.put(name, props.getProperty(name));
            }
        }
        log.info("Auth Filter Routings [{}].", routings);
        return routings;
    }

    /**
     * init
     */
    @PostConstruct
    void init() {
        userFilter = new UserFilterExt(
            responseTool, viewConfig, exceptionMessage);
    }

    static class OrderedProperties extends Properties {
        private static final long serialVersionUID = 1L;
        private final Set<String> keys = new LinkedHashSet<String>();

        public Object put(Object key, Object value) {
            keys.add((String) key);
            return super.put(key, value);
        }

        @SuppressWarnings({ "rawtypes", "unchecked" })
        public Set keySet() {
            return keys;
        }

        public Set<String> stringPropertyNames() {
            return keys;
        }
    }
}
