// bdljsn_location.h                                                  -*-C++-*-
#ifndef INCLUDED_BDLJSN_LOCATION
#define INCLUDED_BDLJSN_LOCATION

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a value-semantic type for location in a JSON document.
//
//@CLASSES:
//  bdljsn::Location: position in a JSON document
//
//@SEE_ALSO: bdljsn_jsonutil, bdljsn_json
//
//@DESCRIPTION: This component provides a single, unconstrained
// (value-semantic) attribute class, `bdljsn::Location`, that is used to
// describe a location in a (JSON) document.  Location is expressed by the
// `offset` (attrbute) in bytes from the start of the document.  See
// `bdljson_jsonutil` for utilities that may provide `bdljsn::Location` values
// when reporting error states.
//
///Attributes
///----------
// ```
// Name                Type           Default
// ------------------  -------------  -------
// offset              bsl::uint64_t  0
// ```
// * `offset`: the offset into the JSON document
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Syntax
///- - - - - - - - - - - -
// This example exercises each of the methods of the `bdljsn::Location` class.
//
// First, create a `bdljsn::Location` object (having the default value):
// ```
// bdljsn::Location locationA;
// assert(0 == locationA.offset());
// ```
// Then, set `locationA` to some other offset:
// ```
// locationA.setOffset(1);
// assert(1 == locationA.offset());
// ```
// Next, use the value constructor to create a second location having the same
// offset as the first:
// ```
//     bdljsn::Location locationB(1);
//     assert(1         == locationB.offset());
//     assert(locationA == locationB);
// ```
// Then, set the second location to the maximum offset:
// ```
// const bsl::uint64_t maxOffset = bsl::numeric_limits<bsl::uint64_t>::max();
//
// locationB.setOffset(maxOffset);
// assert(maxOffset == locationB.offset());
// ```
// Next, create another `Location` that is a copy of the one at `maxOffset':
// ```
// bdljsn::Location locationC(locationB);
// assert(locationB == locationC);
// ```
// Then, set the first location back to the default state:
// ```
// locationA.reset();
// assert(0                  == locationA.offset());
// assert(bdljsn::Location() == locationA);
// ```
//  Next, print the value of each:
// ```
// bsl::cout << locationA << "\n"
//           << locationB << bsl::endl;
//
// bsl::cout << "\n";
//
// locationC.print(bsl::cout, 2, 3);
// ```
// and observe:
// ```
//  0
//  18446744073709551615
//
//       [
//          offset = 18446744073709551615
//       ]
// ```
// Finally, set each location equal to the first:
// ```
// locationC = locationB = locationA;
// assert(0 == locationA.offset());
// assert(0 == locationB.offset());
// assert(0 == locationC.offset());
// ```

#include <bdlscm_version.h>

#include <bslalg_swaputil.h>

#include <bslmf_isbitwisemoveable.h>
#include <bslmf_nestedtraitdeclaration.h>

#include <bslh_hash.h>

#include <bsl_cstdint.h> // 'bsl::uint64_t'
#include <bsl_iosfwd.h>

namespace BloombergLP {
namespace bdljsn {

                               // ==============
                               // class Location
                               // ==============

/// This unconstrained (value-semantic) attribute class specifies a location in
/// a (JSON) document.  See the [](#Attributes) for information on the class
/// attributes.  Note that the class invariants are identically the constraints
/// on the individual attributes.
class Location {

    // DATA
    bsl::uint64_t d_offset; // offset in bytes into a document

    // FRIENDS
    template <class HASHALG>
    friend void hashAppend(HASHALG& hashAlg , const Location& );
    friend void swap(Location& , Location& );

  public:
    BSLMF_NESTED_TRAIT_DECLARATION(Location, bslmf::IsBitwiseMoveable);

    // CREATORS

    /// Create a `Location` object having the default value, 0.
    Location();

    /// Create a `Location` object having the specified `offset`.
    explicit Location(bsl::uint64_t offset);

    /// Create a `Location` object having the same value as the specified
    /// `original` object.
    Location(const Location& original);

    //! ~Location() = default;
        // Destroy this object.

    // MANIPULATORS

    /// Assign to this object the value of the specified `rhs` object.
    Location& operator=(const Location& rhs);

    /// Reset this object to the default value (i.e., its value upon default
    /// construction).
    Location& reset();

    /// Set the "offset" attribute of this object to the specified `value`.
    Location& setOffset(bsl::uint64_t value);

                                  // Aspects

    /// Efficiently exchange the value of this object with the value of the
    /// specified `other` object.  This method provides the no-throw
    /// exception-safety guarantee.
    void swap(Location& other);

    // ACCESSORS

    /// Return the `offset` attribute of this object.
    bsl::uint64_t offset() const;

                                  // Aspects

    /// Format this object to the specified output `stream` at the optionally
    /// specified indentation `level` and return a reference to the modifiable
    /// `stream`.  If `level` is specified, optionally specify
    /// `spacesPerLevel`, the number of spaces per indentation level for this
    /// and all of its nested objects.  Each line is indented by the absolute
    /// value of `level * spacesPerLevel`.  If `level` is negative, suppress
    /// indentation of the first line.  If `spacesPerLevel` is negative,
    /// suppress line breaks and format the entire output on one line.  If
    /// `stream` is initially invalid, this operation has no effect.  Note that
    /// a trailing newline is provided in multiline mode only.
    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
};

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` attribute objects have the
/// same value, and `false` otherwise.  Two attribute objects have the same
/// value if each respective attribute has the same value.
inline
bool operator==(const Location& lhs, const Location& rhs);

/// Return `true` if the specified `lhs` and `rhs` attribute objects do not
/// have the same value, and `false` otherwise.  Two attribute objects do not
/// have the same value if one or more respective attributes differ in values.
inline
bool operator!=(const Location& lhs, const Location& rhs);

/// Write the value of the specified `object` to the specified output `stream`
/// in a single-line format, and return a non-`const` reference to `stream`.
/// If `stream` is not valid on entry, this operation has no effect.  Note that
/// this human-readable format is not fully specified and can change without
/// notice.  Also note that this method has the same behavior as
/// `object.print(stream, 0, -1)`, but with the attribute names elided.
bsl::ostream& operator<<(bsl::ostream& stream, const Location& object);

// FREE FUNCTIONS

/// Pass the specified `object` to the specified `hashAlg`.  This function
/// integrates with the `bslh` modular hashing system and effectively provides
/// a `bsl::hash` specialization for `Location`.
template <class HASHALG>
void hashAppend(HASHALG& hashAlg, const Location& object);

/// Exchange the values of the specified `a` and `b` objects.  This function
/// provides the no-throw exception-safety guarantee.
void swap(Location& a, Location& b);

// ============================================================================
//                         INLINE DEFINITIONS
// ============================================================================

                               // --------------
                               // class Location
                               // --------------

// CREATORS
inline
Location::Location()
: d_offset(0)
{
}

inline
Location::Location(bsl::uint64_t offset)
: d_offset(offset)
{
}

inline
Location::Location(const Location& original)
: d_offset(original.d_offset)
{
}

// MANIPULATORS
inline
Location& Location::operator=(const Location& rhs)
{
    d_offset = rhs.d_offset;

    return *this;
}

inline
Location& Location::reset()
{
    d_offset = 0;
    return *this;
}

inline
Location& Location::setOffset(bsl::uint64_t value)
{
    d_offset = value;
    return *this;
}

                                  // Aspects

inline
void Location::swap(Location& other)
{
    bslalg::SwapUtil::swap(&d_offset,&other.d_offset);
}

// ACCESSORS
inline
bsl::uint64_t Location::offset() const
{
    return d_offset;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdljsn::operator==(const bdljsn::Location& lhs,
                        const bdljsn::Location& rhs)
{
    return lhs.offset() == rhs.offset();
}

inline
bool bdljsn::operator!=(const bdljsn::Location& lhs,
                        const bdljsn::Location& rhs)
{
    return lhs.offset() != rhs.offset();
}

// FREE FUNCTIONS
template <class HASHALG>
inline
void bdljsn::hashAppend(HASHALG& hashAlg, const Location& object)
{
    using ::BloombergLP::bslh::hashAppend;
    hashAppend(hashAlg, object.d_offset);
}

inline
void bdljsn::swap(Location& a, Location& b)
{
    bslalg::SwapUtil::swap(&a.d_offset, &b.d_offset);
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2022 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
