/** 

 */

 #include <Arduino.h>
 #include "GD5800_Serial.h"
 #include <SoftwareSerial.h>


 void  GD5800_Serial::play()
 {
  this->sendCommand(0x01);
}

void  GD5800_Serial::restart()
{
  byte oldVolume = this->getVolume();  
  this->setVolume(0);
  this->next();
  this->pause();
  this->setVolume(oldVolume);
  this->prev();
}

void  GD5800_Serial::pause()
{
  this->sendCommand(0x02);
}

void  GD5800_Serial::next()
{
  this->sendCommand(0x03);
}

void  GD5800_Serial::prev()
{
  this->sendCommand(0x04);
}
void  GD5800_Serial::fastReverse()
{
  this->sendCommand(0x0B);
}

void  GD5800_Serial::fastForward()
{
  this->sendCommand(0x0A);
}

void  GD5800_Serial::playFileByIndexNumber(unsigned int fileNumber)
{  
  this->sendCommand(0x41, (fileNumber>>8) & 0xFF, fileNumber & (byte)0xFF);
}


void  GD5800_Serial::volumeUp()
{
  this->sendCommand(0x05);
}

void  GD5800_Serial::volumeDn()
{
  this->sendCommand(0x06);
}

void  GD5800_Serial::setVolume(byte volumeFrom0To30)
{
  this->sendCommand(0x31, volumeFrom0To30);
}

void  GD5800_Serial::setEqualizer(byte equalizerMode)
{
  this->sendCommand(0x32, equalizerMode);
}

void  GD5800_Serial::setLoopMode(byte loopMode)
{
  this->sendCommand(0x33, loopMode);
}




byte  GD5800_Serial::getStatus()    
{
  byte statTotal = 0;
  byte stat       = 0;
  do
  {
    statTotal = 0;
    for(byte x = 0; x < MP3_STATUS_CHECKS_IN_AGREEMENT; x++)
    {
      stat = this->sendCommandWithUnsignedIntResponse(0x42);      
          if(stat == 0) return 0; // STOP is fairly reliable
          statTotal += stat;
        }

        } while (statTotal != 1 * MP3_STATUS_CHECKS_IN_AGREEMENT && statTotal != 2 * MP3_STATUS_CHECKS_IN_AGREEMENT);

        return statTotal / MP3_STATUS_CHECKS_IN_AGREEMENT;      
      }

      byte  GD5800_Serial::getVolume()    { return this->sendCommandWithUnsignedIntResponse(0x11); }
      byte  GD5800_Serial::getEqualizer() { return this->sendCommandWithUnsignedIntResponse(0x44); }
      byte  GD5800_Serial::getLoopMode()  { return this->sendCommandWithUnsignedIntResponse(0x13); }
    
      unsigned int  GD5800_Serial::countFiles()   
      {
       
          return this->sendCommandWithUnsignedIntResponse(0x16); 
      }

      unsigned int  GD5800_Serial::currentFileIndexNumber()
      {
        
          return this->sendCommandWithUnsignedIntResponse(0x1A);         
    }
    
 
    
    // Used for the status commands, they mostly return an 8 to 16 bit integer 
    // and take no arguments
    unsigned int GD5800_Serial::sendCommandWithUnsignedIntResponse(byte command)
    {      
      char buffer[5];
      this->sendCommand(command, 0, 0, buffer, sizeof(buffer));
      return (unsigned int) strtoul(buffer, NULL, 16);
    }
    
    void  GD5800_Serial::sendCommand(byte command)
    {
      this->sendCommand(command, 0, 0, 0, 0);
    }
    
    void  GD5800_Serial::sendCommand(byte command, byte arg1)
    {
     this->sendCommand(command, arg1, 0, 0, 0);
   }

   void  GD5800_Serial::sendCommand(byte command, byte arg1, byte arg2)
   {
     this->sendCommand(command, arg1, arg2, 0, 0);
   }    

   void  GD5800_Serial::sendCommand(byte command, byte arg1, byte arg2, char *responseBuffer, unsigned int bufferLength)
   {


      // Command structure
      // [7E][number bytes following including command and terminator][command byte][?arg1][?arg2][EF]
      
      // Most commands do not have arguments
      byte args = 0;
      
      // These ones do
      switch(command)
      {        
        case 0x41: args = 2; break;//指定曲目
        case 0x31: args = 1; break;//指定音量
        case 0x32: args = 1; break;//指定EQ        
        case 0x09: args = 1; break;//指定设备
        case 0x33: args = 1; break;//循环播放
        default :args=1;break;
      }
      
      #if MP3_DEBUG
      char buf[4];       
      Serial.println();
      Serial.print("7E ");      
      itoa(2+args, buf, 16); Serial.print(buf); Serial.print(" "); memset(buf, 0, sizeof(buf));
      itoa(command, buf, 16); Serial.print(buf); Serial.print(" "); memset(buf, 0, sizeof(buf));
      if(args>=1) itoa(arg1, buf, 16); Serial.print(buf); Serial.print(" "); memset(buf, 0, sizeof(buf));
      if(args>=2) itoa(arg2, buf, 16); Serial.print(buf); Serial.print(" "); memset(buf, 0, sizeof(buf));
      Serial.print("EF");      
      #endif
      
      // The device appears to send some sort of status information (namely "STOP" when it stops playing)
      // just discard this right before we send the command
      while(this->waitUntilAvailable(10)) this->read();
      
      this->write((byte)0x7E);
      this->write(2+args);
      this->write(command);
      if(args>=1) this->write(arg1);
      if(args==2) this->write(arg2);
      this->write((byte)0xEF);
      
      
      unsigned int i = 0;
      char         j = 0;
      if(responseBuffer && bufferLength) 
      {
        memset(responseBuffer, 0, bufferLength);
      }
      
      // Allow some time for the device to process what we did and 
      // respond, up to 1 second, but typically only a few ms.
      this->waitUntilAvailable(1000);

      
      #if MP3_DEBUG
      Serial.print(" ==> [");
      #endif
      
      while(this->waitUntilAvailable(150))
      {
        j = (char)this->read();
        
        #if MP3_DEBUG
        Serial.print(j);
        #endif
        if(responseBuffer && (i<(bufferLength-1)))
        {
         responseBuffer[i++] = j;
       }
     }

     #if MP3_DEBUG      
     Serial.print("]");
     Serial.println();
     #endif

   }


// as readBytes with terminator character
// terminates if length characters have been read, timeout, or if the terminator character  detected
// returns the number of characters placed in the buffer (0 means no valid data found)

size_t GD5800_Serial::readBytesUntilAndIncluding(char terminator, char *buffer, size_t length, byte maxOneLineOnly)
{
  if (length < 1) return 0;
  size_t index = 0;
  while (index < length) {
    int c = timedRead();
    if (c < 0) break;    
    *buffer++ = (char)c;
    index++;
    if(c == terminator) break;
    if(maxOneLineOnly && ( c == '\n') ) break;
  }
  return index; // return number of characters, not including null terminator
}


// Waits until data becomes available, or a timeout occurs
int GD5800_Serial::waitUntilAvailable(unsigned long maxWaitTime)
{
  unsigned long startTime;
  int c = 0;
  startTime = millis();
  do {
    c = this->available();
    if (c) break;
    } while(millis() - startTime < maxWaitTime);

    return c;
  }
