/* ADCD - A Diminutive CD player for Linux/GNU
   Copyright (C) 2004-2026 Antonio Diaz Diaz.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/*
   Exit status: 0 for a normal exit, 1 for environmental problems
   (file not found, invalid command-line options, I/O errors, etc), 2 to
   indicate a corrupt or invalid input file, 3 for an internal consistency
   error (e.g., bug) which caused adcd to panic.
*/

#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>

#include "arg_parser.h"
#include "msf_time.h"
#include "cd.h"
#include "player.h"


namespace {

const char * const program_name = "adcd";
const char * const program_year = "2026";
const char * invocation_name = program_name;		// default value


void show_help()
  {
  std::fputs(
    "Adcd is a cd player for Linux/GNU with a ncurses (text mode) interface.\n"
    "\nAdcd can play all the tracks from a disc in order (linear mode), or in the\n"
    "order specified by the user (playlist mode), and includes all the functions\n"
    "expected in a stand-alone cd player, including random play and loop mode.\n"
    "\nAdcd also features a non-interactive mode for those who want to play their\n"
    "CDs while using the console for something else.\n"
    "\nMost probably the CD drive needs to be connected to the audio card through\n"
    "an analog audio cable for adcd to be able to play the CD. You may also need\n"
    "a mixer program like rexima or aumix to activate the audio card.\n"
    "\nIf invoked without options, adcd enters interactive mode.\n", stdout );
  std::printf( "\nUsage: %s [options] [device]\n", invocation_name );
  std::fputs( "\nOptions:\n"
    "  -h, --help              display this help and exit\n"
    "  -V, --version           output version information and exit\n"
    "  -c, --close             close tray and exit\n"
    "  -i, --info              display info about current status and exit\n"
    "  -o, --open              open tray and exit\n"
    "  -p, --play[=<n>]        start playing at track <n> (default 1) and exit\n"
    "  -p, --play=[+-]         change to next/previous track and exit\n"
    "  -s, --stop              stop playing and exit\n"
    "  -u, --pause             pause/resume playing and exit\n"
    "  -v, --volume[=[+-]<n>]  show, adjust or set volume and exit\n"
    "\nKeys not shown on screen:\n"
    "  1 to 9             Start playing at track 1 to 9\n"
    "  0                  Start playing at track 10\n"
    "  F1 to F12          Start playing at track 11 to 22\n"
    "  up arrow           Seek backward 10 seconds\n"
    "  down arrow         Seek forward 10 seconds\n"
    "  Page up            Seek backward 30 seconds\n"
    "  Page down          Seek forward 30 seconds\n"
    "  Home               Seek backward 1 minute\n"
    "  End                Seek forward 1 minute\n"
    "  Ins                Seek backward 5 minutes\n"
    "  Del                Seek forward 5 minutes\n"
    "  + -                increase/decrease volume by  1\n"
    "  * /                increase/decrease volume by 10\n"
    "  L                  Loop mode (no_loop, Disc, Track)\n"
    "  M                  Change playing mode (Linear/Playlist)\n"
    "  R                  Generate random playlist\n"
    "  T                  Change displayed time\n"
    "\nKeys used for editing playlist:\n"
    "  arrow keys         Move the cursor\n"
    "  0..9, F1..F12      Insert corresponding track\n"
    "  + -                Increase/decrease track by  1\n"
    "  * /                Increase/decrease track by 10\n"
    "  Del                Delete track at cursor\n"
    "  Backspace          Delete track preceding cursor\n"
    "  Enter              Store playlist and exit edit mode\n"
    "  Q                  Discard changes and exit edit mode\n"
    "\n*Exit status*\n"
    "0 for a normal exit, 1 for environmental problems (file not found, invalid\n"
    "command-line options, I/O errors, etc), 2 to indicate a corrupt or invalid\n"
    "input file, 3 for an internal consistency error (e.g., bug) which caused\n"
    "adcd to panic.\n"
    "\nReport bugs to adcd-bug@nongnu.org\n"
    "Adcd home page: http://www.nongnu.org/adcd/adcd.html\n", stdout );
  }


void show_version()
  {
  std::printf( "%s %s\n", program_name, PROGVERSION );
  std::printf( "Copyright (C) %s Antonio Diaz Diaz.\n", program_year );
  std::fputs( "License GPLv2+: GNU GPL version 2 or later <http://gnu.org/licenses/gpl.html>\n"
              "This is free software: you are free to change and redistribute it.\n"
              "There is NO WARRANTY, to the extent permitted by law.\n", stdout );
  }


void show_error( const char * const msg, const int errcode = 0,
                 const bool help = false )
  {
  if( msg && msg[0] )
    std::fprintf( stderr, "%s: %s%s%s\n", program_name, msg,
                  ( errcode > 0 ) ? ": " : "",
                  ( errcode > 0 ) ? std::strerror( errcode ) : "" );
  if( help )
    std::fprintf( stderr, "Try '%s --help' for more information.\n",
                  invocation_name );
  }

} // end namespace


int main( const int argc, const char * argv[] )
  {
//  const char * mixer_dev_name = "/dev/mixer";
  enum Volcommand { vnone, vadjust, vset, vshow };
  Volcommand vc = vnone;
  int step = 0, track = 1, volume = 0;
  bool open_tray = false, close_tray = false;
  bool info = false, pause = false, play = false, stop = false;
  if( argc > 0 ) invocation_name = argv[0];

  const Arg_parser::Option options[] =
    {
    { 'c', "close",   Arg_parser::no  },
    { 'h', "help",    Arg_parser::no  },
    { 'i', "info",    Arg_parser::no  },
//    { 'm', "mixer",   Arg_parser::yes },
    { 'o', "open",    Arg_parser::no  },
    { 'p', "play",    Arg_parser::maybe },
    { 's', "stop",    Arg_parser::no  },
    { 'u', "pause",   Arg_parser::no  },
    { 'v', "volume",  Arg_parser::maybe },
    { 'V', "version", Arg_parser::no  },
    { 0, 0,           Arg_parser::no  } };

  const Arg_parser parser( argc, argv, options );
  if( parser.error().size() )				// bad option
    { show_error( parser.error().c_str(), 0, true ); return 1; }

  int argind = 0;
  for( ; argind < parser.arguments(); ++argind )
    {
    const int code = parser.code( argind );
    if( !code ) break;					// no more options
    const char * const arg = parser.argument( argind ).c_str();
    switch( code )
      {
      case 'c': close_tray = true; break;
      case 'h': show_help(); return 0;
      case 'i': info = true; break;
//      case 'm': mixer_dev_name = arg; break;
      case 'o': open_tray = true; break;
      case 'p': if( arg[0] == '+' ) step = 1;
                else if( arg[0] == '-' ) step = -1;
                else { play = true; if( arg[0] ) track = std::strtol( arg, 0, 0 ); }
                break;
      case 's': stop = true; break;
      case 'u': pause = true; break;
      case 'v': if( !arg[0] ) vc = vshow;
                else if( isdigit( arg[0] ) ) vc = vset;
                else if( ( arg[0] == '+' || arg[0] == '-' ) && isdigit( arg[1] ) )
                  vc = vadjust;
                else { show_error( "bad volume value." ); return 1; }
                volume = std::strtol( arg, 0, 0 );
                break;
      case 'V': show_version(); return 0;
      default: show_error( "internal error: uncaught option." ); return 3;
      }
    } // end process options

  const char * cd_dev_name = "/dev/cdrom";
  if( argind < parser.arguments() ) cd_dev_name = parser.argument( argind++ ).c_str();
  if( argind < parser.arguments() )
    { show_error( "too many devices.", 0, true ); return 1; }

  // end scan arguments

  CD cd( cd_dev_name );
  bool interactive = true;

  if( vc != vnone )
    {
    interactive = false;
    if( vc == vshow ) std::printf( "Current volume = %d\n", cd.volume() );
    else
      {
      if( vc == vadjust ) cd.volume( cd.volume() + volume );
      else if( vc == vset ) cd.volume( volume );
      std::printf( "New volume = %d\n", cd.volume() );
      }
    }

  if( open_tray ) { cd.open(); interactive = false; }
  if( close_tray ) { cd.close(); interactive = false; }
  if( stop ) { cd.stop(); interactive = false; }
  if( play )
    {
    if( !cd.track( track, true ) )
      { show_error( cd.tracks() ? "bad track number." : cd.status_name() );
        return 1; }
    interactive = false;
    }
  if( step )
    {
    const bool done = (step > 0) ? cd.next_track() : cd.prev_track();
    if( !done )
      { show_error( cd.tracks() ? "can't change track." : cd.status_name() );
        return 1; }
    interactive = false;
    }
  if( info ) { cd.show_info(); interactive = false; }
  if( pause ) { cd.pause(); interactive = false; if( info ) cd.show_info(); }

  if( interactive ) Player::main_loop( cd );
  return 0;
  }
