package com.globallogic.futbol.core.operations;

import android.content.Intent;
import android.os.Bundle;

import com.globallogic.futbol.core.LocalBroadcastManager;
import com.globallogic.futbol.core.OperationApp;
import com.globallogic.futbol.core.OperationResult;
import com.globallogic.futbol.core.broadcasts.OperationBroadcastReceiverHelper;
import com.globallogic.futbol.core.interfaces.IOperation;
import com.globallogic.futbol.core.strategies.OperationStrategy;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A concrete implementation of {@link IOperation}
 *
 * @author facundo.mengoni
 * @see IOperation
 * @since 0.1.0
 */
public abstract class Operation implements IOperation, Serializable {
    //region Constants
    private static final String SAVE_INSTANCE_STRATEGIES_IN_EXECUTION = "SAVE_INSTANCE_STRATEGIES_IN_EXECUTION";
    //endregion

    //region Variables
    public static boolean sAllMultiProcess = false;
    protected boolean mMultiProcess = false;
    public Long mConnectionDelay = 0L;
    //region Logger
    public Logger mLogger;
    private String id;
    //endregion
    private ArrayList<OperationStrategy> mStrategiesInExecution = new ArrayList<>();

    {
        mLogger = Logger.getLogger(getClass().getSimpleName());
        mLogger.setLevel(Level.OFF);
    }
    //endregion

    //region Constructors implementation

    /**
     * Create a new instance with an empty id.
     * <p>
     * The id is used to register the receiver for a specific operation.
     * If you register two operation with different ids then the receiver
     * of one operation never listen the other operation.
     *
     * @see #Operation(String)
     * @see #getId()
     * @see #setId(String)
     */
    protected Operation() {
        this("");
    }

    /**
     * Create a new instance with the specified id.
     * <p>
     * The id is used to register the receiver for a specific operation.
     * If you register two operation with different ids then the receiver
     * of one operation never listen the other operation.
     *
     * @see #Operation()
     * @see #getId()
     * @see #setId(String)
     */
    protected Operation(String id) {
        this.id = id;
    }
    //endregion

    //region Getters & Setters implementation

    /**
     * @return The id of the operation
     * @see Operation(String)
     */
    public String getId() {
        return id;
    }

    /**
     * Defines an id for the operation
     *
     * @see Operation(String)
     */
    public void setId(String id) {
        if (id == null)
            id = "";
        this.id = id;
    }

    /**
     * Defines a time delay for the operation.
     *
     * @see #setConnectionDelay(long)
     * @see OperationStrategy#simulateWaiting()
     */
    protected void setConnectionDelay(int duration) {
        this.mConnectionDelay = (long) duration;
    }

    /**
     * Defines a time delay for the operation
     *
     * @see #setConnectionDelay(int)
     * @see OperationStrategy#simulateWaiting()
     */
    protected void setConnectionDelay(long duration) {
        this.mConnectionDelay = duration;
    }
    //endregion

    //region IOperation implementation

    /**
     * {@inheritDoc}<br>
     * The {@code arg} received will be passed to {@code getStrategies} to use there.
     *
     * @see #doRequest(Object...)
     * @see #getStrategies(Object...)
     */
    @Override
    public boolean performOperation(Object... arg) {
        mLogger.info("Performing operation");
        return doRequest(arg);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void sendBroadcastForStart() {
        mLogger.info("Sending broadcast for start");
        Intent intent = new Intent();
        intent.putExtra(OperationResult.EXTRA_OPERATION_RESULT, OperationResult.START.name);

        String actionWithId = OperationBroadcastReceiverHelper.getActionForStart(this);
        intent.setAction(actionWithId);
        sendBroadcast(intent);

        String actionWithOutID = OperationBroadcastReceiverHelper.getActionForStart(getClass());
        if (!actionWithId.equals(actionWithOutID)) {
            intent.setAction(actionWithOutID);
            sendBroadcast(intent);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void sendBroadcastForFinish() {
        mLogger.info("Sending broadcast for finished");
        Intent intent = new Intent();
        intent.putExtra(OperationResult.EXTRA_OPERATION_RESULT, OperationResult.FINISH.name);

        String actionWithId = OperationBroadcastReceiverHelper.getActionForFinish(this);
        intent.setAction(actionWithId);
        sendBroadcast(intent);

        String actionWithOutID = OperationBroadcastReceiverHelper.getActionForFinish(getClass());
        if (!actionWithId.equals(actionWithOutID)) {
            intent.setAction(actionWithOutID);
            sendBroadcast(intent);
        }
    }
    //endregion

    //region Operation implementation
    /**
     * Notify that the operation starts and execute all the strategies defined.
     *
     * @see #getStrategies(Object...)
     * @see #sendBroadcastForStart()
     * @see OperationStrategy
     */
    private boolean doRequest(Object... arg) {
        mLogger.info("Doing request");
        ArrayList<OperationStrategy> strategies = getStrategies(arg);
        mStrategiesInExecution.addAll(strategies);
        Boolean someRequestExecuted = strategies.size() > 0;
        if (someRequestExecuted) {
            if (mStrategiesInExecution.size() == strategies.size()) {
                sendBroadcastForStart();
            }
            for (OperationStrategy operationStrategy : strategies) {
                operationStrategy.setConnectionDelay(mConnectionDelay);
                operationStrategy.execute();
            }
        }
        return someRequestExecuted;
    }

    /**
     * Returns the list of strategies to do in this operation.
     *
     * @param arg The arguments specified in performOperation()
     * @see #performOperation(Object...)
     */
    protected abstract ArrayList<OperationStrategy> getStrategies(Object... arg);

    public void onStrategyFinish(OperationStrategy anOperationStrategy) {
        mStrategiesInExecution.remove(anOperationStrategy);
        if (mStrategiesInExecution.size() == 0)
            sendBroadcastForFinish();
    }

    public Boolean isWorking() {
        return mStrategiesInExecution.size() > 0;
    }

    public void cancel() {
        Iterator<OperationStrategy> iterator = mStrategiesInExecution.iterator();
        while (iterator.hasNext()) {
            OperationStrategy operationStrategy = iterator.next();
            operationStrategy.cancel();
            iterator.remove();
        }
    }

    public void sendBroadcast(Intent intent) {
        if (sAllMultiProcess || mMultiProcess) {
            OperationApp.getInstance().sendBroadcast(intent);
        } else {
            LocalBroadcastManager.getInstance(OperationApp.getInstance()).sendBroadcast(intent);
        }
    }
    //endregion

    //region Object implementation
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Operation operation = (Operation) o;

        return !(id != null ? !id.equals(operation.id) : operation.id != null);

    }

    @Override
    public int hashCode() {
        return id != null ? id.hashCode() : 0;
    }
    //endregion

    //region Lifecycle implementation
    public void onCreate(Bundle savedInstanceState) {
        if (savedInstanceState != null)
            onRestoreSavedInstance(savedInstanceState);
    }

    public void onSaveInstanceState(Bundle outState) {
        outState.putSerializable(SAVE_INSTANCE_STRATEGIES_IN_EXECUTION, mStrategiesInExecution);
    }

    public void onRestoreSavedInstance(Bundle savedInstanceState) {
        //noinspection unchecked
        mStrategiesInExecution = (ArrayList<OperationStrategy>) savedInstanceState.getSerializable(SAVE_INSTANCE_STRATEGIES_IN_EXECUTION);
    }
    //endregion
}
@@ -5566,47 +5566,8 @@
  0;%0A
-        if (someRequestExecuted) %7B%0A    
     
@@ -5632,36 +5632,32 @@
 ) %7B%0A            
-    
 sendBroadcastFor
@@ -5665,37 +5665,68 @@
 tart();%0A        
-    %7D
+%7D%0A        if (someRequestExecuted) %7B
 %0A            for
@@ -5902,32 +5902,87 @@
 ;%0A            %7D%0A
+        %7D else %7B%0A            sendBroadcastForFinish();%0A
         %7D%0A      

Fix when don't have strategies