package org.jenkinsci.plugins.docker.commons.fingerprint;

import hudson.BulkChange;
import hudson.model.Fingerprint;
import hudson.model.Run;
import jenkins.model.Jenkins;

import java.io.IOException;
import java.util.Collection;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import jenkins.model.FingerprintFacet;

/**
 * Entry point into fingerprint related functionalities in Docker.
 */
public class DockerFingerprints {
    private DockerFingerprints() {} // no instantiation

    private static String trim(String imageId) {
        if (imageId.length() != 64) {
            throw new IllegalArgumentException("Expecting 64char full image ID, but got " + imageId);
        }
        return imageId.substring(0, 32);
    }
    
    /**
     * Gets a fingerprint hash for Docker image.
     * @param imageId Docker image ID.
     * @return 32-symbol fingerprint hash
     */
    /*package*/ static @Nonnull String getImageFingerprintHash(@Nonnull String imageId) {
        return trim(imageId);
    }

    /**
     * Gets {@link Fingerprint} for a given docker image.
     */
    public static @CheckForNull Fingerprint of(@Nonnull String imageId) throws IOException {
        return Jenkins.getInstance().getFingerprintMap().get(trim(imageId));
    }

    private static @Nonnull Fingerprint make(@Nonnull Run<?,?> run, @Nonnull String imageId) throws IOException {
        return Jenkins.getInstance().getFingerprintMap().getOrCreate(run, "<docker-image>", trim(imageId));
    }

    /**
     * Adds a new {@link ContainerRecord} for the specified image, creating necessary intermediate objects as it goes.
     */
    public static void addRunFacet(@Nonnull ContainerRecord record, @Nonnull Run<?,?> run) throws IOException {
        String imageId = record.getImageId();
        Fingerprint f = make(run, imageId);
        Collection<FingerprintFacet> facets = f.getFacets();
        DockerRunFingerprintFacet runFacet = null;
        for (FingerprintFacet facet : facets) {
            if (facet instanceof DockerRunFingerprintFacet) {
                runFacet = (DockerRunFingerprintFacet) facet;
                break;
            }
        }
        BulkChange bc = new BulkChange(f);
        try {
            if (runFacet == null) {
                runFacet = new DockerRunFingerprintFacet(f, System.currentTimeMillis(), imageId);
                facets.add(runFacet);
            }
            runFacet.add(record);
            runFacet.addFor(run);
            DockerFingerprintAction.addToRun(f, imageId, run);
            bc.commit();
        } finally {
            bc.abort();
        }
    }

    /**
     * Creates a new {@link DockerAncestorFingerprintFacet} and {@link DockerDescendantFingerprintFacet} and adds a run.
     * Or adds to existing facets.
     * @param ancestorImageId the ID of the image specified in a {@code FROM} instruction, or null in case of {@code scratch} (i.e., the descendant is a base image)
     * @param descendantImageId the ID of the image which was built
     * @param run the build in which the image building occurred
     */
    public static void addFromFacet(@CheckForNull String ancestorImageId, @Nonnull String descendantImageId, @Nonnull Run<?,?> run) throws IOException {
        long timestamp = System.currentTimeMillis();
        if (ancestorImageId != null) {
            Fingerprint f = make(run, ancestorImageId);
            Collection<FingerprintFacet> facets = f.getFacets();
            DockerDescendantFingerprintFacet descendantFacet = null;
            for (FingerprintFacet facet : facets) {
                if (facet instanceof DockerDescendantFingerprintFacet) {
                    descendantFacet = (DockerDescendantFingerprintFacet) facet;
                    break;
                }
            }
            BulkChange bc = new BulkChange(f);
            try {
                if (descendantFacet == null) {
                    descendantFacet = new DockerDescendantFingerprintFacet(f, timestamp, ancestorImageId);
                    facets.add(descendantFacet);
                }
                descendantFacet.addDescendantImageId(descendantImageId);
                descendantFacet.addFor(run);
                DockerFingerprintAction.addToRun(f, ancestorImageId, run);
                bc.commit();
            } finally {
                bc.abort();
            }
        }
        Fingerprint f = make(run, descendantImageId);
        Collection<FingerprintFacet> facets = f.getFacets();
        DockerAncestorFingerprintFacet ancestorFacet = null;
        for (FingerprintFacet facet : facets) {
            if (facet instanceof DockerAncestorFingerprintFacet) {
                ancestorFacet = (DockerAncestorFingerprintFacet) facet;
                break;
            }
        }
        BulkChange bc = new BulkChange(f);
        try {
            if (ancestorFacet == null) {
                ancestorFacet = new DockerAncestorFingerprintFacet(f, timestamp, descendantImageId);
                facets.add(ancestorFacet);
            }
            if (ancestorImageId != null) {
                ancestorFacet.addAncestorImageId(ancestorImageId);
            }
            ancestorFacet.addFor(run);
            DockerFingerprintAction.addToRun(f, descendantImageId, run);
            bc.commit();
        } finally {
            bc.abort();
        }
    }

}

@@ -504,275 +504,250 @@
 ion%0A
+ 
 %0A    
-private static String trim(String imageId) %7B%0A        if (imageId.length() != 64) %7B%0A            throw new IllegalArgumentException(%22Expecting 64char full image ID, but got %22 + imageId);%0A        %7D%0A        return imageId.substring(0, 32);%0A    %7D%0A    %0A
+/**%0A     * Gets a fingerprint hash for Docker image ID.%0A     * This method calculates image hash without retrieving a fingerprint by %0A     * %7B@link DockerFingerprints#of(java.lang.String)%7D, which may be a high-cost call.%0A 
     
-/*
 *
+ 
 %0A     * 
 Gets
@@ -746,35 +746,22 @@
   * 
-Gets a fingerprint hash for
+@param imageId
  Doc
@@ -761,32 +761,35 @@
 eId Docker image
+ ID
 .%0A     * @param 
@@ -785,38 +785,54 @@
   * 
-@param imageId Docker image ID
+     Only 64-char full image IDs are supported
 .%0A  
@@ -851,14 +851,12 @@
  32-
-symbol
+char
  fin
@@ -879,25 +879,77 @@
    *
-/%0A    /*package*/
+ @throws IllegalArgumentException Invalid image ID%0A     */%0A    public
  sta
@@ -1031,27 +1031,189 @@
     
-return trim(imageId
+if (imageId.length() != 64) %7B%0A            throw new IllegalArgumentException(%22Expecting 64-char full image ID, but got %22 + imageId);%0A        %7D%0A        return imageId.substring(0, 32
 );%0A 
@@ -1446,20 +1446,39 @@
 p().get(
-trim
+getImageFingerprintHash
 (imageId
@@ -1698,12 +1698,31 @@
 %3E%22, 
-trim
+getImageFingerprintHash
 (ima

Introduce DockerFingerprints::getImageFingerprintHash() method.