/*
 * Copyright 2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.gradle.internal.execution.steps;

import com.google.common.collect.ImmutableSortedMap;
import org.gradle.internal.execution.UnitOfWork;
import org.gradle.internal.execution.history.AfterPreviousExecutionState;
import org.gradle.internal.execution.history.ExecutionHistoryStore;
import org.gradle.internal.execution.history.changes.ChangeDetectorVisitor;
import org.gradle.internal.execution.history.changes.OutputFileChanges;
import org.gradle.internal.snapshot.FileSystemSnapshot;

import java.util.Optional;

public class StoreExecutionStateStep<C extends BeforeExecutionContext> implements Step<C, CurrentSnapshotResult> {
    private final Step<? super C, ? extends CurrentSnapshotResult> delegate;

    public StoreExecutionStateStep(
        Step<? super C, ? extends CurrentSnapshotResult> delegate
    ) {
        this.delegate = delegate;
    }

    @Override
    public CurrentSnapshotResult execute(UnitOfWork work, C context) {
        CurrentSnapshotResult result = delegate.execute(work, context);
        UnitOfWork.Identity identity = context.getIdentity();
        context.getHistory()
            .ifPresent(history -> storeState(context, history, identity.getUniqueId(), result));
        return result;
    }

    private void storeState(C context, ExecutionHistoryStore executionHistoryStore, String uniqueId, CurrentSnapshotResult result) {
        ImmutableSortedMap<String, FileSystemSnapshot> outputFilesProducedByWork = result.getOutputFilesProduceByWork();
        context.getBeforeExecutionState().ifPresent(beforeExecutionState -> {
            boolean successful = result.getExecutionResult().isSuccessful();
            // We do not store the history if there was a failure and the outputs did not change, since then the next execution can be incremental.
            // For example the current execution fails because of a compile failure and for the next execution the source file is fixed, so only the one changed source file needs to be compiled.
            if (successful
                || didChangeOutput(context.getAfterPreviousExecutionState(), outputFilesProducedByWork)) {
                executionHistoryStore.store(
                    uniqueId,
                    result.getOriginMetadata(),
                    beforeExecutionState.getImplementation(),
                    beforeExecutionState.getAdditionalImplementations(),
                    beforeExecutionState.getInputProperties(),
                    beforeExecutionState.getInputFileProperties(),
                    outputFilesProducedByWork,
                    successful
                );
            }
        });
    }

    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
    private static boolean didChangeOutput(Optional<AfterPreviousExecutionState> afterPreviousExecutionState, ImmutableSortedMap<String, FileSystemSnapshot> current) {
        // If there is no previous state, then we do have output changes
        if (!afterPreviousExecutionState.isPresent()) {
            return true;
        }

        // If there are different output properties compared to the previous execution, then we do have output changes
        ImmutableSortedMap<String, FileSystemSnapshot> previous = afterPreviousExecutionState.get().getOutputFilesProducedByWork();
        if (!previous.keySet().equals(current.keySet())) {
            return true;
        }

        // Otherwise do deep compare of outputs
        ChangeDetectorVisitor visitor = new ChangeDetectorVisitor();
        OutputFileChanges changes = new OutputFileChanges(previous, current);
        changes.accept(visitor);
        return visitor.hasAnyChanges();
    }
}

@@ -762,150 +762,8 @@
 rk;%0A
-import org.gradle.internal.execution.history.AfterPreviousExecutionState;%0Aimport org.gradle.internal.execution.history.ExecutionHistoryStore;%0A
 impo
@@ -967,36 +967,8 @@
 t;%0A%0A
-import java.util.Optional;%0A%0A
 publ
@@ -1593,171 +1593,84 @@
  -%3E 
-storeState(context, history, identity.getUniqueId(), result));%0A        return result;%0A    %7D%0A%0A    private void storeState(C context, ExecutionHistoryStore e
+context.getBeforeExecutionState()%0A                .ifPresent(beforeE
 xecution
 Hist
@@ -1669,71 +1669,31 @@
 tion
-HistoryStore, String uniqueId, CurrentSnapshotResult result) %7B%0A
+State -%3E %7B%0A            
     
@@ -1821,78 +1821,8 @@
     
-context.getBeforeExecutionState().ifPresent(beforeExecutionState -%3E %7B%0A
     
@@ -1890,24 +1890,31 @@
 ccessful();%0A
+%0A      
             
@@ -1913,16 +1913,18 @@
         
+  
 // We do
@@ -2051,16 +2051,24 @@
 mental.%0A
+        
         
@@ -2270,96 +2270,311 @@
     
-if (successful%0A                %7C%7C didChangeOutput(context.getAfterPreviousExecutionState
+        // If there is no previous state, then we do have output changes%0A                    boolean shouldStore = successful %7C%7C context.getAfterPreviousExecutionState()%0A                        .map(afterPreviousExecutionState -%3E didOutputsChange(afterPreviousExecutionState.getOutputFilesProducedByWork
 (), 
@@ -2600,19 +2600,18 @@
 ByWork))
- %7B
 %0A
+ 
         
@@ -2622,29 +2622,101 @@
     
-executionH
+       .orElse(true);%0A%0A                    if (shouldStore) %7B%0A                        h
 istory
-Store
 .sto
@@ -2743,18 +2743,44 @@
     
-uniqueId,%0A
+        identity.getUniqueId(),%0A    
     
@@ -2795,16 +2795,20 @@
         
+    
 result.g
@@ -2823,24 +2823,32 @@
 Metadata(),%0A
+        
             
@@ -2909,32 +2909,40 @@
                 
+        
 beforeExecutionS
@@ -2974,24 +2974,32 @@
 ntations(),%0A
+        
             
@@ -3061,32 +3061,40 @@
                 
+        
 beforeExecutionS
@@ -3144,16 +3144,24 @@
         
+        
 outputFi
@@ -3199,16 +3199,24 @@
         
+        
 successf
@@ -3234,19 +3234,32 @@
         
-);%0A
+        );%0A     
         
@@ -3266,18 +3266,27 @@
     
-%7D%0A
+   %7D%0A      
         
 %7D);%0A
@@ -3285,662 +3285,322 @@
     
-%7D
+  %7D)
 );%0A    
-%7D%0A%0A
     
-@SuppressWarnings(%22OptionalUsedAsFieldOrParameterType%22)%0A    private static boolean didChangeOutput(Optional%3CAfterPreviousExecutionState%3E afterPreviousExecutionState, ImmutableSortedMap%3CString, FileSystemSnapshot%3E current) %7B%0A        // If there is no previous state, then we do have output changes%0A        if (!afterPreviousExecutionState.isPresent()) %7B%0A            return true;%0A        %7D%0A%0A        // If there are different output properties compared to the previous execution, then we do have output changes%0A        ImmutableSortedMap%3CString, FileSystemSnapshot%3E previous = afterPreviousExecutionState.get().getOutputFilesProducedByWork();
+return result;%0A    %7D%0A%0A    private static boolean didOutputsChange(ImmutableSortedMap%3CString, FileSystemSnapshot%3E previous, ImmutableSortedMap%3CString, FileSystemSnapshot%3E current) %7B%0A        // If there are different output properties compared to the previous execution, then we do have output changes
 %0A   
@@ -3711,16 +3711,17 @@
 therwise
+,
  do deep

Simplify StoreExecutionStateStep