/* 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 nsSimpleURI_h_
#define nsSimpleURI_h_

#include "nsIIPCSerializableURI.h"
#include "nsIURI.h"
#include "nsIURIWithSizeOf.h"
#include "nsISerializable.h"
#include "nsString.h"
#include "nsIClassInfo.h"
#include "nsIURIMutator.h"
#include "nsISimpleURIMutator.h"
#include "URIHasher.h"

namespace mozilla {
namespace net {

#define NS_THIS_SIMPLEURI_IMPLEMENTATION_CID  \
  {/* 0b9bb0c2-fee6-470b-b9b9-9fd9462b5e19 */ \
   0x0b9bb0c2,                                \
   0xfee6,                                    \
   0x470b,                                    \
   {0xb9, 0xb9, 0x9f, 0xd9, 0x46, 0x2b, 0x5e, 0x19}}

class nsSimpleURI : public nsIURI,
                    public nsISerializable,
                    public nsIIPCSerializableURI,
                    public nsIURIWithSizeOf,
                    public URIHasher {
 protected:
  nsSimpleURI() = default;
  virtual ~nsSimpleURI() = default;

 public:
  NS_INLINE_DECL_STATIC_IID(NS_THIS_SIMPLEURI_IMPLEMENTATION_CID)
  NS_DECL_THREADSAFE_ISUPPORTS
  NS_DECL_NSIURI
  NS_DECL_NSISERIALIZABLE
  NS_DECL_NSIIPCSERIALIZABLEURI
  NS_DECL_NSIURIWITHSIZEOF

  // nsSimpleURI methods:

  bool Equals(nsSimpleURI* aOther) { return EqualsInternal(aOther, eHonorRef); }

 protected:
  // enum used in a few places to specify how .ref attribute should be handled
  enum RefHandlingEnum { eIgnoreRef, eHonorRef, eReplaceRef };

  virtual nsresult Clone(nsIURI** result);
  virtual nsresult SetSpecInternal(const nsACString& aSpec,
                                   bool aStripWhitespace = false);
  virtual nsresult SetScheme(const nsACString& input);
  virtual nsresult SetUserPass(const nsACString& input);
  nsresult SetUsername(const nsACString& input);
  virtual nsresult SetPassword(const nsACString& input);
  virtual nsresult SetHostPort(const nsACString& aValue);
  virtual nsresult SetHost(const nsACString& input);
  virtual nsresult SetPort(int32_t port);
  virtual nsresult SetPathQueryRef(const nsACString& aPath);
  virtual nsresult SetRef(const nsACString& aRef);
  virtual nsresult SetFilePath(const nsACString& aFilePath);
  virtual nsresult SetQuery(const nsACString& aQuery);
  virtual nsresult SetQueryWithEncoding(const nsACString& aQuery,
                                        const Encoding* encoding);
  nsresult ReadPrivate(nsIObjectInputStream* stream);

  // Helper to share code between Equals methods.
  virtual nsresult EqualsInternal(nsIURI* other,
                                  RefHandlingEnum refHandlingMode,
                                  bool* result);

  // Helper to be used by inherited classes who want to test
  // equality given an assumed nsSimpleURI.  This must NOT check
  // the passed-in other for QI to our CID.
  bool EqualsInternal(nsSimpleURI* otherUri, RefHandlingEnum refHandlingMode);

  virtual already_AddRefed<nsSimpleURI> StartClone();

  void TrimTrailingCharactersFromPath();

  // Initialize `mQueryPos` and `mRefPos` from `mSpec`, and perform
  // component-specific escapes. `mPathPos` should already be initialized.
  nsresult SetPathQueryRefInternal();

  bool Deserialize(const mozilla::ipc::URIParams&);

  // computed index helpers
  size_t SchemeStart() const { return 0; }
  size_t SchemeEnd() const { return mPathSep; }
  size_t SchemeLen() const { return SchemeEnd() - SchemeStart(); }

  size_t PathStart() const { return mPathSep + 1; }
  inline size_t PathEnd() const;
  size_t PathLen() const { return PathEnd() - PathStart(); }

  bool IsQueryValid() const { return mQuerySep != kNotFound; }
  inline size_t QueryStart() const;
  inline size_t QueryEnd() const;
  size_t QueryLen() const { return QueryEnd() - QueryStart(); }

  bool IsRefValid() const { return mRefSep != kNotFound; }
  inline size_t RefStart() const;
  inline size_t RefEnd() const;
  size_t RefLen() const { return RefEnd() - RefStart(); }

  // dependent substring getters
  nsDependentCSubstring Scheme() {
    return Substring(mSpec, SchemeStart(), SchemeLen());
  }
  nsDependentCSubstring Path() {
    return Substring(mSpec, PathStart(), PathLen());
  }
  nsDependentCSubstring Query() {
    return Substring(mSpec, QueryStart(), QueryLen());
  }
  nsDependentCSubstring Ref() { return Substring(mSpec, RefStart(), RefLen()); }
  nsDependentCSubstring SpecIgnoringRef() {
    return Substring(mSpec, 0, IsRefValid() ? mRefSep : -1);
  }

  // mSpec contains the normalized version of the URL spec (UTF-8 encoded).
  nsCString mSpec;

  // Index of the `:` character which indicates the start of the path.
  int32_t mPathSep = kNotFound;
  // Index of the `?` character which indicates the start of the query.
  // Will be `kNotFound` if there is no query.
  int32_t mQuerySep = kNotFound;
  // Index of the `#` character which indicates the start of the ref.
  // Will be `kNotFound` if there is no ref.
  int32_t mRefSep = kNotFound;

 public:
  class Mutator final : public nsIURIMutator,
                        public BaseURIMutator<nsSimpleURI>,
                        public nsISimpleURIMutator,
                        public nsISerializable {
    NS_DECL_ISUPPORTS
    NS_FORWARD_SAFE_NSIURISETTERS_RET(mURI)
    NS_DEFINE_NSIMUTATOR_COMMON

    NS_IMETHOD
    Write(nsIObjectOutputStream* aOutputStream) override {
      return NS_ERROR_NOT_IMPLEMENTED;
    }

    [[nodiscard]] NS_IMETHOD Read(nsIObjectInputStream* aStream) override {
      return InitFromInputStream(aStream);
    }

    [[nodiscard]] NS_IMETHOD SetSpecAndFilterWhitespace(
        const nsACString& aSpec, nsIURIMutator** aMutator) override {
      if (aMutator) {
        *aMutator = do_AddRef(this).take();
      }

      nsresult rv = NS_OK;
      RefPtr<nsSimpleURI> uri = new nsSimpleURI();
      rv = uri->SetSpecInternal(aSpec, /* filterWhitespace */ true);
      if (NS_FAILED(rv)) {
        return rv;
      }
      mURI = std::move(uri);
      return NS_OK;
    }

    explicit Mutator() = default;

   private:
    virtual ~Mutator() = default;

    friend class nsSimpleURI;
  };

  friend BaseURIMutator<nsSimpleURI>;
};

//-----------------------------------------------------------------------------
// Computed index helpers
//-----------------------------------------------------------------------------

inline size_t nsSimpleURI::PathEnd() const {
  if (IsQueryValid()) {
    return mQuerySep;
  }
  if (IsRefValid()) {
    return mRefSep;
  }
  return mSpec.Length();
}

inline size_t nsSimpleURI::QueryStart() const {
  MOZ_DIAGNOSTIC_ASSERT(IsQueryValid());
  return mQuerySep + 1;
}

inline size_t nsSimpleURI::QueryEnd() const {
  MOZ_DIAGNOSTIC_ASSERT(IsQueryValid());
  if (IsRefValid()) {
    return mRefSep;
  }
  return mSpec.Length();
}

inline size_t nsSimpleURI::RefStart() const {
  MOZ_DIAGNOSTIC_ASSERT(IsRefValid());
  return mRefSep + 1;
}

inline size_t nsSimpleURI::RefEnd() const {
  MOZ_DIAGNOSTIC_ASSERT(IsRefValid());
  return mSpec.Length();
}

}  // namespace net
}  // namespace mozilla

#endif  // nsSimpleURI_h_
