/*  GNU Moe - My Own Editor
    Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
    2014 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 <algorithm>
#include <cstdlib>
#include <string>
#include <vector>

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


namespace Block {

Buffer * bufferp_ = 0, * saved_bufferp = 0;
Point anchor_, begin_, end_, saved_begin, saved_end;


void enable_marking( Buffer & buffer, const Point & p )
  {
  bufferp_ = &buffer; begin_ = p; anchor_ = p; end_ = Point();
  }

bool marking() { return ( anchor_ != Point() ); }

} // end namespace Block


Buffer * Block::bufferp() { return bufferp_; }

Point & Block::anchor() { return anchor_; }

Point & Block::begin() { return begin_; }

Point & Block::end() { return end_; }


bool Block::in_block( const Buffer & buffer, const Point & p )
  {
  return ( &buffer == bufferp_ && begin_.line >= 0 && p >= begin_ && p < end_ &&
           ( !RC::editor_options().rectangle_mode ||
             ( p.col >= begin_.col && p.col < end_.col ) ) );
  }


bool Block::in_block_or_end( const Buffer & buffer, const Point & p )
  {
  return ( &buffer == bufferp_ && begin_.line >= 0 && begin_ < end_ &&
           p >= begin_ && p <= end_ &&
           ( !RC::editor_options().rectangle_mode ||
             ( p.col >= begin_.col && p.col <= end_.col ) ) );
  }


bool Block::valid()
  {
  return ( bufferp_ != 0 && begin_.line >= 0 && begin_ < end_ &&
           ( !RC::editor_options().rectangle_mode || begin_.col < end_.col ) );
  }


void Block::save_block_position()
  {
  saved_bufferp = bufferp_; saved_begin = begin_; saved_end = end_;
  }


void Block::restore_block_position()
  {
  bufferp_ = saved_bufferp; begin_ = saved_begin; end_ = saved_end;
  if( bufferp_ )
    { bufferp_->pvalid( begin_ ); bufferp_->pvalid( end_ ); }
  disable_marking();
  }


void Block::reset()
  {
  bufferp_ = 0; begin_ = end_ = Point(); disable_marking();
  }


void Block::set_block( Buffer & buffer, const Point & p1, const Point & p2 )
  {
  bufferp_ = &buffer; begin_ = p1; end_ = p2; disable_marking();
  }


void Block::set_begin( Buffer & buffer, const Point & p )
  {
  begin_ = p; disable_marking();
  if( &buffer != bufferp_ ) { bufferp_ = &buffer; end_ = Point(); }
  }


void Block::set_end( Buffer & buffer, const Point & p )
  {
  end_ = p; disable_marking();
  if( &buffer != bufferp_ ) { bufferp_ = &buffer; begin_ = Point(); }
  }


void Block::disable_marking() { anchor_ = Point(); }


bool Block::follow_marking( const Buffer & buffer, const Point & p )
  {
  static Point old_p;
  if( marking() && &buffer == bufferp_ && p != old_p )
    {
    if( p < anchor_ ) begin_ = p; else begin_ = anchor_;
    if( p > anchor_ ) end_ = p; else end_ = anchor_;
    old_p = p;
    return true;
    }
  return false;
  }


const char * Block::toggle_marking( Buffer & buffer, const Point & p )
  {
  const char * const msg1 = "Selection started.";
  const char * const msg2 = "Selection finished.";
  const char * const msg3 = "Selection cleared.";
  const char * msg = 0;
  const Buffer * const old_bufferp = bufferp_;
  if( marking() )
    {
    if( &buffer == bufferp_ )
      {
      if( valid() ) { disable_marking(); return msg2; }
      else { set_block( buffer, p, p ); return msg3; }
      }
    else { enable_marking( buffer, p ); msg = msg1; }
    }
  else
    {
    if( in_block_or_end( buffer, p ) )
      { set_block( buffer, p, p ); msg = msg3; }
    else { enable_marking( buffer, p ); msg = msg1; }
    }
  if( old_bufferp ) Screen::repaint( old_bufferp );
  return msg;
  }


bool Block::copy_block( Buffer & buffer, const Point & p, const bool move )
  {
  bool done = false;
  if( buffer.options.read_only || !buffer.pisvalid( p ) || !valid() ||
      ( move && bufferp_->options.read_only ) ||
      !bufferp_->pisvalid( begin_ ) || !bufferp_->pisvalid( end_ ) )
    return done;
  Point p_end;
  const bool same = ( &buffer == bufferp_ );
  buffer.reset_appendable();
  if( !RC::editor_options().rectangle_mode )
    {
    Basic_buffer tmp( *bufferp_, begin_, end_ );
    if( move ) { delete_block(); if( same ) buffer.force_append(); }
    p_end = p;
    done = buffer.pputb( p_end, tmp, tmp.bof(), tmp.eof() );
    }
  else
    {
    Basic_buffer tmp;
    Point p0 = tmp.bof();
    const int len = end_.col - begin_.col;
    for( int line = begin_.line; line <= end_.line; ++line )
      {
      Point p1( line, begin_.col ), p2( line, end_.col );
      bufferp_->pvalid( p1 ); bufferp_->pvalid( p2 );
      tmp.pputb( p0, *bufferp_, p1, p2 );
      for( int i = tmp.characters( p0.line ); i < len; ++i )
        tmp.pputc( p0, ' ' );
      tmp.pputc( p0, '\n' );
      }
    if( move ) { delete_block(); if( same ) buffer.force_append(); }
    for( int line = 0; line <= tmp.last_line(); ++line )
      {
      p0 = p; p0.line += line;
      for( int i = buffer.lines(); i <= p0.line; ++i )
        {
        Point dummy = buffer.eof();
        const bool ai = buffer.options.auto_indent;
        buffer.options.auto_indent = false;
        buffer.pputc( dummy, '\n' ); buffer.force_append();
        buffer.options.auto_indent = ai;
        }
      if( line >= tmp.last_line() ) break;
      for( int i = buffer.eol( p0 ).col; i < p0.col; ++i )
        { Point dummy = buffer.eol( p0 );
          buffer.pputc( dummy, ' ' ); buffer.force_append(); }
      Point p1( line, 0 ), p2 = tmp.eol( p1 );
      if( buffer.pputb( p0, tmp, p1, p2 ) ) { done = true; p_end = p0; }
      buffer.force_append();
      }
    }
  buffer.reset_appendable();
  if( done )
    { if( RC::editor_options().auto_unmark ) reset();
      else set_block( buffer, p, p_end ); }
  return done;
  }


bool Block::delete_block()
  {
  bool done = false;
  if( valid() )
    {
    Buffer * const bp = bufferp_;
    bufferp_ = 0;			// disable adjust functions on block
    bp->reset_appendable();
    if( !RC::editor_options().rectangle_mode )
      done = bp->pdelb( begin_, end_ );
    else
      {
      for( int line = end_.line; line >= begin_.line; --line )
        {
        Point p1( line, begin_.col ), p2( line, end_.col );
        bp->pvalid( p1 ); bp->pvalid( p2 );
        if( bp->pdelb( p1, p2 ) ) done = true;
        bp->force_append();
        }
      }
    bp->reset_appendable();
    bufferp_ = bp; end_ = begin_; disable_marking();
    }
  return done;
  }
