// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_ZUCCHINI_ENCODED_VIEW_H_
#define COMPONENTS_ZUCCHINI_ENCODED_VIEW_H_

#include <stddef.h>
#include <stdint.h>

#include <iterator>
#include <vector>

#include "base/macros.h"
#include "components/zucchini/image_index.h"
#include "components/zucchini/image_utils.h"

namespace zucchini {

// Zucchini-gen performs semantics-aware matching:
// - Same-typed reference target in "old" and "new" can be associated.
//   Associated targets are assigned an identifier called "label" (and for
//   unassociated targets, label = 0).
// - EncodedView maps each offset in "old" and "new" images to a "projected
//   value", which can be:
//   - Raw byte value (0-255) for non-references.
//   - Reference "projected value" (> 256) that depends on target {type, label}
//     at each reference's location (byte 0).
//   - Reference padding value (256) at the body of each reference (bytes 1+).
// - The projected values for "old" and "new" are used to build the equivalence
//   map.

constexpr size_t kReferencePaddingProjection = 256;
constexpr size_t kBaseReferenceProjection = 257;

// A Range (providing begin and end iterators) that adapts ImageIndex to make
// image data appear as an Encoded Image, that is encoded data under a higher
// level of abstraction than raw bytes. In particular:
// - First byte of each reference become a projection of its type and label.
// - Subsequent bytes of each reference becomes |kReferencePaddingProjection|.
// - Non-reference raw bytes remain as raw bytes.
class EncodedView {
 public:
  // RandomAccessIterator whose values are the results of Projection().
  class Iterator {
   public:
    using iterator_category = std::random_access_iterator_tag;
    using value_type = size_t;
    using difference_type = ptrdiff_t;
    using reference = size_t;
    using pointer = size_t*;

    Iterator(const EncodedView* encoded_view, difference_type pos)
        : encoded_view_(encoded_view), pos_(pos) {}

    value_type operator*() const {
      return encoded_view_->Projection(static_cast<offset_t>(pos_));
    }

    value_type operator[](difference_type n) const {
      return encoded_view_->Projection(static_cast<offset_t>(pos_ + n));
    }

    Iterator& operator++() {
      ++pos_;
      return *this;
    }

    Iterator operator++(int) {
      Iterator tmp = *this;
      ++pos_;
      return tmp;
    }

    Iterator& operator--() {
      --pos_;
      return *this;
    }

    Iterator operator--(int) {
      Iterator tmp = *this;
      --pos_;
      return tmp;
    }

    Iterator& operator+=(difference_type n) {
      pos_ += n;
      return *this;
    }

    Iterator& operator-=(difference_type n) {
      pos_ -= n;
      return *this;
    }

    Iterator& operator=(const Iterator& it) {
      encoded_view_ = it.encoded_view_;
      pos_ = it.pos_;
      return *this;
    }

    friend bool operator==(Iterator a, Iterator b) { return a.pos_ == b.pos_; }

    friend bool operator!=(Iterator a, Iterator b) { return !(a == b); }

    friend bool operator<(Iterator a, Iterator b) { return a.pos_ < b.pos_; }

    friend bool operator>(Iterator a, Iterator b) { return b < a; }

    friend bool operator<=(Iterator a, Iterator b) { return !(b < a); }

    friend bool operator>=(Iterator a, Iterator b) { return !(a < b); }

    friend difference_type operator-(Iterator a, Iterator b) {
      return a.pos_ - b.pos_;
    }

    friend Iterator operator+(Iterator it, difference_type n) {
      it += n;
      return it;
    }

    friend Iterator operator-(Iterator it, difference_type n) {
      it -= n;
      return it;
    }

   private:
    const EncodedView* encoded_view_;
    difference_type pos_;
  };

  using value_type = size_t;
  using size_type = offset_t;
  using difference_type = ptrdiff_t;
  using const_iterator = Iterator;

  // |image_index| is the annotated image being adapted, and is required to
  // remain valid for the lifetime of the object.
  explicit EncodedView(const ImageIndex& image_index);
  ~EncodedView();

  // Projects |location| to a scalar value that describes the content at a
  // higher level of abstraction.
  value_type Projection(offset_t location) const;

  bool IsToken(offset_t location) const {
    return image_index_.IsToken(location);
  }

  // Returns the cardinality of the projection, i.e., the upper bound on
  // values returned by Projection().
  value_type Cardinality() const;

  // Associates |labels| to targets for a given |pool|, replacing previous
  // association. Values in |labels| must be smaller than |bound|.
  void SetLabels(PoolTag pool, std::vector<uint32_t>&& labels, size_t bound);
  const ImageIndex& image_index() const { return image_index_; }

  // Range functions.
  size_type size() const { return size_type(image_index_.size()); }
  const_iterator begin() const {
    return const_iterator{this, difference_type(0)};
  }
  const_iterator end() const {
    return const_iterator{this, difference_type(size())};
  }

 private:
  struct PoolInfo {
    PoolInfo();
    PoolInfo(PoolInfo&&);
    ~PoolInfo();

    // |labels| translates IndirectReference target_key to label.
    std::vector<uint32_t> labels;
    size_t bound = 0;
  };

  const ImageIndex& image_index_;
  std::vector<PoolInfo> pool_infos_;

  DISALLOW_COPY_AND_ASSIGN(EncodedView);
};

}  // namespace zucchini

#endif  // COMPONENTS_ZUCCHINI_ENCODED_VIEW_H_
