#ifndef GLOBAL_DECL
#include "global.h"     // Global data types and variables
#endif
#ifndef BROWSER_DECL
#include "browser.h"
#endif
#ifndef PLAYER_DECL
#include "player.h"
#endif
#ifndef PLAYLIST_DECL
#include "playlist.h"
#endif
#ifndef IR_DECL
#include "irc.h"
#endif
#include <stdarg.h>
#include <stdlib.h>
#include <time.h>

browser   objBrowser;    // Pointer to an instance of the browser class
player    objPlayer;     // Pointer to an instance of the player class
playlist  objPlaylist;   // Pointer to an instance of the playlist class
irc       objIrc;        // Pointer to an instance of the IR remote control class

int    intMode = 1;
char   *progname;
struct lirc_config *config;
char   *code;
char   *c;
int    ret;
char   ir_key[100];

int fd; /* File descriptor for the port */

void clean_up_child_process (int signal_number)
{
  /* Clean up the child process.  */
   int status;
   wait (&status);

   if (objPlayer.intStopPressed == 1)     // User has pressed stop, so stop
   {
     objPlayer.intStopPressed = 0;
     objPlayer.is_playing = 0;
     objPlayer.is_paused = 0;
   }
   else                         // Move to the next song in the playlist
   {
     if (objPlaylist.next(&objPlayer, 0) == 0)
     {
       // At the end of the playlist, so stop
       objPlayer.is_playing = 0;
       objPlayer.is_paused = 0;
     }
   }
}

void open_port(void)
{
  fd = open("/dev/ttyS2", O_RDWR | O_NOCTTY | O_NDELAY);
  if (fd == -1)
  {
    perror("open_port: Unable to open /dev/ttyS2 - ");
  }
  else
    fcntl(fd, F_SETFL, 0);
}

void send_text(char* strText, int intLineNo, int intPos)
{
  char linesel[2];
  int intLen;
  char strTemp[50];

  linesel[0] = 254;
  if (intLineNo == 1)
    { linesel[1] = 128; }
  else
    { linesel[1] = 192; }

  write(fd, linesel, 2);

  strncpy(strTemp, &strText[intPos], LCD_CHARACTERS + 1);
  pad_space_to_eol(strTemp);
  intLen = strlen(strTemp);
  write(fd, strTemp, intLen);

 #if OUTPUTDEBUG
  // Simulate LCD output on standard output
  //  if (intLineNo == 1)
  //    { move(5, 5); }
  //  else
  //    { move(6, 5); }
  printf("\n\n\n%s",strTemp);
  //refresh();
 #endif

}

void send_cls(void)
{
  char clschars[2];
  int  n;

  #if OUTPUTDEBUG
    // Clear standard output also
    //clear();
    printf("\n\n\n\n\n\n\n\n\n\n\n\n\n");
  #endif

  clschars[0] = 254;
  clschars[1] = 1;
  write(fd, clschars, 2);
  for (n=0;n<SCROLL_DELAY;n++);
}

void initialise(void)
{
  char strText[100];
  struct sigaction sigchld_action;

  /* Set up signal handler to handle SIGCHLD by calling clean_up_child_process. */
  memset (&sigchld_action, 0, sizeof (sigchld_action));
  sigchld_action.sa_handler = &clean_up_child_process;
  sigaction (SIGCHLD, &sigchld_action, NULL);

  open_port();

  send_cls();

  strcpy(strText, " Welcome to Adam's  ");
  send_text(strText, 1, 0);
  strcpy(strText, "--- mp3 machine! ---");
  send_text(strText, 2, 0);
  sleep(2);

  // Debug info and on-screen prompts
  printf("\n\n-----------------------------------------------------------------");
  printf("\nmp3 Machine Initalised.\nHome directory: %s", HOME_DIR);
  printf("\n-----------------------------------------------------------------");
  printf("\nKeys:\nUP    = Go up in menu or playlist\nDOWN  = Go down in menu or playlist");
  printf("\nLEFT  = Go up a level in the hierarchy\nRIGHT = Enter a sub-directory or play an mp3");
  printf("\n1     = Mode 1 - Song browsing mode\n2     = Mode 2 - Currently playing");
  printf("\n3     = Mode 3 - Playlist editor\n4     = Mode 4 - Clock mode");
  printf("\nS     = Stop playback");
  printf("\nP     = Pause playback");
  printf("\nE     = Enqueue - add to end of playlist");
  printf("\nQ     = Shut down the computer");
  printf("\nX     = Exit application");
  printf("\n-----------------------------------------------------------------\n\n");
}


int menu_loop(void)
{
  char  chrInputChar;
  char  chrIRChar;
  int   intScrollPos = 0;
  int   intDelayLoop = 0;
  char  strPath[400];
  char  strFile[400];
  int   return_code = 0;

  intDelayLoop = PRE_SCROLL_DELAY;
  objBrowser.update = 1;
  objBrowser.display_menu(0);

  do
  {
    if (intDelayLoop < 0)
      { intDelayLoop = SCROLL_DELAY; }
    intDelayLoop--;

    chrInputChar = '\0';
    if (kbhit()) { chrInputChar = getch(); }

    // Get IR inputs
    chrIRChar = objIrc.get_key();
    if (chrIRChar != 0) chrInputChar = chrIRChar;


    return_code = common_keys(chrInputChar);
    if (return_code != 0) { return(return_code); }

    // Cursor keys = 27, 91 then LEFT=68, RIGHT=67, UP=65, DOWN=66
    if (chrInputChar == 27) chrInputChar = getch();
    if (chrInputChar == 91) chrInputChar = getch();

        if (chrInputChar == 65)  /* Go up in the menu */
        {
          objBrowser.go_up();
          intScrollPos = 0;
          intDelayLoop = PRE_SCROLL_DELAY;
        }
        if (chrInputChar == 66)  /* Go down in the menu */
        {
          objBrowser.go_down();
          intScrollPos = 0;
          intDelayLoop = PRE_SCROLL_DELAY;
        }
        if (chrInputChar == 68)  /* Go up a level */
        {
          objBrowser.leave_subdir();
          intScrollPos = 0;
          intDelayLoop = PRE_SCROLL_DELAY;
        }
        if (chrInputChar == 67)    /* Enter a sub-directory, or play an mp3 */
        {
          if (objBrowser.is_directory())  // Enter directory
          {
            objBrowser.enter_subdir();
            intScrollPos = 0;
            intDelayLoop = PRE_SCROLL_DELAY;
          }
          else                            // Play mp3
          {
            objBrowser.get_path(strPath);
            objBrowser.get_current(strFile);

            objPlaylist.clear();               // Clear playlist
            objPlaylist.add(strPath, strFile);          // Add new song to playlist

            objPlayer.play(strPath, strFile);  // Play mp3

            intMode = 2;
            return(2);    // switch to now playing mode
          }
        }

    if (chrInputChar == 'e')
    {
      if (objBrowser.is_file())
      {
        objBrowser.get_path(strPath);
        objBrowser.get_current(strFile);
        objPlaylist.add(strPath, strFile);          // Add new song to playlist
        printf("\nEnqueued:\nPath = '%s'\nFile = '%s'\n", strPath, strFile);
      }
    }

    if (intDelayLoop == 0)       /* Scroll the bottom line of the display */
    {
      if (objBrowser.get_length() > (LCD_CHARACTERS + 4))
      {
        intScrollPos++;
        if(intScrollPos >= objBrowser.get_length() - (LCD_CHARACTERS - 1) ) { intScrollPos = 0; }
        objBrowser.update = 1;
      }
    }

    // Update the display if necessary
    objBrowser.display_menu(intScrollPos);

  } while (chrInputChar != 'x');

  return(-1);
}


int common_keys(char chrKey)
{
  // Return:  -1=exit, 0=do nothing, 1=dir menu, 2=playing, 3=play list

  if (chrKey == 's')  /* Stop playing */
  {
    if (objPlayer.is_playing == 1)
    {
      objPlayer.intStopPressed = 1;
      objPlayer.stop();
      intMode = 1;
      return(1);
    }
  }
  else if (chrKey == 'p')  /* Pause playing */
  {
    if (objPlayer.is_playing == 1)
    {
      objPlayer.pause();

      if (objPlayer.is_paused == 0)
      {
        if (intMode == 2) send_text("Paused...", 1, 0);
        objPlayer.is_paused = 1;
      }
      else
      {
        if (intMode == 2) send_text("Now playing:", 1, 0);
        objPlayer.is_paused = 0;
      }
    }
  }
  else if (chrKey == '.')  // Go to next song in playlist
    objPlaylist.next(&objPlayer, 1);
  else if (chrKey == ',')  // Go to previous song in playlist
    objPlaylist.previous(&objPlayer, 1);

  else if (chrKey == 'q')  /* Shut the computer down */
  {
    popen("shutdown -h now", "w");
  }
  else if (chrKey == '1') { intMode = 1; objBrowser.update = 1; return(1); }
  else if (chrKey == '2') { intMode = 2; return(2); }
  else if (chrKey == '3') { intMode = 3; return(3); }
  else if (chrKey == '4') { intMode = 4; return(4); }

 #if USE_IR
 #endif

  return(0);
}



int play_loop(void)
{
  char  chrInputChar;
  char  chrIRChar;
  //  int   intUpdate = 0;
  int   intScrollPos = 0;
  int   intDelayLoop = PRE_SCROLL_DELAY;
  int   return_code = 2;
  char  strTemp[255];

  strTemp[0] = '\0';

  if (objPlayer.is_paused == 1) send_text("Paused...", 1, 0);
  else if (objPlayer.is_playing == 1) send_text("Now playing:", 1, 0);
  else send_text("Stopped", 1, 0);

  objPlayer.update = 1;
  objPlayer.display_song(intScrollPos);

  do
  {
    if (intDelayLoop < 0)
      { intDelayLoop = SCROLL_DELAY; }
    intDelayLoop--;

    chrInputChar = '\0';
    if (kbhit()) { (unsigned)chrInputChar = getch(); }

    // Get IR inputs
    chrIRChar = objIrc.get_key();
    if (chrIRChar != 0) chrInputChar = chrIRChar;


    return_code = common_keys(chrInputChar);
    if (return_code != 0) { return(return_code); }

    if (intDelayLoop == 0)       /* Scroll the bottom line of the display */
    {
      if (strlen(objPlaylist.current()) > (LCD_CHARACTERS + 4))
      {
        intScrollPos++;
        if((unsigned)intScrollPos >= strlen(objPlaylist.current()) - (LCD_CHARACTERS - 1) ) { intScrollPos = 0; }
        objPlayer.update = 1;
      }
    }

    objPlayer.display_song(intScrollPos);

  } while (chrInputChar != 'x');

  return(-1);
}



int time_loop(void)
{
  char   chrInputChar;
  char   chrIRChar;
  int    intDelayLoop = 1;
  int    return_code = 4;
  struct tm today;
  time_t timTime;
  char   str[81];

  do
  {
    if (intDelayLoop < 0)
      { intDelayLoop = (int)(SCROLL_DELAY / 2); }
    intDelayLoop--;

    chrInputChar = '\0';
    if (kbhit()) { (unsigned)chrInputChar = getch(); }

    // Get IR inputs
    chrIRChar = objIrc.get_key();
    if (chrIRChar != 0) chrInputChar = chrIRChar;

    return_code = common_keys(chrInputChar);
    if (return_code != 0) { return(return_code); }

    if (intDelayLoop == 0)       /* Update the display */
    {
      send_text("-Adam's MP3 Machine-", 1, 0);

      timTime = time( NULL );
      today = *localtime( &timTime );
      strftime( str, sizeof(str)-1, "     %I:%M:%S %p  ", &today );

      send_text(str, 2, 0);
    }
  } while (chrInputChar != 'x');

  return(-1);
}


int list_loop(void)
{
  char  chrInputChar;
  char  chrIRChar;
  int   intUpdate = 0;
  int   intScroll = 0;
  //  char* strTemp;
  //  char  strPath[100];
  int   intScrollPos = 0;
  int   intDelayLoop = PRE_SCROLL_DELAY;
  //  char  strFname[250];
  int   return_code = 0;
  //  int   intLine = 0;

  objPlaylist.select_current();
  objPlaylist.display(0);

  do
  {
    intUpdate = 0; intScroll = 0;

    if (intDelayLoop < 0)
      { intDelayLoop = SCROLL_DELAY; }
    intDelayLoop--;

    chrInputChar = '\0';
    if (kbhit()) { chrInputChar = getch(); }

    // Get IR inputs
    chrIRChar = objIrc.get_key();
    if (chrIRChar != 0) chrInputChar = chrIRChar;


    return_code = common_keys(chrInputChar);
    if (return_code != 0) { return(return_code); }

    // Cursor keys = 27, 91 then LEFT=68, RIGHT=67, UP=65, DOWN=66

    if (chrInputChar == 27) chrInputChar = getch();
    if (chrInputChar == 91) chrInputChar = getch();

    if (chrInputChar == 65)  /* Go up in the menu */
    {
      objPlaylist.up();
      intUpdate = 1;
      intScrollPos = 0;
      intDelayLoop = PRE_SCROLL_DELAY;
    }
    if (chrInputChar == 66)  /* Go down in the menu */
    {
      objPlaylist.down();
      intUpdate = 1;
      intScrollPos = 0;
      intDelayLoop = PRE_SCROLL_DELAY;
    }

    if (intDelayLoop == 0)       /* Scroll the bottom line of the display */
    {
      if (strlen(objPlaylist.current_viewing()) > (LCD_CHARACTERS + 4))
      {
        intScrollPos++;
        if((unsigned)intScrollPos >= strlen(objPlaylist.current_viewing()) - (LCD_CHARACTERS - 1) ) { intScrollPos = 0; }
        intScroll = 1;
      }
    }

    if((intUpdate == 1) || (intScroll == 1))          /* Refresh the display */
    {
      objPlaylist.display(intScrollPos);
    }

  } while (chrInputChar != 'x');

  return(-1);
}


int main(void)
{
  // intMode =  -1=exit, 0=do nothing, 1=dir menu, 2=playing, 3=play list
  int intReturnValue = 0;

  initialise();

  do
  {
    if (intMode == 1)
      { intReturnValue = menu_loop(); }
    else if (intMode == 2)
      { intReturnValue = play_loop(); }
    else if (intMode == 3)
      { intReturnValue = list_loop(); }
    else if (intMode == 4)
      { intReturnValue = time_loop(); }

    if (intReturnValue != 0) { intMode = intReturnValue; }

  } while (intMode != -1);

  send_cls();

  objPlayer.killme();

  close(fd);

 #if USE_IR
  lirc_freeconfig(config);
  lirc_deinit();
 #endif

  return(0);
}


int pad_dash_to_eol(char* strText)
{
  int n;

  for(n=strlen(strText); n<(LCD_CHARACTERS + 4); n++)
  {
    strcat(strText, "-");
  }
  return(0);
}

int pad_space_to_eol(char* strText)
{
  int n;

  if (strlen(strText) < (LCD_CHARACTERS + 4))
  {
    for(n=strlen(strText); n<(LCD_CHARACTERS + 4); n++)
    {
      strcat(strText, " ");
    }
  }
  return(0);
}

int is_mp3(char* strFname)
{
  char* ext;
  if (strlen(strFname) > 4)
  {
    ext = &strFname[strlen(strFname)-4];
    if (strcasecmp(ext, ".mp3") == 0)
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }
  else
  {
    return 0;
  }
}

int kbhit(void)
{
  struct termios term, oterm;
  int fd = 0;
  int c = 0;

  /* get the terminal settings */
  tcgetattr(fd, &oterm);

  /* get a copy of the settings, which we modify */
  memcpy(&term, &oterm, sizeof(term));

  /* put the terminal in non-canonical mode, any
     reads timeout after 0.1 seconds or when a
     single character is read */
  term.c_lflag = term.c_lflag & (!ICANON);
  term.c_cc[VMIN] = 0;
  term.c_cc[VTIME] = 1;
  tcsetattr(fd, TCSANOW, &term);

  /* get input - timeout after 0.1 seconds or
     when one character is read. If timed out
     getchar() returns -1, otherwise it returns
     the character */
  c=getchar();

  /* reset the terminal to original state */
  tcsetattr(fd, TCSANOW, &oterm);

  /* if we retrieved a character, put it back on
     the input stream */
  if (c != -1)
    ungetc(c, stdin);

  /* return 1 if the keyboard was hit, or 0 if it
     was not hit */
  return ((c!=-1)?1:0);
}

/********************************************************/

int getch()
{
  int c, fd=0;
  struct termios term, oterm;

  /* get the terminal settings */
  tcgetattr(fd, &oterm);

  /* get a copy of the settings, which we modify */
  memcpy(&term, &oterm, sizeof(term));

  /* put the terminal in non-canonical mode, any
     reads will wait until a character has been
     pressed. This function will not time out */
  term.c_lflag = term.c_lflag & (!ICANON);
  term.c_cc[VMIN] = 1;
  term.c_cc[VTIME] = 0;
  tcsetattr(fd, TCSANOW, &term);

  /* get a character. c is the character */
  c=getchar();

  /* reset the terminal to its original state */
  tcsetattr(fd, TCSANOW, &oterm);

  /* return the charcter */
  return c;
}


syntax highlighted by Code2HTML, v. 0.9.1