// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Copyright (C) 2013 Henner Zeller <h.zeller@acm.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 2.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://gnu.org/licenses/gpl-2.0.txt>
#ifndef RPI_RGBMATRIX_FRAMEBUFFER_INTERNAL_H
#define RPI_RGBMATRIX_FRAMEBUFFER_INTERNAL_H

#include <stdint.h>

namespace rgb_matrix {
class GPIO;
class PinPulser;
namespace internal {
// Internal representation of the frame-buffer that as well can
// write itself to GPIO.
// Our internal memory layout mimicks as much as possible what needs to be
// written out.
class Framebuffer {
public:
  Framebuffer(int rows, int columns, int parallel);
  ~Framebuffer();

  // Initialize GPIO bits for output. Only call once.
  static void InitGPIO(GPIO *io, int parallel);

  // Set PWM bits used for output. Default is 11, but if you only deal with
  // simple comic-colors, 1 might be sufficient. Lower require less CPU.
  // Returns boolean to signify if value was within range.
  bool SetPWMBits(uint8_t value);
  uint8_t pwmbits() { return pwm_bits_; }

  // Map brightness of output linearly to input with CIE1931 profile.
  void set_luminance_correct(bool on) { do_luminance_correct_ = on; }
  bool luminance_correct() const { return do_luminance_correct_; }

  // Set brightness in percent; range=1..100
  // This will only affect newly set pixels.
  void SetBrightness(uint8_t b) {
    brightness_ = (b <= 100 ? (b != 0 ? b : 1) : 100);
  }
  uint8_t brightness() { return brightness_; }

  void DumpToMatrix(GPIO *io);

  // Canvas-inspired methods, but we're not implementing this interface to not
  // have an unnecessary vtable.
  inline int width() const { return columns_; }
  inline int height() const { return height_; }
  void SetPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue);
  void Clear();
  void Fill(uint8_t red, uint8_t green, uint8_t blue);

private:
  // Map color
  inline uint16_t MapColor(uint8_t c);

  const int rows_;     // Number of rows. 16 or 32.
  const int parallel_; // Parallel rows of chains. 1 or 2.
  const int height_;   // rows * parallel
  const int columns_;  // Number of columns. Number of chained boards * 32.

  uint8_t pwm_bits_;   // PWM bits to display.
  bool do_luminance_correct_;
  uint8_t brightness_;

  const int double_rows_;
  const uint8_t row_mask_;

#if defined(ADAFRUIT_RGBMATRIX_HAT) || defined(ADAFRUIT_RGBMATRIX_HAT_PWM)
  // Adafruit made a HAT to work with this library, but it has a slightly
  // different GPIO mapping. This is this mapping. A variant of this mapping
  // allows using the Raspberry Pi PWM hardware. This requires modifying the
  // HAT to connect GPIO 4 and 18. See #else for regular mapping.
  union IoBits {
    struct {
      // This bitset reflects the GPIO mapping. The naming of the
      // pins of type 'p0_r1' means 'first parallel chain, red-bit one'
      unsigned int unused_0_3         : 4;  // 0..3
#ifdef ADAFRUIT_RGBMATRIX_HAT_PWM      
      unsigned int unused_4           : 1;  // 4
#else
      unsigned int output_enable      : 1;  // 4
#endif
      unsigned int p0_r1              : 1;  // 5
      unsigned int p0_b1              : 1;  // 6
      unsigned int unused_7_11        : 5;  // 7..11
      unsigned int p0_r2              : 1;  // 12
      unsigned int p0_g1              : 1;  // 13
      unsigned int unused_14_15       : 2;  // 14,15
      unsigned int p0_g2              : 1;  // 16
      unsigned int clock              : 1;  // 17
#ifdef ADAFRUIT_RGBMATRIX_HAT_PWM      
      unsigned int output_enable      : 1;  // 18
      unsigned int unused_19          : 1;  // 19
#else
      unsigned int unused_18_19       : 2;  // 18,19
#endif
      unsigned int d                  : 1;  // 20
      unsigned int strobe             : 1;  // 21
      unsigned int a                  : 1;  // 22
      unsigned int p0_b2              : 1;  // 23
      unsigned int unused_24_25       : 2;  // 24,25
      unsigned int b                  : 1;  // 26
      unsigned int c                  : 1;  // 27
    } bits;
    uint32_t raw;
    IoBits() : raw(0) {}
  };
#elif defined(RGB_CLASSIC_PINOUT)
  // Classic pinout before July 2015. Consider upgrading to the new pinout.
  union IoBits {
    struct {
      // This bitset reflects the GPIO mapping. The naming of the
      // pins of type 'p0_r1' means 'first parallel chain, red-bit one'
#ifdef ONLY_SINGLE_CHAIN
#     define PI_REV1_RGB_PINOUT_
      // The Revision1 and Revision2 boards have different GPIO mappings
      // on the pins 2 and 3. Just use both interpretations.
      // To keep the I2C pins free, we don't use these anymore.
      unsigned int output_enable_rev1 : 1;  // 0      (RPi 1, Revision 1)
      unsigned int clock_rev1         : 1;  // 1      (RPi 1, Revision 1)
      unsigned int output_enable_rev2 : 1;  // 2      (Pi1.Rev2; masks: I2C SDA)
      unsigned int clock_rev2         : 1;  // 3      (Pi1.Rev2; masks: I2C SCL)
#else
      unsigned int unused_0_1         : 2;  // 0..1   (only on RPi 1, Revision 1)
      unsigned int p2_g1              : 1;  // 2      (masks SDA when parallel=3)
      unsigned int p2_b1              : 1;  // 3      (masks SCL when parallel=3)
#endif
      unsigned int strobe             : 1;  // 4
      unsigned int p1_g1              : 1;  // 5      (only on A+/B+/Pi2)
      unsigned int p1_b1              : 1;  // 6      (only on A+/B+/Pi2)
      // row: 7..10, but separated as seprate bits to make it easier to shuffle
      // bits if needed.
      unsigned int a                  : 1;  // 7      (masks: SPI0_CE1)
      unsigned int b                  : 1;  // 8      (masks: SPI0_CE0)
      unsigned int c                  : 1;  // 9      (masks: SPI0_MISO)
      unsigned int d                  : 1;  // 10     (masks: SPI0_MOSI)
      unsigned int clock              : 1;  // 11     (masks: SPI0_SCKL)
      unsigned int p1_r1              : 1;  // 12     (only on A+/B+/Pi2)
      unsigned int p1_g2              : 1;  // 13     (only on A+/B+/Pi2)
      unsigned int p2_r1              : 1;  // 14     (masks TxD when parallel=3)
      unsigned int p2_r2              : 1;  // 15     (masks RxD when parallel=3)
      unsigned int unused_16          : 1;  // 16     (only on A+/B+/Pi2)
      unsigned int p0_r1              : 1;  // 17
      unsigned int p0_g1              : 1;  // 18
      unsigned int p1_r2              : 1;  // 19     (only on A+/B+/Pi2)
      unsigned int p1_b2              : 1;  // 20     (only on A+/B+/Pi2)
      unsigned int p2_b2              : 1;  // 21     (only on A+/B+/Pi2)
      unsigned int p0_b1              : 1;  // 22
      unsigned int p0_r2              : 1;  // 23
      unsigned int p0_g2              : 1;  // 24
      unsigned int p0_b2              : 1;  // 25
      unsigned int p2_g2              : 1;  // 26     (only on A+/B+/Pi2)
      unsigned int output_enable      : 1;  // 27     (Not on RPi1, Rev1)
    } bits;
    uint32_t raw;
    IoBits() : raw(0) {}
  };
#else
  // Standard pinout since July 2015
  // This uses the PWM pin to create the timing.
  union IoBits {
    struct {
      // This bitset reflects the GPIO mapping. The naming of the
      // pins of type 'p0_r1' means 'first parallel chain, red-bit one'
      //                                 GPIO Header-pos
      unsigned int unused_0_1     : 2;  //  0..1  (only on RPi 1, Revision 1)
      unsigned int p2_g1          : 1;  //  2 P1-03 (masks SDA when parallel=3)
      unsigned int p2_b1          : 1;  //  3 P1-05 (masks SCL when parallel=3)
      unsigned int strobe         : 1;  //  4 P1-07
      unsigned int p1_g1          : 1;  //  5 P1-29 (only on A+/B+/Pi2)
      unsigned int p1_b1          : 1;  //  6 P1-31 (only on A+/B+/Pi2)
      // TODO: be able to disable chain 0 for higher-pin RPis to gain SPI back.
      unsigned int p0_b1          : 1;  //  7 P1-26 (masks: SPI0_CE1)
      unsigned int p0_r2          : 1;  //  8 P1-24 (masks: SPI0_CE0)
      unsigned int p0_g2          : 1;  //  9 P1-21 (masks: SPI0_MISO
      unsigned int p0_b2          : 1;  // 10 P1-19 (masks: SPI0_MOSI)
      unsigned int p0_r1          : 1;  // 11 P1-23 (masks: SPI0_SCKL)

      unsigned int p1_r1          : 1;  // 12 P1-32 (only on A+/B+/Pi2)
      unsigned int p1_g2          : 1;  // 13 P1-33 (only on A+/B+/Pi2)
      unsigned int p2_r1          : 1;  // 14 P1-08 (masks TxD when parallel=3)
      unsigned int unused_15      : 1;  // 15 P1-10 (RxD) - kept free.
      unsigned int p2_g2          : 1;  // 16 P1-36 (only on A+/B+/Pi2)

      unsigned int clock          : 1;  // 17 P1-11

      unsigned int output_enable  : 1;  // 18 P1-12 (PWM pin: our timing)
      unsigned int p1_r2          : 1;  // 19 P1-35 (only on A+/B+/Pi2)
      unsigned int p1_b2          : 1;  // 20 P1-38 (only on A+/B+/Pi2)
      unsigned int p2_b2          : 1;  // 21 P1-40 (only on A+/B+/Pi2)

      unsigned int a              : 1;  // 22 P1-15  // row bits.
      unsigned int b              : 1;  // 23 P1-16
      unsigned int c              : 1;  // 24 P1-18
      unsigned int d              : 1;  // 25 P1-22

      unsigned int p2_r2          : 1;  // 26 P1-37 (only on A+/B+/Pi2)
      unsigned int p0_g1          : 1;  // 27 P1-13 (Not on RPi1, Rev1)
    } bits;
    uint32_t raw;
    IoBits() : raw(0) {}
  };
#endif

  // The frame-buffer is organized in bitplanes.
  // Highest level (slowest to cycle through) are double rows.
  // For each double-row, we store pwm-bits columns of a bitplane.
  // Each bitplane-column is pre-filled IoBits, of which the colors are set.
  // Of course, that means that we store unrelated bits in the frame-buffer,
  // but it allows easy access in the critical section.
  IoBits *bitplane_buffer_;
  inline IoBits *ValueAt(int double_row, int column, int bit);
};
}  // namespace internal
}  // namespace rgb_matrix
#endif // RPI_RGBMATRIX_FRAMEBUFFER_INTERNAL_H
