/**
 * Project: dubbo.registry-1.1.0-SNAPSHOT
 * 
 * File Created at 2010-4-15
 * $Id: ProviderServiceImpl.java 185206 2012-07-09 03:06:37Z tony.chenl $
 * 
 * Copyright 2008 Alibaba.com Croporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * Alibaba Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Alibaba.com.
 */
package com.alibaba.dubbo.governance.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;

import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.governance.service.OverrideService;
import com.alibaba.dubbo.governance.service.ProviderService;
import com.alibaba.dubbo.governance.sync.util.Pair;
import com.alibaba.dubbo.governance.sync.util.SyncUtils;
import com.alibaba.dubbo.registry.common.domain.Override;
import com.alibaba.dubbo.registry.common.domain.Provider;
import com.alibaba.dubbo.registry.common.route.ParseUtils;

/**
 * IbatisProviderService
 * 
 * @author tony.chenl
 */
public class ProviderServiceImpl extends AbstractService implements ProviderService {
	
	@Autowired
	OverrideService overrideService;

    public void create(Provider provider) {
        URL url = provider.toUrl();
        registryService.register(url);
    }

    public void enableProvider(Long id) {
        if(id == null) {
            throw new IllegalStateException("no provider id");
        }
        
        Provider oldProvider = findProvider(id);
       
        if(oldProvider == null) {
            throw new IllegalStateException("Provider was changed!");
        } 
        if (oldProvider.isDynamic()) {
	        //保证disable的override唯一
	        if(!oldProvider.isEnabled()){
	        	Override override = new Override();
	        	override.setAddress(oldProvider.getAddress());
	        	override.setService(oldProvider.getService());
	        	override.setEnabled(true);
	        	override.setParams(Constants.DISABLED_KEY+"=false");
	        	overrideService.saveOverride(override);
	        	return;
	        }
	        List<Override> oList = overrideService.findByServiceAndAddress(oldProvider.getService(), oldProvider.getAddress());
	       
	        for(Override o : oList){
	        	Map<String, String> params = StringUtils.parseQueryString(o.getParams());
	        	if(params.containsKey(Constants.DISABLED_KEY)){
	        		if(params.get(Constants.DISABLED_KEY) .equals("true")){
	        			overrideService.deleteOverride(o.getId());
	        		}
	        	}
	        }
        } else {
        	oldProvider.setEnabled(true);
        	updateProvider(oldProvider);
        }
    }

    public void disableProvider(Long id) {
        if(id == null) {
            throw new IllegalStateException("no provider id");
        }
        
        Provider oldProvider = findProvider(id);
        if(oldProvider == null) {
            throw new IllegalStateException("Provider was changed!");
        }
        
        if (oldProvider.isDynamic()) {
	        //保证disable的override唯一
	        if(oldProvider.isEnabled()){
	        	Override override = new Override();
	        	override.setAddress(oldProvider.getAddress());
	        	override.setService(oldProvider.getService());
	        	override.setEnabled(true);
	        	override.setParams(Constants.DISABLED_KEY+"=true");
	        	overrideService.saveOverride(override);
	        	return;
	        }
	        List<Override> oList = overrideService.findByServiceAndAddress(oldProvider.getService(), oldProvider.getAddress());
	       
	        for(Override o : oList){
	        	Map<String, String> params = StringUtils.parseQueryString(o.getParams());
	        	if(params.containsKey(Constants.DISABLED_KEY)){
	        		if(params.get(Constants.DISABLED_KEY) .equals("false")){
	        			overrideService.deleteOverride(o.getId());
	        		}
	        	}
	        }
        } else {
        	oldProvider.setEnabled(false);
        	updateProvider(oldProvider);
        }
        
    }
    
    public void doublingProvider(Long id) {
    	setWeight(id, 2F);
    }
    
    public void halvingProvider(Long id) {
    	setWeight(id, 0.5F);
    }

    public void setWeight(Long id, float factor) {
        if(id == null) {
            throw new IllegalStateException("no provider id");
        }
        Provider oldProvider = findProvider(id);
        if(oldProvider == null) {
            throw new IllegalStateException("Provider was changed!");
        }
        Map<String, String> map = StringUtils.parseQueryString(oldProvider.getParameters());
    	String weight = map.get(Constants.WEIGHT_KEY);
        if (oldProvider.isDynamic()) {
	        //保证disable的override唯一
	        List<Override> overrides = overrideService.findByServiceAndAddress(oldProvider.getService(), oldProvider.getAddress());
	        if (overrides == null || overrides.size() == 0) {
	        	int value = getWeight(weight, factor);
	        	if (value != Constants.DEFAULT_WEIGHT) {
		        	Override override = new Override();
		        	override.setAddress(oldProvider.getAddress());
		        	override.setService(oldProvider.getService());
		        	override.setEnabled(true);
		        	override.setParams(Constants.WEIGHT_KEY + "=" + String.valueOf(value));
		        	overrideService.saveOverride(override);
	        	}
	        } else {
		        for(Override override : overrides){
		        	Map<String, String> params = StringUtils.parseQueryString(override.getParams());
		        	String overrideWeight = params.get(Constants.WEIGHT_KEY);
		        	if (overrideWeight == null || overrideWeight.length() == 0) {
		        		overrideWeight = weight;
		        	}
		        	int value = getWeight(overrideWeight, factor);
		        	if (value == getWeight(weight, 1)) {
		        		params.remove(Constants.WEIGHT_KEY);
		        	} else {
		        		params.put(Constants.WEIGHT_KEY, String.valueOf(value));
		        	}
		        	if (params.size() > 0) {
		        		override.setParams(StringUtils.toQueryString(params));
		        		overrideService.updateOverride(override);
		        	} else {
		        		overrideService.deleteOverride(override.getId());
		        	}
		        }
	        }
        } else {
        	int value = getWeight(weight, factor);
        	if (value == Constants.DEFAULT_WEIGHT) {
        		map.remove(Constants.WEIGHT_KEY);
        	} else {
        		map.put(Constants.WEIGHT_KEY, String.valueOf(value));
        	}
        	oldProvider.setParameters(StringUtils.toQueryString(map));
        	updateProvider(oldProvider);
        }
    }
    
    private int getWeight(String value, float factor) {
    	int weight = 100;
    	if (value != null && value.length() > 0) {
    		weight = Integer.parseInt(value);
    	}
    	weight = (int) (weight * factor);
    	if (weight < 1) weight = 1;
    	if (weight == 2) weight = 3;
    	if (weight == 24) weight = 25;
    	return weight;
    }

    public void deleteStaticProvider(Long id) {
        URL oldProvider = findProviderUrl(id);
        if(oldProvider == null) {
            throw new IllegalStateException("Provider was changed!");
        }
        registryService.unregister(oldProvider);
    }

    public void updateProvider(Provider provider) {
        Long id = provider.getId();
        if(id == null) {
            throw new IllegalStateException("no provider id");
        }
        
        URL oldProvider = findProviderUrl(id);
        if(oldProvider == null) {
            throw new IllegalStateException("Provider was changed!");
        }
        URL newProvider = provider.toUrl();
        
        registryService.unregister(oldProvider);
        registryService.register(newProvider);
    }

    public Provider findProvider(Long id) {
        return SyncUtils.url2Provider(findProviderUrlPair(id));
    }
    
    public Pair<Long, URL> findProviderUrlPair(Long id) {
        return SyncUtils.filterFromCategory(getRegistryCache(), Constants.PROVIDERS_CATEGORY, id);
    }

    public List<String> findServices() {
        List<String> ret = new ArrayList<String>();
        ConcurrentMap<String, Map<Long, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
        if(providerUrls != null) ret.addAll(providerUrls.keySet());
        return ret;
    }

    public List<String> findAddresses() {
        List<String> ret = new ArrayList<String>();
        
        ConcurrentMap<String, Map<Long, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
        if(null == providerUrls) return ret;
        
        for(Map.Entry<String, Map<Long, URL>> e1 : providerUrls.entrySet()) {
            Map<Long, URL> value = e1.getValue();
            for(Map.Entry<Long, URL> e2 : value.entrySet()) {
                URL u = e2.getValue();
                String app = u.getAddress();
                if(app != null) ret.add(app);
            }
        }
        
        return ret;
    }

    public List<String> findAddressesByApplication(String application) {
        List<String> ret = new ArrayList<String>();
        ConcurrentMap<String, Map<Long, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
        for(Map.Entry<String, Map<Long, URL>> e1 : providerUrls.entrySet()) {
            Map<Long, URL> value = e1.getValue();
            for(Map.Entry<Long, URL> e2 : value.entrySet()) {
                URL u = e2.getValue();
                if(application.equals(u.getParameter(Constants.APPLICATION_KEY))) {
                    String addr = u.getAddress();
                    if(addr != null) ret.add(addr);
                }
            }
        }
        
        return ret;
    }

    public List<String> findAddressesByService(String service) {
        List<String> ret = new ArrayList<String>();
        ConcurrentMap<String, Map<Long, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
        if(null == providerUrls) return ret;
        
        for(Map.Entry<Long, URL> e2 : providerUrls.get(service).entrySet()) {
            URL u = e2.getValue();
            String app = u.getAddress();
            if(app != null) ret.add(app);
        }
        
        return ret;
    }

    public List<String> findApplicationsByServiceName(String service) {
        List<String> ret = new ArrayList<String>();
        ConcurrentMap<String, Map<Long, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
        if(null == providerUrls) return ret;
        
        Map<Long, URL> value = providerUrls.get(service);
        if(value == null){
        	return ret;
        }
        for(Map.Entry<Long, URL> e2 : value.entrySet()) {
            URL u = e2.getValue();
            String app = u.getParameter(Constants.APPLICATION_KEY);
            if(app != null) ret.add(app);
        }
        
        return ret;
    }

    public List<Provider> findByService(String serviceName) {
        return SyncUtils.url2ProviderList(findProviderUrlByService(serviceName));
    }
    
    private Map<Long, URL> findProviderUrlByService(String service) {
        Map<String, String> filter = new HashMap<String, String>();
        filter.put(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY);
        filter.put(SyncUtils.SERVICE_FILTER_KEY, service);
        
        return SyncUtils.filterFromCategory(getRegistryCache(), filter);
    }

    public List<Provider> findAll() {
        return SyncUtils.url2ProviderList(findAllProviderUrl());
    }
    
    private Map<Long, URL> findAllProviderUrl() {
        Map<String, String> filter = new HashMap<String, String>();
        filter.put(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY);
        return SyncUtils.filterFromCategory(getRegistryCache(), filter);
    }
    
    public List<Provider> findByAddress(String providerAddress) {
        return SyncUtils.url2ProviderList(findProviderUrlByAddress(providerAddress));
    }
    
    public Map<Long, URL> findProviderUrlByAddress(String address) {
        Map<String, String> filter = new HashMap<String, String>();
        filter.put(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY);
        filter.put(SyncUtils.ADDRESS_FILTER_KEY, address);
        
        return SyncUtils.filterFromCategory(getRegistryCache(), filter);
    }

    public List<String> findServicesByAddress(String address) {
        List<String> ret = new ArrayList<String>();
        
        ConcurrentMap<String, Map<Long, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
        if(providerUrls == null || address == null || address.length() == 0) return ret;
        
        for(Map.Entry<String, Map<Long, URL>> e1 : providerUrls.entrySet()) {
            Map<Long, URL> value = e1.getValue();
            for(Map.Entry<Long, URL> e2 : value.entrySet()) {
                URL u = e2.getValue();
                if(address.equals(u.getAddress())) {
                    ret.add(e1.getKey());
                    break;
                }
            }
        }
        
        return ret;
    }

    public List<String> findApplications() {
        List<String> ret = new ArrayList<String>();
        ConcurrentMap<String, Map<Long, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
        if(providerUrls == null) return ret;
        
        for(Map.Entry<String, Map<Long, URL>> e1 : providerUrls.entrySet()) {
            Map<Long, URL> value = e1.getValue();
            for(Map.Entry<Long, URL> e2 : value.entrySet()) {
                URL u = e2.getValue();
                String app = u.getParameter(Constants.APPLICATION_KEY);
                if(app != null) ret.add(app);
            }
        }
        
        return ret;
    }

    public List<Provider> findByApplication(String application) {
        return SyncUtils.url2ProviderList(findProviderUrlByApplication(application));
    }
    
    private Map<Long, URL> findProviderUrlByApplication(String application) {
        Map<String, String> filter = new HashMap<String, String>();
        filter.put(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY);
        filter.put(Constants.APPLICATION_KEY, application);
        return SyncUtils.filterFromCategory(getRegistryCache(), filter);
    }

    public List<String> findServicesByApplication(String application) {
        List<String> ret = new ArrayList<String>();
        
        ConcurrentMap<String, Map<Long, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
        if(providerUrls == null || application == null || application.length() == 0) return ret;
        
        for(Map.Entry<String, Map<Long, URL>> e1 : providerUrls.entrySet()) {
            Map<Long, URL> value = e1.getValue();
            for(Map.Entry<Long, URL> e2 : value.entrySet()) {
                URL u = e2.getValue();
                if(application.equals(u.getParameter(Constants.APPLICATION_KEY))) {
                    ret.add(e1.getKey());
                    break;
                }
            }
        }
        
        return ret;
    }

    public List<String> findMethodsByService(String service) {
        List<String> ret = new ArrayList<String>();

        ConcurrentMap<String, Map<Long, URL>> providerUrls = getRegistryCache().get(Constants.PROVIDERS_CATEGORY);
        if(providerUrls == null || service == null || service.length() == 0) return ret;
        
        Map<Long, URL> providers = providerUrls.get(service);
        if(null == providers || providers.isEmpty()) return ret;
        
        Entry<Long, URL> p = providers.entrySet().iterator().next();
        String value = p.getValue().getParameter("methods");
        if (value == null || value.length() == 0) {
            return ret;
        }
        String[] methods = value.split(ParseUtils.METHOD_SPLIT);
        if (methods == null || methods.length == 0) {
            return ret;
        }
        
        for(String m : methods) {
            ret.add(m);
        }
        return ret;
    }

    private URL findProviderUrl(Long id) {
        return findProvider(id).toUrl();
    }

    public Provider findByServiceAndAddress(String service, String address) {
        return SyncUtils.url2Provider(findProviderUrl(service, address));
    }
    
	private Pair<Long, URL> findProviderUrl(String service, String address) {
        Map<String, String> filter = new HashMap<String, String>();
        filter.put(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY);
        filter.put(SyncUtils.ADDRESS_FILTER_KEY, address);
        
        Map<Long, URL> ret = SyncUtils.filterFromCategory(getRegistryCache(), filter);
        if(ret.isEmpty()) {
            return null;
        }
        else { 
            Long key = ret.entrySet().iterator().next().getKey();
            return new Pair<Long, URL>(key, ret.get(key));
        }
    }
    
}