var Drone = require('./drone').Drone;

/************************************************************************
### Drone.sphere() method

Creates a sphere.

#### Parameters
 
 * block - The block the sphere will be made of.
 * radius - The radius of the sphere.

#### Example

To create a sphere of Iron with a radius of 10 blocks...

    sphere( blocks.iron, 10);

![sphere example](img/sphereex1.png)

Spheres are time-consuming to make. You *can* make large spheres (250 radius) but expect the
server to be very busy for a couple of minutes while doing so.

***/
Drone.extend( 'sphere', function( block, radius ) {
  var lastRadius = radius,
      slices = [ [ radius , 0 ] ],
      diameter = radius * 2,
      bm = this._getBlockIdAndMeta( block ),
      r2 = radius * radius,
      i = 0,
      newRadius,
      yOffset,
      sr,
      sh,
      v,
      h;

  if ( radius > 127 ) { 
    throw new Error('Sphere radius must be less than 128 blocks');
  }
  for ( i = 0; i <= radius; i++ ) {
    newRadius = Math.round( Math.sqrt( r2 - i * i ) );
    if ( newRadius == lastRadius ) {
      slices[ slices.length - 1 ][ 1 ]++;
    } else {
      slices.push( [ newRadius , 1 ] );
    }
    lastRadius = newRadius;
  }
  this.chkpt( 'sphere' );
  //
  // mid section
  //
  this.up( radius - slices[0][1] )
    .cylinder( block, radius, ( slices[0][1]*2 ) - 1, { blockType: bm[0], meta: bm[1] } )
    .down( radius - slices[0][1] );
  
  yOffset = -1;
  for ( i = 1; i < slices.length; i++ ) {
    yOffset += slices[i-1][1];
    sr = slices[i][0];
    sh = slices[i][1];
    v = radius + yOffset;
    h = radius - sr;
    // northern hemisphere
    this.up( v )
      .fwd( h )
      .right( h )
      .cylinder( block, sr, sh, { blockType: bm[0], meta: bm[1] } )
      .left( h )
      .back( h )
      .down( v );
    
    // southern hemisphere
    v = radius - ( yOffset + sh + 1 );
    this.up( v )
      .fwd( h )
      .right( h )
      .cylinder( block, sr, sh, { blockType: bm[0], meta: bm[1]} )
      .left( h )
      .back( h )
      .down( v );
  }
  return this.move( 'sphere' );
});
/************************************************************************
### Drone.sphere0() method

Creates an empty sphere.

#### Parameters
 
 * block - The block the sphere will be made of.
 * radius - The radius of the sphere.

#### Example

To create a sphere of Iron with a radius of 10 blocks...

    sphere0( blocks.iron, 10);

Spheres are time-consuming to make. You *can* make large spheres (250 radius) but expect the
server to be very busy for a couple of minutes while doing so.

***/
Drone.extend('sphere0', function(block,radius)
{
  var lastRadius = radius,
      slices = [ [ radius, 0 ] ],
      diameter = radius * 2,
      bm = this._getBlockIdAndMeta( block ),
      r2 = radius*radius,
      i,
      newRadius,
      sr,
      sh,
      v,
      h,
      len,
      yOffset;

  if ( radius > 127 ) { 
    throw new Error('Sphere radius must be less than 128 blocks');
  }

  for ( i = 0; i <= radius; i++ ) {
    newRadius = Math.round( Math.sqrt( r2 - i * i ) );
    if ( newRadius == lastRadius ) {
      slices[ slices.length - 1 ][ 1 ]++;
    } else {
      slices.push( [ newRadius, 1 ] );
    }
    lastRadius = newRadius;
  }
  this.chkpt( 'sphere0' );
  //
  // mid section
  //
  this.up( radius - slices[0][1] )
    .arc({ blockType: bm[0], 
           meta: bm[1], 
           radius: radius, 
           strokeWidth: 2,
           stack: (slices[0][1]*2)-1,
           fill: false
         })
    .down( radius - slices[0][1] );
  
  yOffset = -1;
  len = slices.length;
  for ( i = 1; i < len; i++ ) {
    yOffset += slices[i-1][1];
    sr = slices[i][0];
    sh = slices[i][1];
    v = radius + yOffset;
    h = radius-sr;
    // northern hemisphere
    // .cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]})
    this.up( v ).fwd( h ).right( h )
      .arc({
        blockType: bm[0],
        meta: bm[1],
        radius: sr,
        stack: sh,
        fill: false,
        strokeWidth: i < len - 1 ? 1 + ( sr - slices[ i + 1 ][ 0 ] ) : 1
      })
      .left( h ).back( h ).down( v );
    
    // southern hemisphere
    v = radius - ( yOffset + sh + 1 );
    this.up( v ).fwd( h ).right( h )
      .arc({
        blockType: bm[0],
        meta: bm[1],
        radius: sr,
        stack: sh,
        fill: false,
        strokeWidth: i < len - 1 ? 1 + ( sr - slices[ i + 1 ][ 0 ] ) : 1
      })
      .left( h ).back( h ). down( v );
  }
  this.move( 'sphere0' );

  return this;

});
/************************************************************************
### Drone.hemisphere() method

Creates a hemisphere. Hemispheres can be either north or south.

#### Parameters

 * block - the block the hemisphere will be made of.
 * radius - the radius of the hemisphere
 * northSouth - whether the hemisphere is 'north' or 'south'

#### Example

To create a wood 'north' hemisphere with a radius of 7 blocks...

    hemisphere(blocks.oak, 7, 'north');

![hemisphere example](img/hemisphereex1.png)

***/
Drone.extend( 'hemisphere', function( block, radius, northSouth ) {
  var lastRadius = radius,
      slices = [ [ radius, 0 ] ],
      diameter = radius * 2,
      bm = this._getBlockIdAndMeta(block),
      r2 = radius * radius,
      i = 0,
      newRadius;

  if ( radius > 255 ) { 
    throw new Error('Hemisphere radius must be less than 256 blocks');
  }

  for ( i = 0; i <= radius; i++ ) {
    newRadius = Math.round( Math.sqrt( r2 - i * i ) );
    if ( newRadius == lastRadius ) {
      slices[ slices.length - 1 ][ 1 ]++;
    } else {
      slices.push( [ newRadius, 1 ] );
    }
    lastRadius = newRadius;
  }
  this.chkpt( 'hsphere' );
  //
  // mid section
  //
  if ( northSouth == 'north' ) {
    this.cylinder( block, radius, slices[0][1], { blockType: bm[0], meta: bm[1] } );
  } else {
    this.up( radius - slices[0][1] )
      .cylinder( block, radius, slices[0][1], { blockType: bm[0], meta: bm[1] } )
      .down( radius - slices[0][1] );
  }
  
  var yOffset = -1;
  for ( i = 1; i < slices.length; i++ ) {
    yOffset += slices[i-1][1];
    var sr = slices[i][0];
    var sh = slices[i][1];
    var v = yOffset, h = radius-sr;
    if ( northSouth == 'north' ) {
      // northern hemisphere
      this.up( v ).fwd( h ).right( h )
        .cylinder( block, sr, sh, { blockType: bm[0], meta: bm[1] } )
        .left( h ).back( h ).down( v );
    } else {
      // southern hemisphere
      v = radius - ( yOffset + sh + 1 );
      this.up( v ).fwd( h ).right( h )
        .cylinder( block, sr, sh, { blockType: bm[0], meta: bm[1] } )
        .left( h ).back( h ).down( v );
    }
  }
  return this.move( 'hsphere' );
});
/************************************************************************
### Drone.hemisphere0() method

Creates a hollow hemisphere. Hemispheres can be either north or south.

#### Parameters

 * block - the block the hemisphere will be made of.
 * radius - the radius of the hemisphere
 * northSouth - whether the hemisphere is 'north' or 'south'

#### Example

To create a glass 'north' hemisphere with a radius of 20 blocks...

    hemisphere0(blocks.glass, 20, 'north');

![hemisphere example](img/hemisphereex2.png)

***/
Drone.extend( 'hemisphere0', function( block, radius, northSouth ) {

  if ( radius > 255 ) { 
    throw new Error('Hemisphere radius must be less than 256 blocks');
  }

/*
  return this.hemisphere( block, radius, northSouth)
    .fwd().right().up( northSouth == 'north' ? 0 : 1 )
    .hemisphere( 0, radius-1, northSouth )
    .back().left().down( northSouth == 'north' ? 0 : 1 );
*/
  var lastRadius = radius,
      slices = [ [ radius, 0 ] ],
      diameter = radius * 2,
      bm = this._getBlockIdAndMeta(block),
      r2 = radius * radius,
      i = 0,
      len,
      newRadius;

  if ( radius > 255 ) { 
    throw new Error('Hemisphere radius must be less than 256 blocks');
  }


  for ( i = 0; i <= radius; i++ ) {
    newRadius = Math.round( Math.sqrt( r2 - i * i ) );
    if ( newRadius == lastRadius ) {
      slices[ slices.length - 1 ][ 1 ]++;
    } else {
      slices.push( [ newRadius, 1 ] );
    }
    lastRadius = newRadius;
  }
  this.chkpt( 'hsphere0' );
  //
  // mid section
  //
  if ( northSouth == 'north' ) {

    this.arc({ 
      blockType: bm[0], 
      meta: bm[1],
      radius: radius,
      strokeWidth: 1,
      stack: slices[0][1],
      fill: false
    });
  } else {
    this.up( radius - slices[0][1] );

    this.arc({
      blockType: bm[0],
      meta: bm[1],
      radius: radius,
      strokeWidth: 1,
      stack: slices[0][1],
      fill: false
    });

    this.down( radius - slices[0][1] );
  }
  
  var yOffset = -1;
  len = slices.length;
  for ( i = 1; i < slices.length; i++ ) {
    yOffset += slices[i-1][1];
    var sr = slices[i][0];
    var sh = slices[i][1];
    var v = yOffset, h = radius-sr;
    if ( northSouth == 'north' ) {
      // northern hemisphere
      this.up( v )
        .fwd( h )
        .right( h );

      this.arc( { 
        blockType: bm[0],
        meta: bm[1],
        radius: sr,
        stack: sh,
        fill: false,
        strokeWidth: i < len - 1 ? 1 + ( sr - slices[ i + 1 ][ 0 ] ) : 1
      } );

      this.left( h )
        .back( h )
        .down( v );
    } else {
      // southern hemisphere
      v = radius - ( yOffset + sh + 1 );
      this.up( v )
        .fwd( h )
        .right( h );

      this.arc({
        blockType: bm[0],
        meta: bm[1],
        radius: sr,
        stack: sh,
        fill: false,
        strokeWidth: i < len - 1 ? 1 + ( sr - slices[ i + 1 ][ 0 ] ) : 1
      });

      this.left( h )
        .back( h )
        .down( v );
    }
  }
  return this.move( 'hsphere0' );

});
