/*  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/>.
*/

#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <string>
#include <vector>
#include <unistd.h>

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


namespace Bufhandle_vector {

int _first;				// index of first element
std::vector< Buffer_handle > data;


// Saves modified buffers if editor dies
//
void emergency_save()
  {
  if( handles() == 0 ) return;
  int i;
  for( i = 0; i < handles(); ++i )
    if( data[i].buffer().modified() ) break;
  if( i >= handles() ) return;

  const std::string dead_name( "DEADMOE" );
  RC::editor_options().backup = false;
  FILE *f = std::fopen( dead_name.c_str(), "a" );
  time_t dummy = time( 0 );
  std::fprintf( f, "\n*** Modified files in Moe when it aborted on %s", ctime( &dummy ) );
  std::fflush( f );
  for( i = 0; i < handles(); ++i )
    if( data[i].buffer().modified() )
      {
      std::fprintf( f, "\n*** File '%s'\n", data[i].buffer().uname().c_str() );
      std::fflush( f );
      data[i].buffer().save( &dead_name, false, true );
      }
  std::fclose( f );
  }

} // end namespace Bufhandle_vector


void Bufhandle_vector::init() throw()
  { _first = 0; std::atexit( emergency_save ); }


void Bufhandle_vector::save_all_named() throw()
  {
  int count = 0;
  bool done = true;

  for( int i = 0; i < handles(); ++i )
    {
    const Buffer & buffer = data[i].buffer();
    if( buffer.modified() && buffer.name().size() )
      {
      try { if( buffer.save() ) ++count; else done = false; }
      catch( Buffer::Error ) { done = false; }
      }
    }
  const char * s = ( done ? "" : "Only " );
  char buf[80];
  snprintf( buf, sizeof buf, "%s%d named buffer(s) saved", s, count );
  Screen::show_status_lines();
  Screen::show_message( buf );
  }


void Bufhandle_vector::show_status() throw()
  {
  int mcount = 0, ucount = 0;

  for( int i = 0; i < handles(); ++i )
    {
    if( data[i].buffer().modified() ) ++mcount;
    if( !data[i].buffer().name().size() ) ++ucount;
    }

  char buf[80];
  snprintf( buf, sizeof buf,
            "**  %d modified and  %d unnamed out of  %d total buffer(s) **",
            mcount, ucount, handles() );
  Screen::show_status_lines();
  Screen::show_message( buf );
  }


int Bufhandle_vector::absolute_index( const int i ) throw()
  { return ( i + _first ) % handles(); }


int Bufhandle_vector::circular_index( const int ai ) throw()
  { return ( ai + handles() - _first ) % handles(); }


Buffer_handle & Bufhandle_vector::handle( const int i ) throw()
  { return data[absolute_index(i)]; }


int Bufhandle_vector::handles() throw() { return data.size(); }


int Bufhandle_vector::handles( const Buffer & buffer ) throw()
  {
  int c = 0;
  for( int i = 0; i < handles(); ++i ) if( &buffer == &data[i].buffer() ) ++c;
  return c;
  }


int Bufhandle_vector::last_visited( const int i ) throw()
  {
  static int ai = 0;
  const int ci = circular_index( ai );
  ai = absolute_index( i );
  return ci;
  }


void Bufhandle_vector::next() throw()
  { ++_first; _first %= handles(); }


void Bufhandle_vector::prev() throw()
  { --_first; _first += handles(); _first %= handles(); }


void Bufhandle_vector::set_first( const int ai ) throw()
  { _first = ai % handles(); }


int Bufhandle_vector::find_or_add_handle( const Buffer::Options & opts,
                                          const std::string * namep,
                                          int i, const int line )
  {
  if( namep && namep->size() )
    for( int j = 0; j < handles(); ++j )
      if( *namep == data[j].buffer().name() )
        return circular_index( j );

  Buffer *bufp = new Buffer( opts, namep );
  if( namep && namep->size() ) RC::apply_regex_options( *bufp );

  if( i < 0 ) i = handles();
  else
    {
    i = absolute_index( i );
    if( i == 0 ) i = handles(); else if( i <= _first ) ++_first;
    }
  data.insert( data.begin() + i, Buffer_handle( *bufp, line ) );
  return circular_index( i );
  }


     // Add empty buffer if no files given or stdin is not a tty
void Bufhandle_vector::add_handle_if_pending_input_or_empty( const int line )
  {
  if( !handles() || !isatty( fileno( stdin ) ) )
    find_or_add_handle( RC::default_buffer_options(), 0, -1, line );
  }


void Bufhandle_vector::duplicate_handle( int i )
  {
  i = absolute_index( i );
  if( i < _first ) ++_first;
  data.insert( data.begin() + i + 1, data[i] );
  }


bool Bufhandle_vector::delete_handle( int i ) throw()
  {
  if( i < 0 || i >= handles() ) return false;
  else i = absolute_index( i );
  Buffer & buffer = data[i].buffer();
  int j;
  for( j = 0; j < handles(); ++j )
    if( j != i && &buffer == &data[j].buffer() ) break;
  if( j >= handles() )			// last handle for this buffer
    {
    if( Block::bufferp() == &buffer ) Block::reset();
    delete &buffer;
    }
  data.erase( data.begin() + i );
  if( i <= _first && handles() ) prev();
  return true;
  }
