/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */

/* libmwaw
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
* Copyright (C) 2006, 2007 Andrew Ziem
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/

/* This header contains code specific to some bitmap
 */

#ifndef MWAW_PICT_BITMAP
#  define MWAW_PICT_BITMAP


#include <vector>

#include "libmwaw_internal.hxx"
#include "MWAWDebug.hxx"
#include "MWAWPict.hxx"

////////////////////////////////////////////////////////////
//
//   Some container
//
////////////////////////////////////////////////////////////

/** \brief a template class to store a 2D array of m_data */
template <class T> class MWAWPictBitmapContainer
{
public:
  //! constructor given size
  explicit MWAWPictBitmapContainer(MWAWVec2i const &sz)
    : m_size(sz)
    , m_data(nullptr)
  {
    if (m_size[0]*m_size[1] == 0) return;
    m_data = new T[size_t(m_size[0]*m_size[1])];
    std::uninitialized_fill_n(m_data, m_size[0] * m_size[1], T());
  }
  //! destructor
  virtual ~MWAWPictBitmapContainer()
  {
    if (m_data) delete [] m_data;
  }

  //! returns ok, if the m_data is allocated
  bool ok() const
  {
    return (m_data != nullptr);
  }

  //! a comparison operator
  int cmp(MWAWPictBitmapContainer<T> const &orig) const
  {
    int diff = m_size.cmpY(orig.m_size);
    if (diff) return diff;
    if (!m_data) return orig.m_data ? 1 : 0;
    if (!orig.m_data) return -1;
    for (int i=0; i < m_size[0]*m_size[1]; i++) {
      if (m_data[i] < orig.m_data[i]) return -1;
      if (m_data[i] > orig.m_data[i]) return 1;
    }
    return 0;
  }
  //! return the array size
  MWAWVec2i const &size() const
  {
    return m_size;
  }
  //! gets the number of row
  int numRows() const
  {
    return m_size[0];
  }
  //! gets the number of column
  int numColumns() const
  {
    return m_size[1];
  }

  //! accessor of a cell m_data
  T const &get(int i, int j) const
  {
    if (m_data == nullptr || i<0 || i >= m_size[0] || j<0 || j >= m_size[1])
      throw libmwaw::GenericException();
    return m_data[i+m_size[0]*j];
  }
  //! accessor of a row m_data
  T const *getRow(int j) const
  {
    if (m_data == nullptr || j<0 || j >= m_size[1])
      throw libmwaw::GenericException();
    return m_data+m_size[0]*j;
  }

  //! sets a cell m_data
  void set(int i, int j, T const &v)
  {
    if (m_data == nullptr || i<0 || i >= m_size[0] || j<0 || j >= m_size[1]) {
      MWAW_DEBUG_MSG(("MWAWPictBitmapContainer::set: call with bad coordinate %d %d\n", i, j));
      return;
    }
    m_data[i+j*m_size[0]] = v;
  }

  //! sets a line of m_data
  template <class U>
  void setRow(int j, U const *val)
  {
    if (m_data == nullptr || j<0 || j >= m_size[1]) {
      MWAW_DEBUG_MSG(("MWAWPictBitmapContainer::setRow: call with bad coordinate %d\n", j));
      return;
    }
    for (int i = 0, ind=j*m_size[0]; i < m_size[0]; i++, ind++) m_data[ind] = T(val[i]);
  }

  //! sets a column of m_data
  template <class U>
  void setColumn(int i, U const *val)
  {
    if (m_data == nullptr || i<0 || i >= m_size[0]) {
      MWAW_DEBUG_MSG(("MWAWPictBitmapContainer::setColumn: call with bad coordinate %d\n", i));
      return;
    }
    for (int j = 0, ind=i; j < m_size[1]; j++, ind+=m_size[0]) m_data[ind] = T(val[i]);
  }

private:
  MWAWPictBitmapContainer(MWAWPictBitmapContainer const &orig) = delete;
  MWAWPictBitmapContainer &operator=(MWAWPictBitmapContainer const &orig) = delete;
protected:
  //! the size
  MWAWVec2i m_size;
  //! the m_data placed by row ie. d_00, d_10, ... , d_{X-1}0, ..
  T *m_data;
};

//! a bool container with a function to put packed row
class MWAWPictBitmapContainerBool final : public MWAWPictBitmapContainer<bool>
{
public:
  //! constructor
  explicit MWAWPictBitmapContainerBool(MWAWVec2i const &sz)
    : MWAWPictBitmapContainer<bool>(sz)
  {
  }
  //! destructor
  ~MWAWPictBitmapContainerBool() final;
  //! a comparison operator
  int cmp(MWAWPictBitmapContainerBool const &orig) const
  {
    int diff = m_size.cmpY(orig.m_size);
    if (diff) return diff;
    if (!m_data) return orig.m_data ? 1 : 0;
    if (!orig.m_data) return -1;
    for (int i=0; i < m_size[0]*m_size[1]; i++) {
      if (m_data[i] == orig.m_data[i]) continue;
      return m_data[i] ? 1 : -1;
    }
    return 0;
  }

  //! allows to use packed m_data
  void setRowPacked(int j, unsigned char const *val, unsigned char const *end)
  {
    if (m_data == nullptr || j<0 || j >= m_size[1] || val >= end) {
      MWAW_DEBUG_MSG(("MWAWPictBitmapContainerBool::setRowPacked: call with bad coordinate %d\n", j));
      return;
    }
    for (int i = 0, ind = j*m_size[0]; i < m_size[0];) {
      unsigned char v = (val < end) ? *(val++) : 0;
      unsigned char mask = 0x80;
      for (int p = 0; p < 8 && i < m_size[0]; i++, p++, ind++) {
        m_data[ind] = ((v&mask) != 0);
        mask = static_cast<unsigned char>(mask >> 1);
      }
    }
  }
};

//! Generic class used to construct bitmap
class MWAWPictBitmap : public MWAWPict
{
public:
  //! destructor
  ~MWAWPictBitmap() override;

  //! the picture subtype: blackwhite, indexed, color
  enum SubType { BW, Indexed, Color };
  //! returns the picture type
  Type getType() const override
  {
    return MWAWPict::Bitmap;
  }
  //! returns the picture subtype
  virtual SubType getSubType() const = 0;

  //! returns the final picture
  bool getBinary(MWAWEmbeddedObject &picture) const override
  {
    if (!valid()) return false;

    librevenge::RVNGBinaryData data;
    createFileData(data);
    picture=MWAWEmbeddedObject(data, "image/pict");
    return true;
  }

  //! returns true if the picture is valid
  virtual bool valid() const
  {
    return false;
  }

  /** a virtual function used to obtain a strict order,
  must be redefined in the subs class */
  int cmp(MWAWPict const &a) const override
  {
    int diff = MWAWPict::cmp(a);
    if (diff) return diff;
    auto const &aPict = static_cast<MWAWPictBitmap const &>(a);

    // the type
    diff = getSubType() - aPict.getSubType();
    if (diff) return (diff < 0) ? -1 : 1;

    return 0;
  }

protected:
  //! abstract function which creates the result file
  virtual bool createFileData(librevenge::RVNGBinaryData &result) const = 0;

  //! protected constructor: use check to construct a picture
  explicit MWAWPictBitmap(MWAWVec2i const &sz)
  {
    setBdBox(MWAWBox2f(MWAWVec2f(0,0), MWAWVec2f(sz)));
  }
};

/** a bitmap of bool to store black-white bitmap */
class MWAWPictBitmapBW final : public MWAWPictBitmap
{
public:
  //! returns the picture subtype
  SubType getSubType() const final
  {
    return BW;
  }

  /** a virtual function used to obtain a strict order,
  must be redefined in the subs class */
  int cmp(MWAWPict const &a) const final
  {
    int diff = MWAWPictBitmap::cmp(a);
    if (diff) return diff;
    auto const &aPict = static_cast<MWAWPictBitmapBW const &>(a);

    return m_data.cmp(aPict.m_data);
  }

  //! returns true if the picture is valid
  bool valid() const final
  {
    return m_data.ok();
  }

  //! the constructor
  explicit MWAWPictBitmapBW(MWAWVec2i const &sz)
    : MWAWPictBitmap(sz)
    , m_data(sz)
  {
  }

  //! the picture size
  MWAWVec2i const &size() const
  {
    return m_data.size();
  }
  //! the number of rows
  int numRows() const
  {
    return m_data.numRows();
  }
  //! the number of columns
  int numColumns() const
  {
    return m_data.numColumns();
  }
  //! returns a cell content
  bool get(int i, int j) const
  {
    return m_data.get(i,j);
  }
  //! returns the cells content of a row
  bool const *getRow(int j) const
  {
    return m_data.getRow(j);
  }
  //! sets a cell contents
  void set(int i, int j, bool v)
  {
    m_data.set(i,j, v);
  }
  //! sets all cell contents of a row
  void setRow(int j, bool const *val)
  {
    m_data.setRow(j, val);
  }
  //! sets all cell contents of a row given packed m_data
  void setRowPacked(int j, unsigned char const *val, unsigned char const *end)
  {
    m_data.setRowPacked(j, val, end);
  }
  //! sets all cell contents of a column
  void setColumn(int i, bool const *val)
  {
    m_data.setColumn(i, val);
  }

protected:
  //! function which creates the result file
  bool createFileData(librevenge::RVNGBinaryData &result) const final;

  //! the data
  MWAWPictBitmapContainerBool m_data;
};

/** a bitmap of int to store indexed bitmap */
class MWAWPictBitmapIndexed final : public MWAWPictBitmap
{
public:
  //! return the picture subtype
  SubType getSubType() const final
  {
    return Indexed;
  }

  /** a virtual function used to obtain a strict order,
  must be redefined in the subs class */
  int cmp(MWAWPict const &a) const final
  {
    int diff = MWAWPictBitmap::cmp(a);
    if (diff) return diff;
    auto const &aPict = static_cast<MWAWPictBitmapIndexed const &>(a);

    diff=int(m_colors.size())-int(aPict.m_colors.size());
    if (diff) return (diff < 0) ? -1 : 1;
    for (size_t c=0; c < m_colors.size(); c++) {
      if (m_colors[c] < aPict.m_colors[c])
        return 1;
      if (m_colors[c] > aPict.m_colors[c])
        return -1;
    }
    return m_data.cmp(aPict.m_data);
  }

  //! returns true if the picture is valid
  bool valid() const final
  {
    return m_data.ok();
  }

  //! the constructor
  explicit MWAWPictBitmapIndexed(MWAWVec2i const &sz)
    : MWAWPictBitmap(sz)
    , m_data(sz)
    , m_colors()
  {
  }

  //! the picture size
  MWAWVec2i const &size() const
  {
    return m_data.size();
  }
  //! the number of rows
  int numRows() const
  {
    return m_data.numRows();
  }
  //! the number of columns
  int numColumns() const
  {
    return m_data.numColumns();
  }
  //! returns a cell content
  int get(int i, int j) const
  {
    return m_data.get(i,j);
  }
  //! returns the cells content of a row
  int const *getRow(int j) const
  {
    return m_data.getRow(j);
  }

  //! sets a cell contents
  void set(int i, int j, int v)
  {
    m_data.set(i,j, v);
  }
  //! sets all cell contents of a row
  template <class U> void setRow(int j, U const *val)
  {
    m_data.setRow(j, val);
  }
  //! sets all cell contents of a column
  template <class U> void setColumn(int i, U const *val)
  {
    m_data.setColumn(i, val);
  }

  //! returns the array of indexed colors
  std::vector<MWAWColor> const &getColors() const
  {
    return m_colors;
  }
  //! sets the array of indexed colors
  void setColors(std::vector<MWAWColor> const &cols)
  {
    m_colors = cols;
  }

protected:
  //! the function which creates the result file
  bool createFileData(librevenge::RVNGBinaryData &result) const final;

  //! the m_data
  MWAWPictBitmapContainer<int> m_data;
  //! the colors
  std::vector<MWAWColor> m_colors;
};

/** a bitmap of MWAWColor to store true color bitmap

    \note: this class is actually the only class which can create
    bitmap with transparency (by creating a BMP), but as
    LibreOffice/OpenOffice seem to ignore the alpha channel when
    importing BMP pictures...
 */
class MWAWPictBitmapColor final : public MWAWPictBitmap
{
public:
  //! return the picture subtype
  SubType getSubType() const final
  {
    return Indexed;
  }

  /** a virtual function used to obtain a strict order,
  must be redefined in the subs class */
  int cmp(MWAWPict const &a) const final
  {
    int diff = MWAWPictBitmap::cmp(a);
    if (diff) return diff;
    auto const &aPict = static_cast<MWAWPictBitmapColor const &>(a);

    return m_data.cmp(aPict.m_data);
  }

  //! returns true if the picture is valid
  bool valid() const final
  {
    return m_data.ok();
  }

  //! the constructor
  MWAWPictBitmapColor(MWAWVec2i const &sz, bool useAlphaChannel=false)
    : MWAWPictBitmap(sz)
    , m_data(sz)
    , m_hasAlpha(useAlphaChannel)
  {
  }

  //! the picture size
  MWAWVec2i const &size() const
  {
    return m_data.size();
  }
  //! the number of rows
  int numRows() const
  {
    return m_data.numRows();
  }
  //! the number of columns
  int numColumns() const
  {
    return m_data.numColumns();
  }
  //! returns a cell content
  MWAWColor get(int i, int j) const
  {
    return m_data.get(i,j);
  }
  //! returns the cells content of a row
  MWAWColor const *getRow(int j) const
  {
    return m_data.getRow(j);
  }

  //! sets a cell contents
  void set(int i, int j, MWAWColor const &v)
  {
    m_data.set(i,j, v);
  }
  //! sets all cell contents of a row
  void setRow(int j, MWAWColor const *val)
  {
    m_data.setRow(j, val);
  }
  //! sets all cell contents of a column
  void setColumn(int i, MWAWColor const *val)
  {
    m_data.setColumn(i, val);
  }

protected:
  //! the function which creates the result file
  bool createFileData(librevenge::RVNGBinaryData &result) const final;

  //! the data
  MWAWPictBitmapContainer<MWAWColor> m_data;

  //! true if the bitmap has alpha color
  bool m_hasAlpha;
};
#endif
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab:
