//////////////////////////////////////////////////
// (c) MMIII Sven Reichard
// 

# include <Bitboard.h>
# include <iomanip>
# include <Directions/Direction.h>
namespace Alice
{

  std::vector<unsigned long long> 
  Bitboard::setMask(64);
  
  std::vector<unsigned long long> 
  Bitboard::clearMask(64);

  std::vector<unsigned long long> 
  Bitboard::setMask90(64);
  
  std::vector<unsigned long long> 
  Bitboard::clearMask90(64);

  std::vector<unsigned long long> 
  Bitboard::setMask45(64);
  
  std::vector<unsigned long long> 
  Bitboard::clearMask45(64);

  std::vector<unsigned long long> 
  Bitboard::setMask135(64);
  
  std::vector<unsigned long long> 
  Bitboard::clearMask135(64);

  std::vector<unsigned long long>
  Bitboard::spreadByte( 256 );

  std::vector<Bitboard>
  Bitboard::knightAttacks(0 );
  std::vector<Bitboard>
  Bitboard::kingAttacks(0 );
  
  std::vector<Bitboard>
  Bitboard::upDiagonalMask(16);

  std::vector<Bitboard>
  Bitboard::downDiagonalMask(16);

  std::vector<int>
  Bitboard::firstBit(256*256);

  Bitboard
  Bitboard::fileAttacks[256][64];
  
  Bitboard
  Bitboard::rankAttacks[256][64];

  Bitboard
  Bitboard::upDiagonalAttacks[256][64];
  
  Bitboard
  Bitboard::downDiagonalAttacks[256][64];

  unsigned char 
  Bitboard::attacks[256][8];

  Bitboard::Initializer 
  Bitboard::initializer;
  
  std::vector<int>
  Bitboard::upDiagonalNumber(64);

  std::vector<int>
  Bitboard::downDiagonalNumber(64);

  void
  Bitboard::initializeClass()
  {
    for (int i = 0; i < 64; i++)
      {
	setMask[i] = 1ull << i;
	clearMask[i] = ~ setMask[i];
      }
    for (int i = 0; i < 64; i++)
      {
	Square sq( i );
	setMask90[i] = setMask[sq.flippedNumber()];
	setMask45[i] = setMask[sq.getNumberRotated45()];
	setMask135[i] = setMask[sq.getNumberRotated135()];
	
	clearMask90[i] = clearMask[sq.flippedNumber()];
	clearMask45[i] = clearMask[sq.getNumberRotated45()];
	clearMask135[i] = clearMask[sq.getNumberRotated135()];
	
      }
    for (unsigned int  i = 0; i < 256; i++)
      spreadByte[i] = 0x0101010101010101ull * i;
    for (int i = 0; i < 16; i++)
      {
	upDiagonalMask[i] = getUpDiagonalMask(i );
	downDiagonalMask[i] = getDownDiagonalMask(i);
      }
    for (unsigned int i = 0; i < firstBit.size(); i++)
      {
	int res = 0;
	int mask = 1;
	while (res < 16 && ! (mask & i))
	  {
	    res ++;
	    mask <<= 1;
	  }
	firstBit[i] = res;
      }
    for (int byte = 0; byte < 256; byte++)
      for (int x = 0; x < 8; x++)
	attacks[byte][x] = calculateAttacks( byte, x );
    for (int byte = 0; byte < 256; byte++)
      for (int x = 0; x < 64; x++)
	{
	  rankAttacks[byte][x] = calculateRankAttacks(byte, x );
	  fileAttacks[byte][x] = calculateFileAttacks(byte, x );
	  upDiagonalAttacks[byte][x] = calculateUpDiagonalAttacks(byte, x);
	  downDiagonalAttacks[byte][x] = calculateDownDiagonalAttacks(byte, x);
	}
    
  };

  Bitboard::Bitboard( unsigned long long n )
    : number( n )
  {
    
  };
  
  Bitboard::~Bitboard()
  {
    
  };
  /*
  Bitboard::operator unsigned long long() const
  {
    return number;
  };
  */

  Bitboard
  Bitboard::operator~() const
  {
    return ~number;
  };

  void
  Bitboard::write( std::ostream& out ) const
  {
    out << std::hex << std::setw(16)<<std::setfill('0') << number << std::dec;
  };
  
  std::ostream&
  operator<<(std::ostream& out, const Bitboard& b )
  {
    b.write( out );
    return out;
  };
  
  unsigned char
  Bitboard::getByte( int which ) const
  {
    return number >> (8*which);
  };
  
  bool
  Bitboard::operator[]( const Square& sq ) const
  {
    return (number & setMask[sq.number()]) != 0;
  };
  
  int
  Bitboard::getFirstBit() const
  {
    unsigned long long n = number;
    int result = 0;
    if (!(n & 0xffffffff))
      {
	result = 32;
	n >>= 32;
      }
    if (!(n&0xffff))
      {
	result += 16;
	n >>= 16;
      }
    return result + firstBit[n&0xffff];
    return 64;
  };
  
  int
  Bitboard::getLastBit() const
  {
    int i = 63;
    while (i >= 0 && !(*this)[i])
      i--;
    return i;
  };

  unsigned char
  Bitboard::calculateAttacks( unsigned char line, int x )
  {

    unsigned char result = 0;
    int pos = x & 7;
    unsigned char mask = 1 << pos;
    do
      {
	mask <<= 1;
	result |= mask;
      } while ( mask && !(line & mask) );
    if (!x)
      return result;
    pos = x&7;
    mask = 1 << pos;
    do
      {
	mask >>= 1;
	result |= mask;
      } while ( mask && !(line & mask ) );
    return result;
    
  };

  
  Bitboard
  Bitboard::calculateFileAttacks( unsigned char line, const Square& sq )
  {
    Bitboard result = getAttacks( line, sq.number() & 7);
    result.number <<= (sq.number() & 0xf8 );
    return result;
  };

  Bitboard
  Bitboard::calculateRankAttacks( unsigned char line, const Square& source )
  {

    int squareNumber = source.flippedNumber();;
    unsigned char attacks = getAttacks(line, squareNumber&7);
    Bitboard result;
    result.setRank(squareNumber >> 3, attacks);
    return result;
    
  };

  const Bitboard
  Bitboard::calculateUpDiagonalAttacks( const unsigned char& byte, 
					const Square& source )
  {
    int diag = source.getUpDiagonalNumber();
    unsigned char attackByte = getAttacks(byte, source.rank());
    Bitboard result(spreadByte[attackByte]);
    result &= upDiagonalMask[ diag ];
    return result;
  };

  const Bitboard
  Bitboard::calculateDownDiagonalAttacks( const unsigned char& byte, 
					  const Square& source )
  {
    int diag = source.getDownDiagonalNumber();
    unsigned char attackByte = getAttacks(byte, source.rank());
    Bitboard result(spreadByte[attackByte]);
    result &= downDiagonalMask[ diag ];
    return result;
  };
  Bitboard
  Bitboard::calculateKingAttacks( const Square& source )
  {
    Bitboard result;
    for (std::list<Direction*>::const_iterator it =
	   Direction::allInstances().begin();
	 it != Direction::allInstances().end();
	 ++ it)
      if ((*it)->isOrthogonal() || (*it)->isDiagonal())
	{
	  Square dest = (*it)->appliedTo(source);
	  if (dest.isValid())
	    result.set(dest);
	}
    return result;
  };
  Bitboard
  Bitboard::getKingAttacks( const Square& source )
  {
    // this can't be done in the class initialization, since we
    // depend on the class Direction to be already initialized.
    if (! kingAttacks.size())
      {
	kingAttacks.resize( 64 );
	for (int i = 0; i < 64; i++)
	  kingAttacks[i] = calculateKingAttacks(i);
      }
    return kingAttacks[ source.number() ];
  };
  Bitboard
  Bitboard::calculateKnightAttacks( const Square& source )
  {
    Bitboard result;
    for (std::list<Direction*>::const_iterator it =
	   Direction::allInstances().begin();
	 it != Direction::allInstances().end();
	 ++ it)
      if ((*it)->isKnightDirection())
	{
	  Square dest = (*it)->appliedTo(source);
	  if (dest.isValid())
	    result.set(dest);
	}
    return result;
  };
  const Bitboard&
  Bitboard::getKnightAttacks( const Square& source )
  {
    if (! knightAttacks.size())
      {
	knightAttacks.resize( 64 );
	for (int i = 0; i < 64; i++)
	  knightAttacks[i] = calculateKnightAttacks(i);
      }
    return knightAttacks[ source.number() ];
  }
  void
  Bitboard::setRank( int which, unsigned char byte )
  {
# if 1
    unsigned char one = byte & 1;
    byte &= 0xfe;
    number = 0x02040810204081ull;
    number <<= which;
    number *= byte;
    (*this) &= getRankMask(which);
    if (one)
      set(which);
    return;
# endif
    unsigned char mask = 1;
    for (int i = which; i < 64; i+=8, mask<<= 1 )
      if (byte & mask)
	set(i);
  };
  
  Bitboard
  Bitboard::getRankMask( int which )
  {
    return 0x0101010101010101ull << which;
  };

  Bitboard
  Bitboard::getDownDiagonalMask( int which )
  {
    Bitboard result;
    for (int i = 0; i < 64; i++)
      {
	Square sq(i);
	if (sq.getDownDiagonalNumber() == which )
	  result.set(sq);
      }
    return result;
  };

  Bitboard
  Bitboard::getUpDiagonalMask( int which )
  {
    unsigned long long result = 0x8040201008040201ull;
    int shift = which - 8;
    if (shift < 0)
      return result << (-8*shift);
    return result >> (8*shift);
  };

  BitboardIterator::BitboardIterator( const Bitboard& b )
    : board( b )
  {

  };

  BitboardIterator::operator bool() const
  {
    return board.number != 0;
  };
  
  Square
  BitboardIterator::operator*() const
  {
    return board.getFirstBit();
  };

  BitboardIterator&
  BitboardIterator::operator++()
  {
    board.clearLastBit();
    return *this;
  };

}; // namespace Alice
