/*  GNU Moe - My Own Editor
    Copyright (C) 2005, 2006, 2007, 2008, 2009 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 3 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/>.
*/
/*
    Return values: 0 for a normal exit, 1 for environmental problems
    (file not found, invalid flags, I/O errors, etc), 2 to indicate a
    corrupt or invalid input file, 3 for an internal consistency error
    (eg, bug) which caused moe to panic.
*/

#include <cctype>
#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <unistd.h>

#include "buffer.h"
#include "buffer_handle.h"
#include "rc.h"
#include "screen.h"


namespace {

const char * invocation_name = 0;
const char * const Program_name     = "GNU Moe";
const char * const program_name     = "moe";
const char * const config_file_name = "moerc";
const char * const program_year     = "2009";


void show_help() throw()
  {
  std::printf( "%s - A powerful and user-friendly text editor.\n", Program_name );
  std::printf( "\nUsage: %s [global-options] [[+line] filename [file-options]]...\n", invocation_name );
  std::printf( "  `global-options' are a mix of editor-options and default file-options.\n");
  std::printf( "  Most long option names have a negative form `--backup' `--no-backup'.\n" );
  std::printf( "\nEditor options:\n" );
  std::printf( "  -h, --help               display this help and exit\n" );
  std::printf( "  -V, --version            output version information and exit\n" );
  std::printf( "  -b, --backup             create backup files (default)\n" );
  std::printf( "  -e, --exit-ask           save-and-close always asks for confirmation\n" );
  std::printf( "  -H, --smart-home         go home, then go to first non-blank character\n" );
  std::printf( "  -i, --ignore-case        make search case insensitive by default\n" );
  std::printf( "  -k, --keep-lines=<n>     number of lines to keep for PgUp/PgDn\n" );
  std::printf( "  -m, --max-windows=<n>    max number of windows to show at once\n" );
  std::printf( "  -n, --indent-step=<n>    number of spaces to add when indenting a block\n" );
  std::printf( "  -1, --orphan             put extra files in orphaned buffers\n" );
//  std::printf( "  -p, --beep               beep on errors and when cursor goes past extremes\n" );
  std::printf( "  -s, --search-wrap        search wraps at end of file\n" );
  std::printf( "  -u, --auto-unmark        turn off highlighting after block copy or move\n" );
  std::printf( "  -x, --rectangle          rectangular block mode\n" );
  std::printf( "\nFile options:\n" );
  std::printf( "  -a, --auto-indent        auto indent\n" );
  std::printf( "  -l, --lmargin=<col>      left margin\n" );
  std::printf( "  -r, --rmargin=<col>      right margin\n" );
  std::printf( "  -o, --read-only          file is read-only\n" );
  std::printf( "  -O, --overwrite          insert mode off\n" );
  std::printf( "  -w, --word-wrap          word wrap\n" );
  std::printf( "\nReport bugs to bug-moe@gnu.org\n" );
  std::printf( "Moe home page: http://www.gnu.org/software/moe/moe.html\n" );
  std::printf( "General help using GNU software: http://www.gnu.org/gethelp\n" );
  }


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


int set_signal( int signum, void (*handler)(int) ) throw()
  {
  struct sigaction new_action;

  new_action.sa_handler = handler;
  sigemptyset( &new_action.sa_mask );
  new_action.sa_flags = SA_RESTART;
  return sigaction( signum, &new_action, 0 );
  }


void restore_signals() throw()
  {
  set_signal( SIGINT, SIG_DFL );
  set_signal( SIGPIPE, SIG_DFL );
  set_signal( SIGFPE, SIG_DFL );
  set_signal( SIGALRM, SIG_DFL );
  }


void set_signals() throw()
  {
  std::atexit( restore_signals );
  set_signal( SIGINT, SIG_IGN );
  set_signal( SIGPIPE, SIG_IGN );
  set_signal( SIGFPE, SIG_IGN );
  set_signal( SIGALRM, Screen::clock_handler );
  }


bool use_it_anyway( std::string name ) throw()
  {
  if( !isatty( fileno( stdin ) ) ) return false;
  std::fprintf( stderr, "There are errors in '%s'. Use it anyway (y/N)? ",
                name.c_str() );
  std::fflush( stderr );
  return ( std::tolower( std::fgetc( stdin ) ) == 'y' );
  }


int process_config_file() throw()
  {
  std::fprintf( stderr, "\n" );
  std::string name( RC::home_directory() );
  if( name.size() )
    {
    name += "/."; name += config_file_name;
    const int retval = RC::process_rcfile( name );
    if( retval == 0 || ( retval == 2 && use_it_anyway( name ) ) ) return 0;
    else RC::reset();
    }
  name = SYSCONFDIR; name += '/'; name += config_file_name;
  const int retval = RC::process_rcfile( name );
  if( retval == 0 || ( retval == 2 && use_it_anyway( name ) ) ) return 0;
  else RC::reset();
  std::fprintf( stderr, "Couldn't open '%s'\n", name.c_str() );
  RC::reset();
  return 0;
  }

} // end namespace


const std::string & RC::home_directory() throw()
  {
  static bool first_time = true;
  static std::string home;

  if( first_time )
    { first_time = false; const char *p = getenv( "HOME" ); if( p ) home = p; }

  return home;
  }


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


int main( const int argc, const char * argv[] )
  {
  invocation_name = argv[0];

  int retval = RC::read_options( argc, argv );
  if( retval )
    {
    if( retval == 'h' ) { show_help(); return 0; }
    if( retval == 'V' ) { show_version(); return 0; }
    return retval;
    }

  Bufhandle_vector::init();

  retval = process_config_file();
  if( !retval )
    {
    retval = RC::process_options();
    if( retval == 2 )
      {
      std::fprintf( stderr, "Press RETURN to continue, Ctrl-C to abort.\n" );
      std::fgetc( stdin ); retval = 0;
      }
    }
  if( retval ) return retval;

  if( !Screen::init() ) { RC::show_error( "screen too small" ); return 1; }
  set_signals();

  char buf[88];
  snprintf( buf, sizeof buf,
    "@i ** My Own Editor v%s ** (iso-8859) ** Copyright (C) %s Antonio Diaz ** ",
    PROGVERSION, program_year );
  return Screen::user_loop( buf );
  }
