/*
 *  Copyright (C) 2015-2018 Team Kodi
 *  This file is part of Kodi - https://kodi.tv
 *
 *  SPDX-License-Identifier: GPL-2.0-or-later
 *  See LICENSES/README.md for more information.
 */

#pragma once

#include <stdint.h>

#include <string>
#include <vector>

class CHttpRange
{
public:
  CHttpRange() = default;
  CHttpRange(uint64_t firstPosition, uint64_t lastPosition);
  virtual ~CHttpRange() = default;

  bool operator<(const CHttpRange &other) const;
  bool operator==(const CHttpRange &other) const;
  bool operator!=(const CHttpRange &other) const;

  virtual uint64_t GetFirstPosition() const { return m_first; }
  virtual void SetFirstPosition(uint64_t firstPosition) { m_first = firstPosition; }
  virtual uint64_t GetLastPosition() const { return m_last; }
  virtual void SetLastPosition(uint64_t lastPosition) { m_last = lastPosition; }

  virtual uint64_t GetLength() const;
  virtual void SetLength(uint64_t length);

  virtual bool IsValid() const;

protected:
  uint64_t m_first = 1;
  uint64_t m_last = 0;
};

typedef std::vector<CHttpRange> HttpRanges;

class CHttpResponseRange : public CHttpRange
{
public:
  CHttpResponseRange();
  CHttpResponseRange(uint64_t firstPosition, uint64_t lastPosition);
  CHttpResponseRange(const void* data, uint64_t firstPosition, uint64_t lastPosition);
  CHttpResponseRange(const void* data, uint64_t length);
  ~CHttpResponseRange() override = default;

  bool operator==(const CHttpResponseRange &other) const;
  bool operator!=(const CHttpResponseRange &other) const;

  const void* GetData() const { return m_data; }
  void SetData(const void* data) { m_data = data; }
  void SetData(const void* data, uint64_t length);
  void SetData(const void* data, uint64_t firstPosition, uint64_t lastPosition);

  bool IsValid() const override;

protected:
  const void* m_data;
};

typedef std::vector<CHttpResponseRange> HttpResponseRanges;

class CHttpRanges final
{
public:
  CHttpRanges();
  explicit CHttpRanges(const HttpRanges& httpRanges);

  const HttpRanges& Get() const { return m_ranges; }
  bool Get(size_t index, CHttpRange& range) const;
  bool GetFirst(CHttpRange& range) const;
  bool GetLast(CHttpRange& range) const;
  size_t Size() const { return m_ranges.size(); }
  bool IsEmpty() const { return m_ranges.empty(); }

  bool GetFirstPosition(uint64_t& position) const;
  bool GetLastPosition(uint64_t& position) const;
  uint64_t GetLength() const;

  bool GetTotalRange(CHttpRange& range) const;

  void Add(const CHttpRange& range);
  void Remove(size_t index);
  void Clear();

  HttpRanges::const_iterator Begin() const { return m_ranges.begin(); }
  HttpRanges::const_iterator End() const { return m_ranges.end(); }

  bool Parse(const std::string& header);
  bool Parse(const std::string& header, uint64_t totalLength);

protected:
  void SortAndCleanup();

  HttpRanges m_ranges;
};

class HttpRangeUtils
{
public:
  /*!
  * \brief Generates a valid Content-Range HTTP header value for the given HTTP
  * range definition.
  *
  * \param range HTTP range definition used to generate the Content-Range HTTP header
  * \return Content-Range HTTP header value
  */
  static std::string GenerateContentRangeHeaderValue(const CHttpRange* range);

  /*!
  * \brief Generates a valid Content-Range HTTP header value for the given HTTP
  * range properties.
  *
  * \param start Start position of the HTTP range
  * \param end Last/End position of the HTTP range
  * \param total Total length of original content (not just the range)
  * \return Content-Range HTTP header value
  */
  static std::string GenerateContentRangeHeaderValue(uint64_t start, uint64_t end, uint64_t total);

#ifdef HAS_WEB_SERVER
  /*!
   * \brief Generates a multipart boundary that can be used in ranged HTTP
   * responses.
   *
   * \return Multipart boundary that can be used in ranged HTTP responses
   */
  static std::string GenerateMultipartBoundary();

  /*!
  * \brief Generates the multipart/byteranges Content-Type HTTP header value
  * containing the given multipart boundary for a ranged HTTP response.
  *
  * \param multipartBoundary Multipart boundary to be used in the ranged HTTP response
  * \return multipart/byteranges Content-Type HTTP header value
  */
  static std::string GenerateMultipartBoundaryContentType(const std::string& multipartBoundary);

  /*!
  * \brief Generates a multipart boundary including the Content-Type HTTP
  * header value with the (actual) given content type of the original
  * content.
  *
  * \param multipartBoundary Multipart boundary to be used in the ranged HTTP response
  * \param contentType (Actual) Content type of the original content
  * \return Multipart boundary (including the Content-Type HTTP header) value that can be used in ranged HTTP responses
  */
  static std::string GenerateMultipartBoundaryWithHeader(const std::string& multipartBoundary, const std::string& contentType);

  /*!
  * \brief Generates a multipart boundary including the Content-Type HTTP
  * header value with the (actual) given content type of the original
  * content and the Content-Range HTTP header value for the given range.
  *
  * \param multipartBoundary Multipart boundary to be used in the ranged HTTP response
  * \param contentType (Actual) Content type of the original content
  * \param range HTTP range definition used to generate the Content-Range HTTP header
  * \return Multipart boundary (including the Content-Type and Content-Range HTTP headers) value that can be used in ranged HTTP responses
  */
  static std::string GenerateMultipartBoundaryWithHeader(const std::string& multipartBoundary, const std::string& contentType, const CHttpRange* range);

  /*!
  * \brief Generates a multipart boundary including the Content-Type HTTP
  * header value with the (actual) given content type of the original
  * content and the Content-Range HTTP header value for the given range.
  *
  * \param multipartBoundaryWithContentType Multipart boundary (already including the Content-Type HTTP header value) to be used in the ranged HTTP response
  * \param range HTTP range definition used to generate the Content-Range HTTP header
  * \return Multipart boundary (including the Content-Type and Content-Range HTTP headers) value that can be used in ranged HTTP responses
  */
  static std::string GenerateMultipartBoundaryWithHeader(const std::string& multipartBoundaryWithContentType, const CHttpRange* range);

  /*!
  * \brief Generates a multipart boundary end that can be used in ranged HTTP
   * responses.
  *
  * \param multipartBoundary Multipart boundary to be used in the ranged HTTP response
  * \return Multipart boundary end that can be used in a ranged HTTP response
  */
  static std::string GenerateMultipartBoundaryEnd(const std::string& multipartBoundary);
#endif // HAS_WEB_SERVER
};
