package ru.bitel.bgbilling.modules.inet.dyn.device.misc;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import ru.bitel.bgbilling.apps.inet.access.sa.ServiceActivator;
import ru.bitel.bgbilling.apps.inet.access.sa.ServiceActivatorEvent;
import ru.bitel.bgbilling.kernel.dynamic.server.DynamicClassManager;
import ru.bitel.bgbilling.modules.inet.common.bean.InetDevice;
import ru.bitel.bgbilling.modules.inet.common.bean.InetDeviceType;
import ru.bitel.bgbilling.modules.inet.common.bean.enums.InetServState;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.ParameterMap;
import ru.bitel.common.Preferences;
import ru.bitel.common.Utils;

/**
 * ServiceActivator состоящий сразу из нескольких.<br>
 * Классы указываются в конфигурации типа устройства или устройства.<br>
 * sa.composite.orderMode= указывает порядок выполнения: 1 - всегда в указанном, кроме disconnect/destroy, 2 (по умолчанию) - при serviceCancel/serviceModify(newState==DISABLE) и т.п. - в обратном.
 * <pre>
 * sa.composite.orderMode=1
 * sa.composite.1.class=
 * sa.composite.2.class=
 * </pre>
 * 
 * @author amir
 */
public class CompositeServiceActivator
	implements ServiceActivator
{
	private static final Logger logger = LogManager.getLogger();

	private ServiceActivator[] serviceActivators;
	private ServiceActivator[] reverseOrderServiceActivators;
	private ServiceActivator[] disableOrderServiceActivators;

	protected Object result( Object newResult, Object result )
	{
		if ( result == null )
		{
			return newResult;
		}
		else
		{
			return result;
		}
	}

	@Override
	public Object init( Setup setup, int moduleId, InetDevice device, InetDeviceType deviceType, ParameterMap config )
		throws Exception
	{
		logger.debug( "init" );

		final DynamicClassManager dynamicClassManager = DynamicClassManager.getInstance();

		ParameterMap deviceConfig = new Preferences( Utils.maskNull( deviceType.getConfig() ), "\n" );
		deviceConfig = deviceConfig.inherit( new Preferences( Utils.maskNull( device.getInvConfig() ), "\n" ) );
		deviceConfig = deviceConfig.inherit( new Preferences( Utils.maskNull( device.getConfig() ), "\n" ) );

		List<ServiceActivator> list = new ArrayList<>();

		for ( Map.Entry<Integer, ParameterMap> e : deviceConfig.subIndexed( "sa.composite." ).entrySet() )
		{
			String clazz = e.getValue().get( "class" );
			if ( e.getKey() <= 0 || clazz == null )
			{
				continue;
			}

			try
			{
				ServiceActivator sa = dynamicClassManager.newInstance( ServiceActivator.class, clazz );
				list.add( sa );
			}
			catch( Throwable t )
			{
				logger.error( "Error loading class: " + clazz + ". " + t.getMessage(), t );
			}
		}

		serviceActivators = list.toArray( new ServiceActivator[list.size()] );

		logger.info( "Composite ServiceActivators count: " + serviceActivators.length );

		int orderMode = deviceConfig.getInt( "sa.composite.orderMode", 1 );

		logger.info( "Composite orderMode: " + orderMode );

		if ( orderMode > 0 )
		{
			Collections.reverse( list );

			reverseOrderServiceActivators = list.toArray( new ServiceActivator[list.size()] );

			if ( orderMode > 1 )
			{
				disableOrderServiceActivators = reverseOrderServiceActivators;
			}
			else
			{
				disableOrderServiceActivators = serviceActivators;
			}
		}
		else
		{
			reverseOrderServiceActivators = serviceActivators;
			disableOrderServiceActivators = serviceActivators;
		}

		Object result = null;

		for ( ServiceActivator sa : serviceActivators )
		{
			result = result( sa.init( setup, moduleId, device, deviceType, config ), result );
		}

		return result;
	}

	@Override
	public Object destroy()
		throws Exception
	{
		logger.debug( "destroy" );

		Object result = null;

		for ( ServiceActivator sa : reverseOrderServiceActivators )
		{
			result = result( sa.destroy(), result );
		}

		return result;
	}

	@Override
	public Object connect()
		throws Exception
	{
		Object result = null;

		for ( ServiceActivator sa : serviceActivators )
		{
			result = result( sa.connect(), result );
		}

		return result;
	}

	@Override
	public Object disconnect()
		throws Exception
	{
		Object result = null;

		for ( ServiceActivator sa : reverseOrderServiceActivators )
		{
			result = result( sa.disconnect(), result );
		}

		return result;
	}

	@Override
	public Object serviceCreate( ServiceActivatorEvent e )
		throws Exception
	{
		Object result = null;

		for ( ServiceActivator sa : serviceActivators )
		{
			result = result( sa.serviceCreate( e ), result );
		}

		return result;
	}

	@Override
	public Object serviceModify( ServiceActivatorEvent e )
		throws Exception
	{
		Object result = null;

		if ( e.getNewState() == InetServState.STATE_ENABLE.getCode() )
		{
			for ( ServiceActivator sa : serviceActivators )
			{
				result = result( sa.serviceModify( e ), result );
			}
		}
		else
		{
			for ( ServiceActivator sa : disableOrderServiceActivators )
			{
				result = result( sa.serviceModify( e ), result );
			}
		}

		return result;
	}

	@Override
	public Object serviceCancel( ServiceActivatorEvent e )
		throws Exception
	{
		Object result = null;

		for ( ServiceActivator sa : disableOrderServiceActivators )
		{
			result = result( sa.serviceCancel( e ), result );
		}

		return result;
	}

	@Override
	public Object connectionModify( ServiceActivatorEvent e )
		throws Exception
	{
		Object result = null;

		if ( e.getNewState() == InetServState.STATE_ENABLE.getCode() )
		{
			for ( ServiceActivator sa : serviceActivators )
			{
				result = result( sa.connectionModify( e ), result );
			}
		}
		else
		{
			for ( ServiceActivator sa : disableOrderServiceActivators )
			{
				result = result( sa.connectionModify( e ), result );
			}
		}

		return result;
	}

	@Override
	public Object connectionClose( ServiceActivatorEvent e )
		throws Exception
	{
		Object result = null;

		for ( ServiceActivator sa : disableOrderServiceActivators )
		{
			result = result( sa.connectionClose( e ), result );
		}

		return result;
	}

	@Override
	public Object onAccountingStart( ServiceActivatorEvent e )
		throws Exception
	{
		Object result = null;

		for ( ServiceActivator sa : serviceActivators )
		{
			result = result( sa.onAccountingStart( e ), result );
		}

		return result;
	}

	@Override
	public Object onAccountingStop( ServiceActivatorEvent e )
		throws Exception
	{
		Object result = null;

		for ( ServiceActivator sa : disableOrderServiceActivators )
		{
			result = result( sa.onAccountingStop( e ), result );
		}

		return result;
	}
}
