/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef ScriptPreloader_inl_h
#define ScriptPreloader_inl_h

#include "mozilla/Attributes.h"
#include "mozilla/Assertions.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsString.h"
#include "nsTArray.h"

#include <prio.h>

namespace mozilla {

namespace loader {

using mozilla::dom::AutoJSAPI;

static inline Result<Ok, nsresult> Write(PRFileDesc* fd, const void* data,
                                         int32_t len) {
  if (PR_Write(fd, data, len) != len) {
    return Err(NS_ERROR_FAILURE);
  }
  return Ok();
}

static inline Result<Ok, nsresult> WritePadding(PRFileDesc* fd,
                                                uint8_t padding) {
  static const char paddingBytes[8] = "PADBYTE";
  MOZ_DIAGNOSTIC_ASSERT(padding <= sizeof(paddingBytes));

  if (padding == 0) {
    return Ok();
  }

  if (PR_Write(fd, static_cast<const void*>(paddingBytes), padding) !=
      padding) {
    return Err(NS_ERROR_FAILURE);
  }
  return Ok();
}

struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI {
  AutoSafeJSAPI() { Init(); }
};

template <typename T>
struct Matcher;

// Wraps the iterator for a nsTHashTable so that it may be used as a range
// iterator. Each iterator result acts as a smart pointer to the hash element,
// and has a Remove() method which will remove the element from the hash.
//
// It also accepts an optional Matcher instance against which to filter the
// elements which should be iterated over.
//
// Example:
//
//    for (auto& elem : HashElemIter<HashType>(hash)) {
//        if (elem->IsDead()) {
//            elem.Remove();
//        }
//    }
template <typename T>
class HashElemIter {
  using Iterator = typename T::Iterator;
  using ElemType = typename T::UserDataType;

  T& hash_;
  Matcher<ElemType>* matcher_;
  Iterator iter_;

 public:
  explicit HashElemIter(T& hash, Matcher<ElemType>* matcher = nullptr)
      : hash_(hash), matcher_(matcher), iter_(hash.Iter()) {}

  class Elem {
    friend class HashElemIter<T>;

    HashElemIter<T>& iter_;
    bool done_;

    Elem(HashElemIter& iter, bool done) : iter_(iter), done_(done) {
      skipNonMatching();
    }

    Iterator& iter() { return iter_.iter_; }

    void skipNonMatching() {
      if (iter_.matcher_) {
        while (!done_ && !iter_.matcher_->Matches(get())) {
          iter().Next();
          done_ = iter().Done();
        }
      }
    }

   public:
    Elem& operator*() { return *this; }

    ElemType get() {
      if (done_) {
        return nullptr;
      }
      return iter().UserData();
    }

    const ElemType get() const { return const_cast<Elem*>(this)->get(); }

    ElemType operator->() { return get(); }

    const ElemType operator->() const { return get(); }

    operator ElemType() { return get(); }

    void Remove() { iter().Remove(); }

    Elem& operator++() {
      MOZ_ASSERT(!done_);

      iter().Next();
      done_ = iter().Done();

      skipNonMatching();
      return *this;
    }

    bool operator!=(Elem& other) const {
      return done_ != other.done_ || this->get() != other.get();
    }
  };

  Elem begin() { return Elem(*this, iter_.Done()); }

  Elem end() { return Elem(*this, true); }
};

template <typename T>
HashElemIter<T> IterHash(T& hash,
                         Matcher<typename T::UserDataType>* matcher = nullptr) {
  return HashElemIter<T>(hash, matcher);
}

template <typename T, typename F>
bool Find(T&& iter, F&& match) {
  for (auto& elem : iter) {
    if (match(elem)) {
      return true;
    }
  }
  return false;
}

};  // namespace loader
};  // namespace mozilla

#endif  // ScriptPreloader_inl_h
