#include "cmGlobal.h"
#include "cmFloatTypes.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmGr.h"
#include "cmGrDevCtx.h"
#include "cmGrPlot.h"

#include "cmAudioFile.h"
#include "cmAudioFileMgr.h"
#include "cmGrPlotAudio.h"

typedef struct
{
  cmGrPlObjH_t       oH;
  cmAfmFileH_t       afH;
  unsigned           chIdx;

  cmGrRenderObjCb_t  renderCbFunc;
  void*              renderCbArg;

  cmGrDestroyObjCb_t destroyCbFunc;  
  void*              destroyCbArg;

  cmGrIsInsideObjCb_t isInsideCbFunc;
  void*               isInsideCbArg;

  void*       mem;   //
  cmGrPExt_t  pext;  // extents of the visible portion of this audio object
  unsigned    pixN;  // count of pixel columns used by this audio object
  cmSample_t* fMinV; // fMinV[pixN] = min sample value for each visible column
  cmSample_t* fMaxV; // fMaxV[pixN] = max sample value for each visible column
  int*        iMinV; // iMinV[pixN] = top pixel for each column line 
  int*        iMaxV; // iMaxV[pixN] = bottom pixel for each column line

} cmGrPlObjAf_t;

cmGrPlRC_t _cmGrPlObjAfCalcImage( cmGrPlObjAf_t* op, cmGrH_t grH )
{
  cmGrPlRC_t rc     = kOkGrPlRC;
  cmGrObjH_t grObjH = cmGrPlotObjHandle(op->oH);
  cmGrVExt_t vwExt,objExt,drExt;

  // get the intersection of the view and this audio object
  cmGrViewExtents( grH, &vwExt );
  cmGrPlotObjVExt( op->oH, &objExt );
  cmGrVExtIntersect(&drExt,&vwExt,&objExt);

  // if the audio object is visible
  if( cmGrVExtIsNullOrEmpty(&drExt) )
  {
    cmGrPExtSetNull(&op->pext);
    op->pixN = 0;
  }
  else
  {
    // get the extents of the visible portion of the audio object
    cmGrVExt_VtoP( grH, cmGrPlotObjHandle(op->oH), &drExt, &op->pext);

    // store the count of horizontal pixels
    op->pixN    = op->pext.sz.w;

    // allocate a cache to hold the image data
    unsigned byteCnt = op->pixN * 2 * sizeof(int) + op->pixN * 2 * sizeof(cmSample_t);
    op->mem   = cmMemResize(char,op->mem,byteCnt);
    op->fMinV = (cmSample_t*)op->mem;
    op->fMaxV = op->fMinV + op->pixN;
    op->iMinV = (int*)(op->fMaxV + op->pixN);
    op->iMaxV = op->iMinV + op->pixN;
    assert( op->iMaxV + op->pixN == op->mem + byteCnt );

    // locate the offset into the file of the first sample to be displayed
    unsigned si = 0;
    if( drExt.loc.x > objExt.loc.x )
      si = drExt.loc.x - objExt.loc.x;

   
    // get the floating point audio summary signal
    if( cmAfmFileGetSummary( op->afH, op->chIdx, si, drExt.sz.w, op->fMinV, op->fMaxV, op->pixN ) != kOkAfmRC )
    {
      const cmChar_t* afn = cmAudioFileName( cmAfmFileHandle(op->afH));
      rc = cmErrMsg( cmGrPlotErr( cmGrPlotObjMgrHandle(op->oH) ), kRsrcFailGrPlRC, "Audio file summary read failure on '%s'.",afn);
      goto errLabel;
    }

    unsigned i;
    // convert the summary to pixels values
    for(i=0; i<op->pixN; ++i)
    {
      // Note the reversal of min and max during the conversion.
      op->iMaxV[i] = cmGrY_VtoP( grH, grObjH, op->fMinV[i] );
      op->iMinV[i] = cmGrY_VtoP( grH, grObjH, op->fMaxV[i] );
    }
  }
 errLabel:
  return rc;
}


bool _cmGrPlObjAfRender(   cmGrObjFuncArgs_t* args, cmGrDcH_t dcH )
{
  cmGrPlObjAf_t* op = (cmGrPlObjAf_t*)args->cbArg;

  if( _cmGrPlObjAfCalcImage(op, args->grH ) == kOkGrPlRC )
  {
    int i;
    cmGrPExt_t pext;
    cmGrPhysExtents( args->grH, &pext);

    cmGrDcSetColor(dcH, cmGrPlotObjCurLineColor(op->oH));

    // draw a horz line at y=0 
    int y0 = cmGrY_VtoP( args->grH, cmGrPlotObjHandle(op->oH), 0.0 );
    cmGrDcDrawLine(dcH, cmGrPExtL(&op->pext), y0, cmGrPExtR(&op->pext) , y0 );

    // draw a vertical line for each 
    for(i=0; i<op->pixN; ++i)
      cmGrDcDrawLine(dcH, op->pext.loc.x+i, op->iMinV[i], op->pext.loc.x+i, op->iMaxV[i] );        

    // draw a rectangle around the entire audio clip
    cmGrDcDrawRect(dcH, op->pext.loc.x, cmGrPExtT(&pext),  op->pext.sz.w, cmGrPExtB(&pext) );
    
    // draw the file label 
    cmGrDcDrawTextJustify( dcH, cmGrPlotObjFontFamily(op->oH), cmGrPlotObjFontSize(op->oH), cmGrPlotObjFontStyle(op->oH), cmGrPlotObjLabel(op->oH), &op->pext, kHorzCtrJsGrFl | kTopJsGrFl );

  }
  return true;
}

bool  _cmGrPlObjAfIsInside( cmGrObjFuncArgs_t* args, unsigned evtFlags, int px, int py, cmGrV_t vx, cmGrV_t vy )
{
  cmGrPlObjAf_t* op = (cmGrPlObjAf_t*)args->cbArg;

  if( cmGrPExtIsXyInside( &op->pext, px, py ) )
  {
    px -= op->pext.loc.x;
    if( 0 <= px && px < op->pixN )
      return op->iMinV[px] <= py && py <= op->iMaxV[px]; 
      
  }

  return false;
}

void _cmGrPlObjAfDestroy( cmGrObjFuncArgs_t* args )
{
  cmGrPlObjAf_t* op = (cmGrPlObjAf_t*)args->cbArg;
  args->cbArg = op->destroyCbArg;
  op->destroyCbFunc(args);  
  cmMemFree(op->mem);
  cmMemFree(op);
}

cmGrPlRC_t  cmGrPlotAudioFileObjCreate(
  cmGrPlObjH_t oH,
  cmAfmFileH_t afH,
  unsigned     audioChIdx )
{
  cmGrPlObjAf_t* op = cmMemAllocZ(cmGrPlObjAf_t,1);
  op->oH  = oH;
  op->afH = afH;
  op->chIdx = audioChIdx;

  cmGrObjH_t grObjH = cmGrPlotObjHandle(op->oH);

  op->renderCbFunc = cmGrObjRenderCbFunc(grObjH);
  op->renderCbArg  = cmGrObjRenderCbArg(grObjH);
  cmGrObjSetRenderCb( grObjH, _cmGrPlObjAfRender, op );

  op->destroyCbFunc = cmGrObjDestroyCbFunc(grObjH);
  op->destroyCbArg  = cmGrObjDestroyCbArg(grObjH);
  cmGrObjSetDestroyCb( grObjH, _cmGrPlObjAfDestroy, op );

  op->isInsideCbFunc = cmGrObjIsInsideCbFunc(grObjH);
  op->isInsideCbArg  = cmGrObjIsInsideCbArg(grObjH);
  cmGrObjSetIsInsideCb( grObjH, _cmGrPlObjAfIsInside, op );

  //cmGrPlotObjSetUserPtr(oH,op);

  return kOkGrPlRC;
}
