#ifndef _ZIM_TOOL_ZIMFILECHECKS_H_
#define _ZIM_TOOL_ZIMFILECHECKS_H_

#include <vector>
#include <iostream>
#include <bitset>
#include <mutex>

#include <mustache.hpp>

#include "json_tools.h"
#include "../progress.h"

namespace zim {
  class Archive;
}

enum StatusCode : int {
   PASS = 0,
   FAIL = 1,
   EXCEPTION = 2
};

enum class LogTag { ERROR, WARNING };

enum class TestType {
    CHECKSUM,
    INTEGRITY,
    EMPTY,
    METADATA,
    FAVICON,
    MAIN_PAGE,
    REDUNDANT,
    URL_INTERNAL,
    URL_EXTERNAL,
    URL_EMPTY,
    REDIRECT,

    COUNT
};

class EnabledTests {
    std::bitset<size_t(TestType::COUNT)> tests;

  public:
    EnabledTests() {}

    void enableAll() { tests.set(); }
    void enable(TestType tt) { tests.set(size_t(tt)); }
    bool isEnabled(TestType tt) const { return tests[size_t(tt)]; }
};

struct ZimCheckOptions {
  EnabledTests enabledTests;
  bool quick = false;
};

enum class MsgId
{
  CHECKSUM,
  MAIN_PAGE,
  METADATA,
  EMPTY_ENTRY,
  OUTOFBOUNDS_LINK,
  ABSPATH_LINK,
  EMPTY_LINKS,
  DANGLING_LINKS,
  EXTERNAL_LINK,
  REDUNDANT_ITEMS,
  REDIRECT_LOOP,
  MISSING_FAVICON
};

using MsgParams = kainjow::mustache::object;

JSON::OutputStream& operator<<(JSON::OutputStream& out, TestType check);
JSON::OutputStream& operator<<(JSON::OutputStream& out, EnabledTests checks);

class ErrorLogger {
  private:
    struct MsgIdWithParams
    {
      MsgId msgId;
      MsgParams msgParams;
    };

    bool logStreamOpen = false;

    // testStatus[i] corresponds to the status of i'th test
    std::bitset<size_t(TestType::COUNT)> testStatus;

    std::mutex msgMutex;

    mutable JSON::OutputStream jsonOutputStream;

    static std::string expand(const MsgIdWithParams& msg);
    void jsonOutput(const MsgIdWithParams& msg) const;

  public:
    explicit ErrorLogger(bool _jsonOutputMode = false);
    ~ErrorLogger();

    void infoMsg(const std::string& msg) const;

    void startLogStream();
    void endLogStream();

    template<class T>
    void addInfo(const std::string& key, const T& value) {
      if ( jsonOutputStream.enabled() ) {
        jsonOutputStream << JSON::property(key, value);
      }
    }

    void setTestResult(TestType type, bool status);
    void addMsg(MsgId msgid, const MsgParams& msgParams);
    bool overallStatus() const;
};


void test_checksum(zim::Archive& archive, ErrorLogger& reporter);
bool test_integrity(const std::string& filename, ErrorLogger& reporter);
void test_metadata(const zim::Archive& archive, ErrorLogger& reporter);
void test_favicon(const zim::Archive& archive, ErrorLogger& reporter);
void test_mainpage(const zim::Archive& archive, ErrorLogger& reporter);
void test_articles(const zim::Archive& archive, ErrorLogger& reporter, ProgressBar& progress,
                   const ZimCheckOptions& options, int thread_count=1);
void test_redirect_loop(const zim::Archive& archive, ErrorLogger& reporter);

#endif
