/*
  splay player plugin
  Copyright (C) 1999  Martin Vogt

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU Library General Public License as published by
  the Free Software Foundation.

  For more information look at the file COPYRIGHT in this package

 */


#include "splayPlugin.h"
#include "../splay/mpegsound.h"

#include "../splay/splayDecoder.h"
#include "../frame/floatFrame.h"
#include "../frame/pcmFrame.h"
#include "../splay/mpegAudioFrame.h"
#include "../splay/mpegAudioInfo.h"
#include "../input/fileAccessWrapper.h"

#include <iostream>

using namespace std;

#define INPUTBUFFER_SIZE 8192

SplayPlugin::SplayPlugin() {
  pow(6.0,3.0);            // fixes bug in __math.h
  doFloat = false;
  lnoLength=false;
  inputbuffer=new unsigned char[INPUTBUFFER_SIZE];
  pcmFrame=new PCMFrame(MP3FRAMESIZE);
  floatFrame=new FloatFrame(MP3FRAMESIZE);
  audioFrame=new AudioFrame();
  framer=new MpegAudioFrame();
  splay=new SplayDecoder();
  lengthInSec=0;
 
  fileAccess=NULL;
  info=NULL; 
  lOutput=true;
}


SplayPlugin::~SplayPlugin() {
  delete [] inputbuffer;
  delete pcmFrame;
  delete floatFrame;
  delete framer;
  delete splay;
  delete audioFrame;
}


// here we can config our decoder with special flags
void SplayPlugin::config(const char* key,const char* value,void* user_data) {
  if (strcmp(key,"dofloat")==0) {
    doFloat=true;
  }
  if (strcmp(key,"-m")==0) {
    splay->config("m","0",NULL);
  }
  if (strcmp(key,"-2")==0) {
    splay->config("2","1",NULL);
  }
  if (strcmp(key,"-c")==0) {
    lnoLength=true;
  }
  if (strcmp(key,"-d")==0) {
    lOutput=false;
  }

  if (strcmp(key,"decode")==0) {
    if (strcmp(value,"true")==0) {
      lDecode=true;
    } else {
      lDecode=false;
    }
  }    
  DecoderPlugin::config(key,value,user_data);
}



void SplayPlugin::decoder_loop() {

  if (input == NULL) {
    cout << "SplayPlugin::decoder_loop input is NULL"<<endl;
    exit(0);
  }
  if (output == NULL) {
    cout << "SplayPlugin::decoder_loop output is NULL"<<endl;
    exit(0);
  }
  // init decoder
  output->audioInit();

  // start decoding

  fileAccess=new FileAccessWrapper(input);
  info= new MpegAudioInfo(fileAccess);


  
  framer->reset();
  lengthInSec=0;
  resyncCounter=0;

  AudioFrame* audioFrame=pcmFrame;
  if (doFloat) {
    audioFrame=floatFrame;
  }

  output->audioInit();
  while(runCheck()) {

    //
    // check for re-init or for "eof"
    //
    switch(streamState) {
    case _STREAM_STATE_INIT :
      framer->reset();
      resyncCounter=5;
      setStreamState(_STREAM_STATE_PLAY);
      continue;
    case _STREAM_STATE_WAIT_FOR_END:
      // exit while loop
      lDecoderLoop=false;
      continue;
    }
    if (doFrameFind() == true) {
      if (splay->decode(framer->outdata(),framer->len(),audioFrame) == false){
	continue;
      }
      // send data out:

      int rest=framer->restBytes();
      // we have inserted more data already than the 
      // framer has processed. But framer tells us
      // how much he still needs to process.
      long pos=input->getBytePosition();
      TimeStamp* stamp=input->getTimeStamp(pos-rest);
      processStreamState(stamp,audioFrame);

      
      // make this stamp invalid for further synchronisation
      stamp->setPTSFlag(false);
    }
  }

  output->audioFlush();

  delete fileAccess;
  delete info;
  fileAccess=NULL;
  info=NULL;


}


int SplayPlugin::doFrameFind() {

  //
  // fine, we can work on the stream:
  //
  int back=false;
  int framerState=framer->getState();
  switch(framerState) {
  case FRAME_NEED: {
    int bytes=framer->canStore();
    int read=input->read((char*)inputbuffer,bytes);

    if (read <= 0) {
      // read error. reset framer, drop frames
      setStreamState(_STREAM_STATE_INIT);
      break;
    }
    framer->store(inputbuffer,read);
    break;
  }
  case FRAME_WORK:
    back=framer->work();
    break;
  case FRAME_HAS: {
    break;
  }
  default:
    cout << "unknown state in mpeg audio framing"<<endl;
    exit(0);
  }
  return back;
}

void SplayPlugin::audioSetup(AudioFrame* setupFrame) {
  setupFrame->copyFormat(audioFrame);
  output->audioSetup(audioFrame->getFrequenceHZ(),
                     audioFrame->getStereo(),
                     audioFrame->getSigned(),
                     audioFrame->getBigEndian(),
                     audioFrame->getSampleSize()); 
}



void SplayPlugin::processStreamState(TimeStamp* stamp,AudioFrame* playFrame){

  // we always have a frame here, with the correct timestamp here.

  switch(streamState) {
  case _STREAM_STATE_FIRST_INIT :
    output->audioOpen();
   
    audioSetup(playFrame);

    if (lnoLength==false) {
      lengthInSec=getTotalLength();
      pluginInfo->setLength(lengthInSec);
      output->writeInfo(pluginInfo);
    }

    setStreamState(_STREAM_STATE_PLAY);
    // yes, here is no break.
    // we want to send the frame to the output.

  case _STREAM_STATE_PLAY :
    if (resyncCounter > 0) {
      resyncCounter--;
      break;
    }
    if (audioFrame->isFormatEqual(playFrame)==false) {
      audioSetup(playFrame);
    }
    if (lOutput == false) {
      break;
    }
    if(doFloat) {
      FloatFrame* floatFrame=(FloatFrame*)playFrame;
      output->audioPlay(stamp,stamp,
			(char*) floatFrame->getData(),
			playFrame->getLen()*sizeof(float) );
    } else {
      PCMFrame* pcmFrame=(PCMFrame*)playFrame;
      output->audioPlay(stamp,stamp,
			(char*)pcmFrame->getData(),
			playFrame->getLen()*sizeof(short int));
    }
    break;
  default:
    cout << "unknown stream state:"<<streamState<<endl;
  }
}



// splay can seek in streams
int SplayPlugin::seek_impl(int second) {


  if (info != NULL) {
    int pos=info->getSeekPosition(second);
    input->seek(pos);
    setStreamState(_STREAM_STATE_INIT);
  } else {
    cout << "cannot seek, plugin not initialized"<<endl;
  }
  return true;
}


int SplayPlugin::getTotalLength() {
  shutdownLock();
  int back=0;

  if (info->getNeedInit()) {
    long pos=input->getBytePosition();
    if (input->seek(0) == true) {
      int bytes=1024;
      // try reading all info, but not more
      // than 1024 iterations.
      info->reset();
      while(bytes>0) {
	if (info->initialize()==true) {
	  break;
	}
	bytes--;
      }
      input->seek(pos);
    }
    // wheter successful or not, never touch
    // the info thing again.
    info->setNeedInit(false);
  }

  back=info->getLength();
  

  shutdownUnlock();
  return back;
}





