/*  GNU ddrescue - Data recovery tool
    Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
    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/>.
*/

#define _FILE_OFFSET_BITS 64

#include <algorithm>
#include <climits>
#include <cstdio>
#include <string>
#include <vector>
#include <stdint.h>

#include "block.h"
#include "ddrescue.h"


namespace {

void input_pos_error( const long long pos, const long long isize )
  {
  char buf[80];
  snprintf( buf, sizeof buf, "can't start reading at pos %lld", pos );
  show_error( buf );
  snprintf( buf, sizeof buf, "input file is only %lld bytes long", isize );
  show_error( buf );
  }

} // end namespace


// Align pos to next boundary if size is big enough
//
void Block::align_pos( const int hardbs ) throw()
  {
  if( hardbs > 1 )
    {
    const int disp = hardbs - ( pos_ % hardbs );
    if( disp < hardbs && size_ > disp )
      { pos_ += disp; size_ -= disp; }
    }
  }


// Align end to previous boundary if size is big enough
//
void Block::align_end( const int hardbs ) throw()
  {
  if( hardbs > 1 && size_ > 0 )
    {
    const long long new_end = end() - ( end() % hardbs );
    if( pos_ < new_end ) size_ = new_end - pos_;
    }
  }


void Block::crop( const Block & b ) throw()
  {
  const long long p = std::max( pos_, b.pos_ );
  const long long s = std::max( 0LL, std::min( end(), b.end() ) - p );
  pos_ = p; size_ = s;
  }


bool Block::join( const Block & b )
  {
  if( this->follows( b ) ) pos_ = b.pos_;
  else if( !b.follows( *this ) ) return false;
  size_ += b.size_;
  if( size_ < 0 || size_ > LLONG_MAX - pos_ )
    internal_error( "size overflow joining two Blocks" );
  return true;
  }


Block Block::split( long long pos, const int hardbs )
  {
  if( hardbs > 1 ) pos -= pos % hardbs;
  if( pos_ < pos && end() > pos )
    {
    const Block b( pos_, pos - pos_ );
    pos_ = pos; size_ -= b.size_;
    return b;
    }
  return Block( 0, 0 );
  }


void Domain::crop( const Block & b )
  {
  for( unsigned int i = block_vector.size(); i > 0; )
    {
    --i;
    block_vector[i].crop( b );
    if( block_vector[i].size() <= 0 )
      block_vector.erase( block_vector.begin() + i );
    }
  }


bool Domain::crop_by_file_size( const long long isize )
  {
  if( isize > 0 )
    for( unsigned int i = 0; i < block_vector.size(); ++i )
      {
      if( block_vector[i].pos() >= isize )
        {
        if( i == 0 )
          { input_pos_error( block_vector[i].pos(), isize ); return false; }
        block_vector.erase( block_vector.begin() + i, block_vector.end() );
        break;
        }
      if( block_vector[i].end() > isize )
        block_vector[i].size( isize - block_vector[i].pos() );
      }
  return true;
  }
