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

import java.util.List;
import java.util.Set;

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

import ru.bitel.bgbilling.apps.inet.access.sa.ServiceActivatorAdapter;
import ru.bitel.bgbilling.apps.inet.access.sa.ServiceActivatorEvent;
import ru.bitel.bgbilling.kernel.event.EventProcessor;
import ru.bitel.bgbilling.modules.inet.common.bean.InetConnection;
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.InetServ;
import ru.bitel.bgbilling.modules.inet.common.bean.InetServOption;
import ru.bitel.bgbilling.modules.inet.common.bean.InetSession;
import ru.bitel.bgbilling.modules.inet.common.bean.InetSessionLog;
import ru.bitel.bgbilling.modules.inet.common.bean.enums.InetServState;
import ru.bitel.bgbilling.modules.inet.common.event.sa.InetSaAccountingEvent;
import ru.bitel.bgbilling.modules.inet.common.event.sa.InetSaOptionsModifyEvent;
import ru.bitel.bgbilling.modules.inet.common.event.sa.InetSaServModifyEvent;
import ru.bitel.bgbilling.modules.inet.common.event.sa.InetSaStateModifyEvent;
import ru.bitel.bgbilling.modules.inet.server.bean.InetDeviceMap;
import ru.bitel.bgbilling.server.util.Setup;
import ru.bitel.common.ParameterMap;
import ru.bitel.common.Utils;

public class RecipientListServiceActivator
    extends ServiceActivatorAdapter
{
    private static final Logger logger = LogManager.getLogger();

    private int moduleId;

    private Set<Integer> deviceIds;
    
    /**
     * Нужно ли после смены состояния соединения (вызова connectionModify) сразу менять состояние в базе.
     */
    private boolean needConnectionStateModify;

    private final EventProcessor eventProcessor = EventProcessor.getInstance();

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

        this.deviceIds = Utils.toIntegerSet( config.get( "sa.recipientList.deviceIds", null ) ); // можно перечислить список ID устройств

        final int rootDeviceId = config.getInt( "sa.recipientList.rootDeviceId", 0 ); // можно указать родительское ус-во, дочерним ус-вам которого будут пересылаться задания
        
        final InetDeviceMap deviceMap = InetDeviceMap.getInstance( moduleId );
        
        if ( rootDeviceId > 0 ) // пересылаем потомкам (дочерним устройствам) конкретной папки
        {
            final InetDeviceMap.InetDeviceMapItem rootDevice = deviceMap.get( rootDeviceId );
            if ( rootDevice != null )
            {
                this.deviceIds.addAll( rootDevice.getDescendantIds() );
            }
        }

        final InetDeviceMap.InetDeviceMapItem currentDevice = deviceMap.get( device.getId() );
        if ( currentDevice != null ) // убираем всех предков и потомков данного ус-ва, чтобы не произошло бесконечного цикла
        {
            if ( currentDevice.getDescendantIds() != null && this.deviceIds.removeAll( currentDevice.getDescendantIds() ) )
            {
                logger.error( "Invalid configuration for RecipientListServiceActivator (sa.recipientList...)" );
            }

            if ( currentDevice.getAncestorIds() != null && this.deviceIds.removeAll( currentDevice.getAncestorIds() ) )
            {
                logger.error( "Invalid configuration for RecipientListServiceActivator (sa.recipientList...)" );
            }
        }

        this.deviceIds.remove( device.getId() );
        
        logger.info( "Recipients: " + this.deviceIds );
        
        this.needConnectionStateModify = config.getInt( "sa.recipientList.connection.stateModify", 0 ) > 0;

        return null;
    }

    @Override
    public Object connectionModify( final ServiceActivatorEvent e )
        throws Exception
    {
        logger.info( "connectionModify" );
        
        if ( e.getNewState() == InetServState.STATE_DISABLE.getCode() || e.getOldState() == InetServState.STATE_DISABLE.getCode() )
        {
            if ( needConnectionStateModify )
            {
                e.setConnectionStateModified( true );
            }
        }

        final int contractId = e.getInetServRuntime().contractRuntime.contractId;
        final int inetServId = e.getInetServId();
        final long connectionId = e.getConnection().getId();

        if ( e.getNewState() == InetServState.STATE_DISABLE.getCode() )
        {
            for ( Integer deviceId : this.deviceIds )
            {
                InetSaStateModifyEvent stateModifyEvent = new InetSaStateModifyEvent( moduleId, contractId, 0, deviceId,
                                                                                      inetServId, connectionId,
                                                                                      InetServState.STATE_DISABLE.getCode(), e.getAccessCode(), false, true );
                stateModifyEvent.setOldOptions( e.getOldOptions() );
                stateModifyEvent.setSource( RecipientListServiceActivator.class.getSimpleName() );

                eventProcessor.publish( stateModifyEvent );
            }
        }
        else if ( e.getOldState() != InetServState.STATE_ENABLE.getCode() && e.getNewState() == InetServState.STATE_ENABLE.getCode() )
        {
            for ( Integer deviceId : this.deviceIds )
            {
                InetSaStateModifyEvent stateModifyEvent = new InetSaStateModifyEvent( moduleId, contractId, 0, deviceId,
                                                                                      inetServId, connectionId,
                                                                                      InetServState.STATE_ENABLE.getCode(), e.getAccessCode(), false, true );
                stateModifyEvent.setOldOptions( e.getOldOptions() );
                stateModifyEvent.setSource( RecipientListServiceActivator.class.getSimpleName() );

                eventProcessor.publish( stateModifyEvent );
            }
        }
        else
        {
            for ( Integer deviceId : this.deviceIds )
            {
                InetSaOptionsModifyEvent optionsModifyEvent = new InetSaOptionsModifyEvent( moduleId, contractId, 0, deviceId,
                                                                                            inetServId, connectionId,
                                                                                            e.getNewOptions(), true );
                optionsModifyEvent.setOldOptions( e.getOldOptions() );
                optionsModifyEvent.setSource( RecipientListServiceActivator.class.getSimpleName() );

                eventProcessor.publish( optionsModifyEvent );
            }
        }

        return null;
    }

    @Override
    public Object onAccountingStart( ServiceActivatorEvent e )
        throws Exception
    {
        logger.info( "onAccountingStart" );

        final InetServ inetServ = e.getInetServRuntime().getInetServ();

        InetSessionLog log = new InetSessionLog( e.getConnection(), new InetSession() );

        for ( Integer deviceId : this.deviceIds )
        {
            InetConnection connection = new InetConnection( log );
            connection.setDeviceId( deviceId );
            connection.setDeviceState( e.getConnection().getDeviceState() );
            connection.setDeviceOptions( e.getConnection().getDeviceOptions() );
            
            InetSaAccountingEvent inetSaAccountingEvent = new InetSaAccountingEvent( moduleId, inetServ.getContractId(), connection, InetSaAccountingEvent.TYPE_START );
            inetSaAccountingEvent.setSource( RecipientListServiceActivator.class.getSimpleName() );

            eventProcessor.publish( inetSaAccountingEvent );
        }

        return null;
    }

    @Override
    public Object onAccountingStop( ServiceActivatorEvent e )
        throws Exception
    {
        logger.info( "onAccountingStop" );

        final InetServ inetServ = e.getInetServRuntime().getInetServ();

        InetSessionLog log = new InetSessionLog( e.getConnection(), new InetSession() );

        for ( Integer deviceId : this.deviceIds )
        {
            InetConnection connection = new InetConnection( log );
            connection.setDeviceId( deviceId );
            connection.setDeviceState( e.getConnection().getDeviceState() );
            connection.setDeviceOptions( e.getConnection().getDeviceOptions() );

            InetSaAccountingEvent inetSaAccountingEvent = new InetSaAccountingEvent( moduleId, inetServ.getContractId(), connection, InetSaAccountingEvent.TYPE_STOP );
            inetSaAccountingEvent.setSource( RecipientListServiceActivator.class.getSimpleName() );
            
            eventProcessor.publish( inetSaAccountingEvent );
        }

        return null;
    }

    @Override
    public Object connectionClose( ServiceActivatorEvent e )
        throws Exception
    {
        logger.info( "connectionModify" );

        return this.connectionModify( e );
    }

    @Override
    public Object serviceModify( ServiceActivatorEvent e )
        throws Exception
    {
        logger.info( "serviceModify" );

        List<InetServOption> inetServOptionList = e.getInetServRuntime().getInetServOptionsList().toList( e.getInetServId() );

        for ( Integer deviceId : this.deviceIds )
        {
            InetSaServModifyEvent inetSaServModifyEvent = new InetSaServModifyEvent( moduleId, deviceId, 0, e.getOldInetServ(), e.getNewInetServ(), (short)e.getNewState(), inetServOptionList, true );
            inetSaServModifyEvent.setSource( RecipientListServiceActivator.class.getSimpleName() );
            
            eventProcessor.publish( inetSaServModifyEvent );
        }

        return null;
    }

    @Override
    public Object serviceCreate( ServiceActivatorEvent e )
        throws Exception
    {
        logger.info( "serviceCreate" );

        return serviceModify( e );
    }

    @Override
    public Object serviceCancel( ServiceActivatorEvent e )
        throws Exception
    {
        logger.info( "serviceCancel" );

        return serviceModify( e );
    }
}
