/**
 * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 *
 * The contents of this file are subject to the terms of the Liferay Enterprise
 * Subscription License ("License"). You may not use this file except in
 * compliance with the License. You can obtain a copy of the License by
 * contacting Liferay, Inc. See the License for the specific language governing
 * permissions and limitations under the License, including but not limited to
 * distribution rights of the Software.
 *
 *
 *
 */

package com.liferay.portal.cache.ehcache.internal.configurator;

import com.liferay.petra.string.StringBundler;
import com.liferay.petra.string.StringPool;
import com.liferay.portal.cache.PortalCacheReplicator;
import com.liferay.portal.cache.configuration.PortalCacheConfiguration;
import com.liferay.portal.cache.configuration.PortalCacheManagerConfiguration;
import com.liferay.portal.kernel.util.GetterUtil;
import com.liferay.portal.kernel.util.ObjectValuePair;
import com.liferay.portal.kernel.util.Props;
import com.liferay.portal.kernel.util.PropsKeys;
import com.liferay.portal.kernel.util.Validator;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.Configuration;

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;

/**
 * @author Dante Wang
 */
@Component(
	immediate = true,
	service = MultiVMEhcachePortalCacheManagerConfigurator.class
)
public class MultiVMEhcachePortalCacheManagerConfigurator
	extends BaseEhcachePortalCacheManagerConfigurator {

	@Activate
	protected void activate() {
		clusterEnabled = GetterUtil.getBoolean(
			props.get(PropsKeys.CLUSTER_LINK_ENABLED));
		_defaultReplicatorPropertiesString = getPortalPropertiesString(
			PropsKeys.EHCACHE_REPLICATOR_PROPERTIES_DEFAULT);
		_replicatorProperties = props.getProperties(
			PropsKeys.EHCACHE_REPLICATOR_PROPERTIES + StringPool.PERIOD, true);
	}

	protected String getPortalPropertiesString(String portalPropertyKey) {
		String[] array = props.getArray(portalPropertyKey);

		if (array.length == 0) {
			return null;
		}

		if (array.length == 1) {
			return array[0];
		}

		StringBundler sb = new StringBundler(array.length * 2);

		for (String value : array) {
			sb.append(value);
			sb.append(StringPool.COMMA);
		}

		sb.setIndex(sb.index() - 1);

		return sb.toString();
	}

	@Override
	protected boolean isRequireSerialization(
		CacheConfiguration cacheConfiguration) {

		if (clusterEnabled) {
			return true;
		}

		return super.isRequireSerialization(cacheConfiguration);
	}

	@Override
	protected void manageConfiguration(
		Configuration configuration,
		PortalCacheManagerConfiguration portalCacheManagerConfiguration) {

		if (!clusterEnabled) {
			return;
		}

		PortalCacheConfiguration defaultPortalCacheConfiguration =
			portalCacheManagerConfiguration.
				getDefaultPortalCacheConfiguration();

		Map<String, ObjectValuePair<Properties, Properties>>
			mergedPropertiesMap = _getMergedPropertiesMap();

		for (Map.Entry<String, ObjectValuePair<Properties, Properties>> entry :
				mergedPropertiesMap.entrySet()) {

			String portalCacheName = entry.getKey();

			PortalCacheConfiguration portalCacheConfiguration =
				portalCacheManagerConfiguration.getPortalCacheConfiguration(
					portalCacheName);

			if (portalCacheConfiguration == null) {
				portalCacheConfiguration =
					defaultPortalCacheConfiguration.newPortalCacheConfiguration(
						portalCacheName);

				portalCacheManagerConfiguration.putPortalCacheConfiguration(
					portalCacheName, portalCacheConfiguration);
			}

			ObjectValuePair<Properties, Properties> propertiesPair =
				entry.getValue();

			if (propertiesPair.getValue() != null) {
				Set<Properties> portalCacheListenerPropertiesSet =
					portalCacheConfiguration.
						getPortalCacheListenerPropertiesSet();

				Iterator<Properties> itr =
					portalCacheListenerPropertiesSet.iterator();

				while (itr.hasNext()) {
					Properties properties = itr.next();

					if ((Boolean)properties.get(
							PortalCacheReplicator.REPLICATOR)) {

						itr.remove();
					}
				}

				portalCacheListenerPropertiesSet.add(propertiesPair.getValue());
			}
		}
	}

	@Override
	protected PortalCacheConfiguration parseCacheListenerConfigurations(
		CacheConfiguration cacheConfiguration, ClassLoader classLoader,
		boolean usingDefault) {

		PortalCacheConfiguration portalCacheConfiguration =
			super.parseCacheListenerConfigurations(
				cacheConfiguration, classLoader, usingDefault);

		if (!clusterEnabled) {
			return portalCacheConfiguration;
		}

		String cacheName = cacheConfiguration.getName();

		String replicatorPropertiesString =
			(String)_replicatorProperties.remove(cacheName);

		if (Validator.isNull(replicatorPropertiesString)) {
			replicatorPropertiesString = _defaultReplicatorPropertiesString;
		}

		Properties replicatorProperties = parseProperties(
			replicatorPropertiesString, StringPool.COMMA);

		replicatorProperties.put(PortalCacheReplicator.REPLICATOR, true);

		Set<Properties> portalCacheListenerPropertiesSet =
			portalCacheConfiguration.getPortalCacheListenerPropertiesSet();

		portalCacheListenerPropertiesSet.add(replicatorProperties);

		return portalCacheConfiguration;
	}

	@Reference(unbind = "-")
	protected void setProps(Props props) {
		this.props = props;
	}

	protected boolean clusterEnabled;

	private Map<String, ObjectValuePair<Properties, Properties>>
		_getMergedPropertiesMap() {

		Map<String, ObjectValuePair<Properties, Properties>>
			mergedPropertiesMap = new HashMap<>();

		for (String portalCacheName :
				_replicatorProperties.stringPropertyNames()) {

			Properties replicatorProperties = parseProperties(
				_replicatorProperties.getProperty(portalCacheName),
				StringPool.COMMA);

			replicatorProperties.put(PortalCacheReplicator.REPLICATOR, true);

			ObjectValuePair<Properties, Properties> objectValuePair =
				mergedPropertiesMap.get(portalCacheName);

			if (objectValuePair == null) {
				mergedPropertiesMap.put(
					portalCacheName,
					new ObjectValuePair(null, replicatorProperties));
			}
			else {
				objectValuePair.setValue(replicatorProperties);
			}
		}

		return mergedPropertiesMap;
	}

	private String _defaultReplicatorPropertiesString;
	private Properties _replicatorProperties;

}