/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef image_expectations_DEFINED
#define image_expectations_DEFINED

#include "SkBitmap.h"
#include "SkJSONCPP.h"
#include "SkOSFile.h"
#include "SkRefCnt.h"

namespace sk_tools {

    /**
     * The digest of an image (either an image we have generated locally, or an image expectation).
     *
     * Currently, this is always a uint64_t hash digest of an SkBitmap.
     */
    class ImageDigest : public SkRefCnt {
    public:
        /**
         * Create an ImageDigest of a bitmap.
         *
         * Note that this is an expensive operation, because it has to examine all pixels in
         * the bitmap.  You may wish to consider using the BitmapAndDigest class, which will
         * compute the ImageDigest lazily.
         *
         * @param bitmap image to get the digest of
         */
        explicit ImageDigest(const SkBitmap &bitmap);

        /**
         * Create an ImageDigest using a hashType/hashValue pair.
         *
         * @param hashType the algorithm used to generate the hash; for now, only
         *     kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 is allowed.
         * @param hashValue the value generated by the hash algorithm for a particular image.
         */
        explicit ImageDigest(const SkString &hashType, uint64_t hashValue);

        /**
         * Returns the hash digest type as an SkString.
         *
         * For now, this always returns kJsonValue_Image_ChecksumAlgorithm_Bitmap64bitMD5 .
         */
        SkString getHashType() const;

        /**
         * Returns the hash digest value as a uint64_t.
         */
        uint64_t getHashValue() const;

    private:
        uint64_t fHashValue;
    };

    /**
     * Container that holds a reference to an SkBitmap and computes its ImageDigest lazily.
     *
     * Computing the ImageDigest can be expensive, so this can help you postpone (or maybe even
     * avoid) that work.
     */
    class BitmapAndDigest {
    public:
        explicit BitmapAndDigest(const SkBitmap &bitmap);

        const ImageDigest *getImageDigestPtr();
        const SkBitmap *getBitmapPtr() const;
    private:
        const SkBitmap fBitmap;
        SkAutoTUnref<ImageDigest> fImageDigestRef;
    };

    /**
     * Collects ImageDigests of actually rendered images, perhaps comparing to expectations.
     */
    class ImageResultsAndExpectations {
    public:
        /**
         * Adds expectations from a JSON file, returning true if successful.
         *
         * If the file exists but is empty, it succeeds, and there will be no expectations.
         * If the file does not exist, this will fail.
         *
         * Reasoning:
         * Generating expectations the first time can be a tricky chicken-and-egg
         * proposition.  "I need actual results to turn into expectations... but the only
         * way to get actual results is to run the tool, and the tool won't run without
         * expectations!"
         * We could make the tool run even if there is no expectations file at all, but it's
         * better for the tool to fail if the expectations file is not found--that will tell us
         * quickly if files are not being copied around as they should be.
         * Creating an empty file is an easy way to break the chicken-and-egg cycle and generate
         * the first real expectations.
         */
        bool readExpectationsFile(const char *jsonPath);

        /**
         * Adds this image to the summary of results.
         *
         * @param sourceName name of the source file that generated this result
         * @param fileName relative path to the image output file on local disk
         * @param digest description of the image's contents
         * @param tileNumber if not NULL, pointer to tile number
         */
        void add(const char *sourceName, const char *fileName, const ImageDigest &digest,
                 const int *tileNumber=NULL);

        /**
         * Returns true if this test result matches its expectations.
         * If there are no expectations for this test result, this will return false.
         *
         * @param sourceName name of the source file that generated this result
         * @param digest description of the image's contents
         * @param tileNumber if not NULL, pointer to tile number
         */
        bool matchesExpectation(const char *sourceName, const ImageDigest &digest,
                                const int *tileNumber=NULL);

        /**
         * Writes the summary (as constructed so far) to a file.
         *
         * @param filename path to write the summary to
         */
        void writeToFile(const char *filename) const;

    private:

        /**
         * Read the file contents from filePtr and parse them into jsonRoot.
         *
         * It is up to the caller to close filePtr after this is done.
         *
         * Returns true if successful.
         */
        static bool Parse(SkFILE* filePtr, Json::Value *jsonRoot);

        Json::Value fActualResults;
        Json::Value fExpectedJsonRoot;
        Json::Value fExpectedResults;
    };

} // namespace sk_tools

#endif  // image_expectations_DEFINED