/// ****************************************************************************
/// @file   
/// @author Dr.Neeton
/// @date   2014/04/22 - 2015/11/11
/// 
/// @brief  イメージデータ
/// 
/// ****************************************************************************

#include "CPL_Core_Image.h"

namespace CPL {
namespace Core {


// _____________________________________________________________________________
/// @copydoc Image_i::GetBitCount
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int ImageData::GetBitCount() const
{
  switch( m_format )
  {
  case eFormat::None  : return 0;
  case eFormat::Gray8 : return 8;
  case eFormat::Gray16: return 16;
  case eFormat::GrayF : return 32;
  case eFormat::Rgb8  : return 8 * 3;
  case eFormat::Rgb16 : return 16 * 3;
  case eFormat::RgbF  : return 32 * 3;
  case eFormat::Rgba8 : return 8 * 4;
  case eFormat::Rgba16: return 16 * 4;
  case eFormat::RgbaF : return 32 * 4;
  }

  return 0;
}

// _____________________________________________________________________________
/// @copydoc Image_i::GetChannelNum
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int ImageData::GetChannelNum() const
{
  return (int)m_channel_tbl.size();
}


// _____________________________________________________________________________
/// @copydoc Image_i::Create(_in<CreateInfo>)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
U64 ImageData::Create( _in<CreateInfo> in_create_info )
{
  m_width  = in_create_info.width;
  m_height = in_create_info.height;
  U64  const length = m_width * m_height;
  auto const color  = in_create_info.color;
  void_cptr  src    = in_create_info.data;
  U8_cptr    src8   = (U8*)src;
  U16_cptr   src16  = (U16*)src;
  float_cptr srcF   = (float*)src;

  switch( in_create_info.format )
  {
  case eFormat::Gray8 :
  case eFormat::Gray16:
  case eFormat::GrayF :
    m_format = eFormat::Gray16; // Gray16固定。
    m_channel_tbl.resize( 1 );
    m_channel_tbl[0].resize( static_cast<size_t>(length) );
    if( src )
    {
      switch( in_create_info.format )
      {
      case eFormat::Gray8 : for( auto& dest : m_channel_tbl[0] ){ dest = *src8++; } break;
      case eFormat::Gray16: for( auto& dest : m_channel_tbl[0] ){ dest = *src16++; } break;
      case eFormat::GrayF : for( auto& dest : m_channel_tbl[0] ){ dest = *srcF++; } break;
      default: break;
      }
    }
    else
    {
      //CPL_LOOP2( length, i )
      for( size_t i = 0; i < static_cast<size_t>(length); ++i )
      {
        m_channel_tbl[0][i]
          = color.r * 0.299f
          + color.g * 0.587f
          + color.b * 0.114f;
      }
    }
    break;

  case eFormat::Rgb8  :
  case eFormat::Rgb16 :
  case eFormat::RgbF  :
  case eFormat::Rgba8 :
  case eFormat::Rgba16:
  case eFormat::RgbaF :
    switch( in_create_info.format )
    {
    case eFormat::Rgb8 :
    case eFormat::Rgb16:
    case eFormat::RgbF :
      m_format = eFormat::Rgb16; // Rgb16固定。
      m_channel_tbl.resize( 3 );
      break;
    case eFormat::Rgba8 :
    case eFormat::Rgba16:
    case eFormat::RgbaF :
      m_format = eFormat::Rgba16; // Rgba16固定。
      m_channel_tbl.resize( 4 );
      break;
    default:
      break;
    }
    // 
    for( auto& ic : m_channel_tbl )
    {
      ic.resize( static_cast<size_t>(length) );
    }
    //
    if( src )
    {
      switch( in_create_info.format )
      {
      case eFormat::Rgb8:
      case eFormat::Rgba8:
        //CPL_LOOP2( length, i )
        for( size_t i = 0; i < static_cast<size_t>(length); ++i )
        {
          //CPL_LOOP2( m_channel_tbl.size(), j )
          for( size_t j = 0; j < m_channel_tbl.size(); ++j)
          {
            m_channel_tbl[j][i] = src8[0];
          }
          src8 += m_channel_tbl.size();
        }
        break;
      case eFormat::Rgb16:
      case eFormat::Rgba16:
        //CPL_LOOP2( length, i )
        for( size_t i = 0; i < static_cast<size_t>(length); ++i )
        {
          //CPL_LOOP2( m_channel_tbl.size(), j )
          for( size_t j = 0; j < m_channel_tbl.size(); ++j )
          {
            m_channel_tbl[j][i] = src16[0];
          }
          src16 += m_channel_tbl.size();
        }
        break;
      case eFormat::RgbF:
      case eFormat::RgbaF:
        //CPL_LOOP2( length, i )
        for( size_t i = 0; i < static_cast<size_t>(length); ++i )
        {
          //CPL_LOOP2( m_channel_tbl.size(), j )
          for( size_t j = 0; j < m_channel_tbl.size(); ++j )
          {
            m_channel_tbl[j][i] = srcF[0];
          }
          srcF += m_channel_tbl.size();
        }
        break;
      default:
        break;
      }
    }
    else
    {
      //CPL_LOOP2( length, i )
      for( size_t i = 0; i < static_cast<size_t>(length); ++i )
      {
        CPL_LOOP2( m_channel_tbl.size(), j )
        {
          m_channel_tbl[j][i] = color.p[j];
        }
      }
    }
    break;

  default:
    return 0;
  }

  return sizeof(m_channel_tbl[0][0]) * length * m_channel_tbl.size();
}


//// _____________________________________________________________________________
///// @copydoc Image_i::Create(pin<void>,_in<size_t>)
//// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//size_t ImageData::Create( pin<void> in_ptr_buf, _in<size_t> in_size )
//{
//  return 0; // 非対応。
//}


// _____________________________________________________________________________
/// @copydoc Image_i::Clone
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool ImageData::Clone( rout<Image_i> out_image ) const
{
  try {
    if( auto p = dynamic_cast<ImageData*>(&out_image) )
    {
      p->m_format      = m_format;
      p->m_width       = m_width;
      p->m_height      = m_height;
      p->m_channel_tbl = m_channel_tbl;
      return true;
    }
    return false;
  }
  catch(...)
  {
    return false;
  }
}


// _____________________________________________________________________________
/// @copydoc Image_i::Output(rout<ImageChannel>,_in<int>)const
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t ImageData::Output( rout<ImageChannel> out_imgch, _in<int> in_ch_No ) const
{
  if( in_ch_No < (int)m_channel_tbl.size() )
  {
    out_imgch = m_channel_tbl[ in_ch_No ];
    return out_imgch.size();
  }
  return 0;
}

// _____________________________________________________________________________
/// @copydoc Image_i::Output(rout<std::vector<ImageChannel>>)const
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
size_t ImageData::Output( rout<std::vector<ImageChannel>> out_imgch_mat ) const
{
  try
  {
    out_imgch_mat = m_channel_tbl;

    return
      out_imgch_mat.size()
        ? (out_imgch_mat[0].size()
            ? out_imgch_mat[0].size() * sizeof(out_imgch_mat[0][0])
            : 0
          )
        : 0;
  }
  catch(...)
  {
    return 0;
  }
}


// _____________________________________________________________________________
/// @copydoc Image_i::Resize
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool
  ImageData::Resize
    ( _in<int     > in_W
    , _in<int     > in_H
    , _in<A8R8G8B8> in_color
    )
{
  if( m_width == in_W  &&  m_height == in_H )
  {
    return false;
  }

  try
  {
    for( auto& ic : m_channel_tbl )
    {
      CPL_LOOP2( Min<int>(in_H, m_height), y )
      {
        CPL_LOOP2( Min<int>(in_W, m_width), x )
        {
          ic[ y * m_width + x ] = ic[ y * in_W + x ];
        }
      }
    }
    // 変更後の値を設定。
    m_width  = in_W;
    m_height = in_H;

    return true;
  }
  catch(...)
  {
    return false;
  }
}



} // namespace Core
} // namespace CPL
