/* The copyright in this software is being made available under the BSD
 * License, included below. This software may be subject to other third party
 * and contributor rights, including patent rights, and no such rights are
 * granted under this license.
 *
 * Copyright (c) 2010-2020, ITU/ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

/** \file     Common.h
 *  \brief    Common 2D-geometrical structures
 */

#ifndef __COMMON__
#define __COMMON__

#include "CommonDef.h"

typedef int PosType;
typedef uint32_t SizeType;
struct Position
{
  PosType x;
  PosType y;

  Position()                                   : x(0),  y(0)  { }
  Position(const PosType _x, const PosType _y) : x(_x), y(_y) { }

  bool operator!=(const Position &other)  const { return x != other.x || y != other.y; }
  bool operator==(const Position &other)  const { return x == other.x && y == other.y; }

  Position offset(const Position pos)                 const { return Position(x + pos.x, y + pos.y); }
  Position offset(const PosType _x, const PosType _y) const { return Position(x + _x   , y + _y   ); }
  void     repositionTo(const Position newPos)              { x  = newPos.x; y  = newPos.y; }
  void     relativeTo  (const Position origin)              { x -= origin.x; y -= origin.y; }

  Position operator-( const Position &other )         const { return{ x - other.x, y - other.y }; }
};

struct Size
{
  SizeType width;
  SizeType height;

  Size()                                              : width(0),      height(0)       { }
  Size(const SizeType _width, const SizeType _height) : width(_width), height(_height) { }

  bool operator!=(const Size &other)      const { return (width != other.width) || (height != other.height); }
  bool operator==(const Size &other)      const { return (width == other.width) && (height == other.height); }
  uint32_t area()                             const { return (uint32_t) width * (uint32_t) height; }
#if REUSE_CU_RESULTS_WITH_MULTIPLE_TUS
  void resizeTo(const Size newSize)             { width = newSize.width; height = newSize.height; }
#endif
};

struct Area : public Position, public Size
{
  Area()                                                                         : Position(),       Size()       { }
  Area(const Position &_pos, const Size &_size)                                  : Position(_pos),   Size(_size)  { }
  Area(const PosType _x, const PosType _y, const SizeType _w, const SizeType _h) : Position(_x, _y), Size(_w, _h) { }

        Position& pos()                           { return *this; }
  const Position& pos()                     const { return *this; }
        Size&     size()                          { return *this; }
  const Size&     size()                    const { return *this; }

  const Position& topLeft()                 const { return *this; }
        Position  topRight()                const { return { (PosType) (x + width - 1), y                          }; }
        Position  bottomLeft()              const { return { x                        , (PosType) (y + height - 1) }; }
        Position  bottomRight()             const { return { (PosType) (x + width - 1), (PosType) (y + height - 1) }; }
        Position  center()                  const { return { (PosType) (x + width / 2), (PosType) (y + height / 2) }; }

  bool contains(const Position &_pos)       const { return (_pos.x >= x) && (_pos.x < (x + width)) && (_pos.y >= y) && (_pos.y < (y + height)); }
  bool contains(const Area &_area)          const { return contains(_area.pos()) && contains(_area.bottomRight()); }

  bool operator!=(const Area &other)        const { return (Size::operator!=(other)) || (Position::operator!=(other)); }
  bool operator==(const Area &other)        const { return (Size::operator==(other)) && (Position::operator==(other)); }
};

struct UnitScale
{
  UnitScale()                 : posx( 0), posy( 0), area(posx+posy) {}
  UnitScale( int sx, int sy ) : posx(sx), posy(sy), area(posx+posy) {}
  int posx;
  int posy;
  int area;

  template<typename T> T scaleHor ( const T &in ) const { return in >> posx; }
  template<typename T> T scaleVer ( const T &in ) const { return in >> posy; }
  template<typename T> T scaleArea( const T &in ) const { return in >> area; }

  Position scale( const Position &pos  ) const { return { pos.x >> posx, pos.y >> posy }; }
  Size     scale( const Size     &size ) const { return { size.width >> posx, size.height >> posy }; }
  Area     scale( const Area    &_area ) const { return Area( scale( _area.pos() ), scale( _area.size() ) ); }
};
namespace std
{
  template <>
  struct hash<Position> : public unary_function<Position, uint64_t>
  {
    uint64_t operator()(const Position& value) const
    {
      return (((uint64_t)value.x << 32) + value.y);
    }
  };

  template <>
  struct hash<Size> : public unary_function<Size, uint64_t>
  {
    uint64_t operator()(const Size& value) const
    {
      return (((uint64_t)value.width << 32) + value.height);
    }
  };
}
inline size_t rsAddr(const Position &pos, const uint32_t stride, const UnitScale &unitScale )
{
  return (size_t)(stride >> unitScale.posx) * (size_t)(pos.y >> unitScale.posy) + (size_t)(pos.x >> unitScale.posx);
}

inline size_t rsAddr(const Position &pos, const Position &origin, const uint32_t stride, const UnitScale &unitScale )
{
  return (stride >> unitScale.posx) * ((pos.y - origin.y) >> unitScale.posy) + ((pos.x - origin.x) >> unitScale.posx);
}

inline size_t rsAddr(const Position &pos, const uint32_t stride )
{
  return stride * (size_t)pos.y + (size_t)pos.x;
}

inline size_t rsAddr(const Position &pos, const Position &origin, const uint32_t stride )
{
  return stride * (pos.y - origin.y) + (pos.x - origin.x);
}

inline Area clipArea(const Area &_area, const Area &boundingBox)
{
  Area area = _area;

  if (area.x + area.width > boundingBox.x + boundingBox.width)
  {
    area.width = boundingBox.x + boundingBox.width - area.x;
  }

  if (area.y + area.height > boundingBox.y + boundingBox.height)
  {
    area.height = boundingBox.y + boundingBox.height - area.y;
  }

  return area;
}


class SizeIndexInfo
{
public:
  SizeIndexInfo(){}
  virtual ~SizeIndexInfo(){}
  SizeType numAllWidths()               { return (SizeType)m_idxToSizeTab.size(); }
  SizeType numAllHeights()              { return (SizeType)m_idxToSizeTab.size(); }
  SizeType numWidths()                  { return (SizeType)m_numBlkSizes; }
  SizeType numHeights()                 { return (SizeType)m_numBlkSizes; }
  SizeType sizeFrom( SizeType idx )     { return m_idxToSizeTab[idx]; }
  SizeType idxFrom( SizeType size )     { CHECKD( m_sizeToIdxTab[size] == std::numeric_limits<SizeType>::max(), "Index of given size does NOT EXIST!" ); return m_sizeToIdxTab[size]; }
  bool     isCuSize( SizeType size )    { return m_isCuSize[size]; }
  virtual void init( SizeType maxSize ) {}

protected:

  void xInit()
  {
    m_isCuSize.resize( m_sizeToIdxTab.size(), false );

    std::vector<SizeType> grpSizes;

    for( int i = 0, n = 0; i < m_sizeToIdxTab.size(); i++ )
    {
      if( m_sizeToIdxTab[i] != std::numeric_limits<SizeType>::max() )
      {
        m_sizeToIdxTab[i] = n;
        m_idxToSizeTab.push_back( i );
        n++;
      }

      if( m_sizeToIdxTab[i] != std::numeric_limits<SizeType>::max() && m_sizeToIdxTab[i >> 1] != std::numeric_limits<SizeType>::max() && i >= 4 )
      {
        m_isCuSize[i] = true;
      }

      // collect group sizes (for coefficient group coding)
      SizeType grpSize = i >> ( ( i & 3 ) != 0 ? 1 : 2 );
      if( m_sizeToIdxTab[i] != std::numeric_limits<SizeType>::max() && m_sizeToIdxTab[grpSize] == std::numeric_limits<SizeType>::max() )
      {
        grpSizes.push_back( grpSize );
      }
    }

    m_numBlkSizes = (SizeType)m_idxToSizeTab.size();

    for( SizeType grpSize : grpSizes )
    {
      if( grpSize > 0 && m_sizeToIdxTab[grpSize] == std::numeric_limits<SizeType>::max() )
      {
        m_sizeToIdxTab[grpSize] = (SizeType)m_idxToSizeTab.size();
        m_idxToSizeTab.push_back( grpSize );
      }
    }
  };

  std::vector<bool    > m_isCuSize;
  int                   m_numBlkSizes; // as opposed to number all sizes, which also contains grouped sizes
  std::vector<SizeType> m_sizeToIdxTab;
  std::vector<SizeType> m_idxToSizeTab;
};

class SizeIndexInfoLog2 : public SizeIndexInfo
{
public:
  SizeIndexInfoLog2(){}
  ~SizeIndexInfoLog2(){};

  void init( SizeType maxSize )
  {
    for( int i = 0, n = 0; i <= maxSize; i++ )
    {
      SizeType val = std::numeric_limits<SizeType>::max();
      if( i == ( 1 << n ) )
      {
        n++;
        val = i;
      }
      m_sizeToIdxTab.push_back( val );
    }
    SizeIndexInfo::xInit();
  }
};



#endif
