/*
TJpg_Decoder.cpp

Created by Bodmer 18/10/19
Modified by Blueforcer 02/23

Latest version here:
https://github.com/Bodmer/TJpg_Decoder
*/

#include "TJpg_Decoder.h"

// Create a class instance to be used by the sketch (defined as extern in header)
TJpg_Decoder TJpgDec;

/***************************************************************************************
** Function name:           TJpg_Decoder
** Description:             Constructor
***************************************************************************************/
TJpg_Decoder::TJpg_Decoder()
{
  // Setup a pointer to this class for static functions
  thisPtr = this;
}

/***************************************************************************************
** Function name:           ~TJpg_Decoder
** Description:             Destructor
***************************************************************************************/
TJpg_Decoder::~TJpg_Decoder()
{
  // Bye
}

/***************************************************************************************
** Function name:           setJpgScale
** Description:             Set the reduction scale factor (1, 2, 4 or 8)
***************************************************************************************/
void TJpg_Decoder::setSwapBytes(bool swapBytes)
{
  _swap = swapBytes;
}

/***************************************************************************************
** Function name:           setJpgScale
** Description:             Set the reduction scale factor (1, 2, 4 or 8)
***************************************************************************************/
void TJpg_Decoder::setJpgScale(uint8_t scaleFactor)
{
  switch (scaleFactor)
  {
  case 1:
    jpgScale = 0;
    break;
  case 2:
    jpgScale = 1;
    break;
  case 4:
    jpgScale = 2;
    break;
  case 8:
    jpgScale = 3;
    break;
  default:
    jpgScale = 0;
  }
}

/***************************************************************************************
** Function name:           setCallback
** Description:             Set the sketch callback function to render decoded blocks
***************************************************************************************/
void TJpg_Decoder::setCallback(SketchCallback sketchCallback)
{
  tft_output = sketchCallback;
}

/***************************************************************************************
** Function name:           jd_input (declared static)
** Description:             Called by tjpgd.c to get more data
***************************************************************************************/
unsigned int TJpg_Decoder::jd_input(JDEC *jdec, uint8_t *buf, unsigned int len)
{
  TJpg_Decoder *thisPtr = TJpgDec.thisPtr;
  jdec = jdec; // Supress warning

  // Handle an array input
  if (thisPtr->jpg_source == TJPG_ARRAY)
  {
    // Avoid running off end of array
    if (thisPtr->array_index + len > thisPtr->array_size)
    {
      len = thisPtr->array_size - thisPtr->array_index;
    }

    // If buf is valid then copy len bytes to buffer
    if (buf)
      memcpy_P(buf, (const uint8_t *)(thisPtr->array_data + thisPtr->array_index), len);

    // Move pointer
    thisPtr->array_index += len;
  }

#ifdef TJPGD_LOAD_FFS
  // Handle SPIFFS input
  else if (thisPtr->jpg_source == TJPG_FS_FILE)
  {
    // Check how many bytes are available
    uint32_t bytesLeft = thisPtr->jpgFile.available();
    if (bytesLeft < len)
      len = bytesLeft;

    if (buf)
    {
      // Read into buffer, pointer moved as well
      thisPtr->jpgFile.read(buf, len);
    }
    else
    {
      // Buffer is null, so skip data by moving pointer
      thisPtr->jpgFile.seek(thisPtr->jpgFile.position() + len);
    }
  }
#endif

#if defined(TJPGD_LOAD_SD_LIBRARY)
  // Handle SD library input
  else if (thisPtr->jpg_source == TJPG_SD_FILE)
  {
    // Check how many bytes are available
    uint32_t bytesLeft = thisPtr->jpgSdFile.available();
    if (bytesLeft < len)
      len = bytesLeft;

    if (buf)
    {
      // Read into buffer, pointer moved as well
      thisPtr->jpgSdFile.read(buf, len);
    }
    else
    {
      // Buffer is null, so skip data by moving pointer
      thisPtr->jpgSdFile.seek(thisPtr->jpgSdFile.position() + len);
    }
  }
#endif

  return len;
}

/***************************************************************************************
** Function name:           jd_output (declared static)
** Description:             Called by tjpgd.c with an image block for rendering
***************************************************************************************/
// Pass image block back to the sketch for rendering, may be a complete or partial MCU
int TJpg_Decoder::jd_output(JDEC *jdec, void *bitmap, JRECT *jrect)
{

  TJpg_Decoder *thisPtr = TJpgDec.thisPtr;
  jdec = jdec; // Supress warning as ID is not used

  int16_t x = jrect->left + thisPtr->jpeg_x;
  int16_t y = jrect->top + thisPtr->jpeg_y;
  uint16_t w = jrect->right + 1 - jrect->left;
  uint16_t h = jrect->bottom + 1 - jrect->top;
  // Pass the image block and rendering parameters in a callback to the sketch
  return thisPtr->tft_output(x, y, w, h, (uint16_t *)bitmap);
}

/***************************************************************************************
** Function name:           drawFsJpg für AWTRIX
** Description:             Draw a jpg with opened file handle at x,y
***************************************************************************************/
JRESULT TJpg_Decoder::drawFsJpg(int32_t x, int32_t y, fs::File inFile)
{

  JDEC jdec;
  JRESULT jresult = JDR_OK;

  jpg_source = TJPG_FS_FILE;
  jpeg_x = x;
  jpeg_y = y;

  jdec.swap = _swap;

  jpgFile = inFile;

  jresult = jd_prepare(&jdec, jd_input, workspace, TJPGD_WORKSPACE_SIZE, (unsigned int)0);

  // Extract image and render
  if (jresult == JDR_OK)
  {
    jresult = jd_decomp(&jdec, jd_output, jpgScale);
  }
  inFile.seek(0);
  return jresult;
}
