text: /* 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     Debug.cpp
      \brief    Defines types and objects for environment-variable-based debugging and feature control
  */
  
  #include "Debug.h"
  #include <algorithm>
  #include <math.h>
  #include "TComDataCU.h"
  #include "TComPic.h"
  #include "TComYuv.h"
  
  static const UInt settingNameWidth  = 66;
  static const UInt settingHelpWidth  = 84;
  static const UInt settingValueWidth = 3;
  
  #if DEBUG_STRING
  // these strings are used to reorder the debug output so that the encoder and decoder match.
  const TChar *debug_reorder_data_inter_token[MAX_NUM_COMPONENT+1]
   = {"Start of channel 0 inter debug\n", "Start of channel 1 inter debug\n", "Start of channel 2 inter debug\n", "End of inter residual debug\n"} ;
  const TChar *partSizeToString[NUMBER_OF_PART_SIZES]={"2Nx2N(0)", "2NxN(1)", "Nx2N(2)", "NxN(3)", "2Nx(N/2+3N/2)(4)", "2Nx(3N/2+N/2)(5)", "(N/2+3N/2)x2N(6)", "(3N/2+N/2)x2N(7)"};
  #endif
  
  // --------------------------------------------------------------------------------------------------------------------- //
  
  //EnvVar definition
  
  std::list<std::pair<std::string, std::string> > &EnvVar::getEnvVarList()
  {
    static std::list<std::pair<std::string, std::string> > varInfoList;
    return varInfoList;
  }
  
  std::list<EnvVar*> &EnvVar::getEnvVarInUse()
  {
    static std::list<EnvVar*> varInUseList;
    return varInUseList;
  }
  
  static inline Void printPair(const std::pair<std::string, std::string> &p)
  {
    if (p.second=="")
    {
      std::cout << "\n" << std::setw(settingNameWidth) << p.first << "\n" << std::endl;
    }
    else
    {
      std::cout << std::setw(settingNameWidth) << p.first << ":   " << p.second << "\n" << std::endl;
    }
  }
  
  static inline Void printVal(const EnvVar* env)
  {
    std::cout << std::setw(settingNameWidth) << env->getName() << " = " << std::setw(settingValueWidth) << env->getInt() << " (string = " << std::setw(15) << env->getString() << ")" << std::endl;
  }
  
  //static inline Bool sameEnvName( const std::pair<std::string, std::string> &a,
  //                                const std::pair<std::string, std::string> &b )
  //{
  //  // only check env name
  //  return (a.first==b.first);
  //}
  
  Void EnvVar::printEnvVar()
  {
  //  getEnvVarList().unique(sameEnvName);
    if (getEnvVarList().size()!=0)
    {
      std::cout << "--- Environment variables:\n" << std::endl;
      for_each(getEnvVarList().begin(), getEnvVarList().end(), printPair);
    }
    std::cout << std::endl;
  }
  
  Void EnvVar::printEnvVarInUse()
  {
    if (getEnvVarInUse().size()!=0)
    {
      std::cout << "RExt Environment variables set as follows: \n" << std::endl;
      for_each(getEnvVarInUse().begin(), getEnvVarInUse().end(), printVal);
    }
    std::cout << std::endl;
  }
  
  EnvVar::EnvVar(const std::string &sName, const std::string &sDefault, const std::string &sHelp) :
                                                                  m_sName(sName),
                                                                  m_sHelp(sHelp),
                                                                  m_sVal(),
                                                                  m_dVal(0),
                                                                  m_iVal(0),
                                                                  m_bSet(false)
  {
    if (getenv(m_sName.c_str()))
    {
      m_sVal = getenv(m_sName.c_str());
      m_bSet = true;
      getEnvVarInUse().push_back(this);
    }
    else
    {
      m_sVal = sDefault;
    }
  
    m_dVal = strtod(m_sVal.c_str(), 0);
    m_iVal = Int(m_dVal);
  
    getEnvVarList().push_back( std::pair<std::string, std::string>(m_sName, indentNewLines(lineWrap(splitOnSettings(m_sHelp), settingHelpWidth), (settingNameWidth + 4))) );
  }
  
  
  // --------------------------------------------------------------------------------------------------------------------- //
  
  // Debug environment variables:
  
  EnvVar Debug("-- Debugging","","");
  
  EnvVar DebugOptionList::DebugSBAC             ("DEBUG_SBAC",        "0", "Output debug data from SBAC entropy coder (coefficient data etc.)"                              );
  EnvVar DebugOptionList::DebugRQT              ("DEBUG_RQT",         "0", "Output RQT debug data from entropy coder"                                                       );
  EnvVar DebugOptionList::DebugPred             ("DEBUG_PRED",        "0", "Output prediction debug"                                                                        );
  EnvVar DebugOptionList::ForceLumaMode         ("FORCE_LUMA_MODE",   "0", "Force a particular intra direction for Luma (0-34)"                                             );
  EnvVar DebugOptionList::ForceChromaMode       ("FORCE_CHROMA_MODE", "0", "Force a particular intra direction for chroma (0-5)"                                            );
  
  #if DEBUG_STRING
  EnvVar DebugOptionList::DebugString_Structure ("DEBUG_STRUCTURE",   "0", "Produce output on chosen structure                        bit0=intra, bit1=inter");
  EnvVar DebugOptionList::DebugString_Pred      ("DEBUG_PRED",        "0", "Produce output on prediction data.                        bit0=intra, bit1=inter");
  EnvVar DebugOptionList::DebugString_Resi      ("DEBUG_RESI",        "0", "Produce output on residual data.                          bit0=intra, bit1=inter");
  EnvVar DebugOptionList::DebugString_Reco      ("DEBUG_RECO",        "0", "Produce output on reconstructed data.                     bit0=intra, bit1=inter");
  EnvVar DebugOptionList::DebugString_InvTran   ("DEBUG_INV_QT",      "0", "Produce output on inverse-quantiser and transform stages. bit0=intra, bit1=inter");
  #endif
  
  // --------------------------------------------------------------------------------------------------------------------- //
  
  //macro value printing function
  
  Void printMacroSettings()
  {
    std::cout << "Non-environment-variable-controlled macros set as follows: \n" << std::endl;
  
    //------------------------------------------------
  
    //setting macros
  
    PRINT_CONSTANT(RExt__DECODER_DEBUG_BIT_STATISTICS,                                settingNameWidth, settingValueWidth);
    PRINT_CONSTANT(RExt__HIGH_BIT_DEPTH_SUPPORT,                                      settingNameWidth, settingValueWidth);
    PRINT_CONSTANT(RExt__HIGH_PRECISION_FORWARD_TRANSFORM,                            settingNameWidth, settingValueWidth);
  
    PRINT_CONSTANT(O0043_BEST_EFFORT_DECODING,                                        settingNameWidth, settingValueWidth);
  
    PRINT_CONSTANT(ME_ENABLE_ROUNDING_OF_MVS,                                         settingNameWidth, settingValueWidth);
  
    //------------------------------------------------
  
    std::cout << std::endl;
  }
  
  
  // --------------------------------------------------------------------------------------------------------------------- //
  
  //Debugging
  
  UInt  g_debugCounter  = 0;
  
  #if DEBUG_ENCODER_SEARCH_BINS
  const UInt debugEncoderSearchBinTargetLine = 0;
  const UInt debugEncoderSearchBinWindow     = 1000000;
  #endif
  
  #if DEBUG_CABAC_BINS
  const UInt debugCabacBinTargetLine = 0;
  const UInt debugCabacBinWindow     = 1000000;
  #endif
  
  Void printSBACCoeffData(  const UInt          lastX,
                            const UInt          lastY,
                            const UInt          width,
                            const UInt          height,
                            const UInt          chan,
                            const UInt          absPart,
                            const UInt          scanIdx,
                            const TCoeff *const pCoeff,
                            const Bool          finalEncode
                          )
  {
    if (DebugOptionList::DebugSBAC.getInt()!=0 && finalEncode)
    {
      std::cout << "Size: " << width << "x" << height << ", Last X/Y: (" << lastX << ", " << lastY << "), absPartIdx: " << absPart << ", scanIdx: " << scanIdx << ", chan: " << chan << "\n";
      for (Int i=0; i<width*height; i++)
      {
        std::cout << std::setw(3) << pCoeff[i];// + dcVal;
        if (i%width == width-1)
        {
          std::cout << "\n";
        }
        else
        {
          std::cout << ",";
        }
      }
      std::cout << std::endl;
    }
  }
  
  Void printCbfArray( TComDataCU* pcCU  )
  {
    const UInt CUSizeInParts = pcCU->getWidth(0)/4;
    const UInt numValidComp=pcCU->getPic()->getNumberValidComponents();
    for (UInt ch=0; ch<numValidComp; ch++)
    {
      const ComponentID compID=ComponentID(ch);
      printf("channel: %d\n", ch);
      for (Int y=0; y<CUSizeInParts; y++)
      {
        for (Int x=0; x<CUSizeInParts; x++)
        {
          printf(x+1==CUSizeInParts?"%3d\n":"%3d, ", pcCU->getCbf(compID)[g_auiRasterToZscan[y*CUSizeInParts + x]]);
        }
      }
    }
  }
  
  UInt getDecimalWidth(const Double value)
  {
    return (value == 0) ? 1 : (UInt(floor(log10(fabs(value)))) + ((value < 0) ? 2 : 1));
                                                                 //for the minus sign
  }
  
  UInt getZScanIndex(const UInt x, const UInt y)
  {
    UInt remainingX = x;
    UInt remainingY = y;
    UInt offset     = 0;
    UInt result     = 0;
  
    while ((remainingX != 0) || (remainingY != 0))
    {
      result |= ((remainingX & 0x1) << offset) | ((remainingY & 0x1) << (offset + 1));
  
      remainingX >>= 1;
      remainingY >>= 1;
      offset      += 2;
    }
  
    return result;
  }
  
  
  // --------------------------------------------------------------------------------------------------------------------- //
  
  //String manipulation functions for aligning and wrapping printed text
  
  
  std::string splitOnSettings(const std::string &input)
  {
    std::string result = input;
  
    std::string::size_type searchFromPosition = 0;
  
    while (searchFromPosition < result.length())
    {
      //find the " = " that is used to define each setting
      std::string::size_type equalsPosition = result.find(" = ", searchFromPosition);
  
      if (equalsPosition == std::string::npos)
      {
        break;
      }
  
      //then find the end of the numeric characters
      std::string::size_type splitPosition = result.find_last_of("1234567890", equalsPosition);
  
      //then find the last space before the first numeric character...
      if (splitPosition != std::string::npos)
      {
        splitPosition = result.find_last_of(' ', splitPosition);
      }
  
      //...and replace it with a new line
      if (splitPosition != std::string::npos)
      {
        result.replace(splitPosition, 1, 1, '\n');
      }
  
      //start the next search from the end of the " = " string
      searchFromPosition = (equalsPosition + 3);
    }
  
    return result;
  }
  
  
  std::string lineWrap(const std::string &input, const UInt maximumLineLength)
  {
    if (maximumLineLength == 0)
    {
      return input;
    }
    std::string result = input;
  
    std::string::size_type lineStartPosition = result.find_first_not_of(' '); //don't wrap any leading spaces in the string
  
    while (lineStartPosition != std::string::npos)
    {
      //------------------------------------------------
  
      const std::string::size_type searchFromPosition = lineStartPosition + maximumLineLength;
  
      if (searchFromPosition >= result.length())
      {
        break;
      }
  
      //------------------------------------------------
  
      //first check to see if there is another new line character before the maximum line length
      //we can't use find for this unfortunately because it doesn't take both a beginning and an end for its search range
      std::string::size_type nextLineStartPosition = std::string::npos;
      for (std::string::size_type currentPosition = lineStartPosition; currentPosition <= searchFromPosition; currentPosition++)
      {
        if (result[currentPosition] == '\n')
        {
          nextLineStartPosition = currentPosition + 1;
          break;
        }
      }
  
      //------------------------------------------------
  
      //if there ia another new line character before the maximum line length, we need to start this loop again from that position
      if (nextLineStartPosition != std::string::npos)
      {
        lineStartPosition = nextLineStartPosition;
      }
      else
      {
        std::string::size_type spacePosition = std::string::npos;
  
        //search backwards for the last space character (must use signed Int because lineStartPosition can be 0)
        for (Int currentPosition = Int(searchFromPosition); currentPosition >= Int(lineStartPosition); currentPosition--)
        {
          if (result[currentPosition] == ' ')
          {
            spacePosition = currentPosition;
            break;
          }
        }
  
        //if we didn't find a space searching backwards, we must hyphenate
        if (spacePosition == std::string::npos)
        {
          result.insert(searchFromPosition, "-\n");
          lineStartPosition = searchFromPosition + 2; //make sure the next search ignores the hyphen
        }
        else //if we found a space to split on, replace it with a new line character
        {
          result.replace(spacePosition, 1, 1, '\n');
          lineStartPosition = spacePosition + 1;
        }
      }
  
      //------------------------------------------------
    }
  
    return result;
  }
  
  
  std::string indentNewLines(const std::string &input, const UInt indentBy)
  {
    std::string result = input;
  
    const std::string indentString(indentBy, ' ');
    std::string::size_type offset = 0;
  
    while ((offset = result.find('\n', offset)) != std::string::npos)
    {
      if ((++offset) >= result.length())
      {
        break; //increment offset so we don't find the same \n again and do no indentation at the end
      }
      result.insert(offset, indentString);
    }
  
    return result;
  }
  
  
  // --------------------------------------------------------------------------------------------------------------------- //
  
  
  Void printBlockToStream( std::ostream &ss, const TChar *pLinePrefix, TComYuv &src, const UInt numSubBlocksAcross, const UInt numSubBlocksUp, const UInt defWidth )
  {
    const UInt numValidComp=src.getNumberValidComponents();
  
    for (UInt ch=0; ch<numValidComp ; ch++)
    {
      const ComponentID compID = ComponentID(ch);
      const UInt width  = src.getWidth(compID);
      const UInt height = src.getHeight(compID);
      const UInt stride = src.getStride(compID);
      const Pel* blkSrc = src.getAddr(compID);
      const UInt subBlockWidth=width/numSubBlocksAcross;
      const UInt subBlockHeight=height/numSubBlocksUp;
  
      ss << pLinePrefix << " compID: " << compID << "\n";
      for (UInt y=0; y<height; y++)
      {
        if ((y%subBlockHeight)==0 && y!=0)
        {
          ss << pLinePrefix << '\n';
        }
  
        ss << pLinePrefix;
        for (UInt x=0; x<width; x++)
        {
          if ((x%subBlockWidth)==0 && x!=0)
          {
            ss << std::setw(defWidth+2) << "";
          }
  
          ss << std::setw(defWidth) << blkSrc[y*stride + x] << ' ';
        }
        ss << '\n';
      }
      ss << pLinePrefix << " --- \n";
    }
  }
  
  #if DEBUG_STRING
  Int DebugStringGetPredModeMask(PredMode mode)
  {
    return (mode==MODE_INTRA)?1:2;
  }
  
  Void DebugInterPredResiReco(std::string &sDebug, TComYuv &pred, TComYuv &resi, TComYuv &reco, Int predmode_mask)
  {
    if (DebugOptionList::DebugString_Pred.getInt()&predmode_mask)
    {
      std::stringstream ss(std::stringstream::out);
      printBlockToStream(ss, "###inter-pred: ", pred);
      std::string debugTmp;
      debugTmp=ss.str();
      sDebug=debugTmp+sDebug;
    }
    if (DebugOptionList::DebugString_Resi.getInt()&predmode_mask)
    {
      std::stringstream ss(std::stringstream::out);
      printBlockToStream(ss, "###inter-resi: ", resi);
      sDebug+=ss.str();
    }
    if (DebugOptionList::DebugString_Reco.getInt()&predmode_mask)
    {
      std::stringstream ss(std::stringstream::out);
      printBlockToStream(ss, "###inter-reco: ", reco);
      sDebug+=ss.str();
    }
  }
  #endif
  