<?php
/**
 * Copyright (C) 2014-2015 Graham Breach
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * For more information, please contact <graham@goat1000.com>
 */

require_once 'SVGGraphPieGraph.php';

class ExplodedPieGraph extends PieGraph {

  protected $largest_value;
  protected $smallest_value;

  /**
   * Calculates reduced radius of pie
   */
  protected function Calc()
  {
    parent::Calc();
    $this->explode_amount = min($this->radius_x - 10, $this->radius_y - 10,
      max(2, (int)$this->explode_amount));
    $this->radius_y -= $this->explode_amount;
    $this->radius_x -= $this->explode_amount;
  }

  /**
   * Returns a single slice of pie
   */
  protected function GetSlice($item, $angle_start, $angle_end, $radius_x,
    $radius_y, &$attr, $single_slice)
  {
    if($single_slice)
      return parent::GetSlice($item, $angle_start, $angle_end, $radius_x,
        $radius_y, $attr, $single_slice);

    $x_start = $y_start = $x_end = $y_end = 0;
    $angle_start += $this->s_angle;
    $angle_end += $this->s_angle;
    $this->CalcSlice($angle_start, $angle_end, $radius_x, $radius_y,
      $x_start, $y_start, $x_end, $y_end);
    $outer = ($angle_end - $angle_start > M_PI ? 1 : 0);
    $sweep = ($this->reverse ? 0 : 1);

    // find explosiveness
    list($xo, $yo) = $this->GetExplode($item, $angle_start, $angle_end);
    $xc = $this->x_centre + $xo;
    $yc = $this->y_centre + $yo;
    $x_start += $xo;
    $x_end += $xo;
    $y_start += $yo;
    $y_end += $yo;
    $attr['d'] = "M{$xc},{$yc} L$x_start,$y_start " .
      "A{$radius_x} {$radius_y} 0 $outer,$sweep $x_end,$y_end z";
    return $this->Element('path', $attr);
  }

  /**
   * Returns the x,y offset caused by explosion
   */
  protected function GetExplode($item, $angle_start, $angle_end)
  {
    $range = $this->largest_value - $this->smallest_value;
    switch($this->explode) {
    case 'none' :
      $diff = 0;
      break;
    case 'all' :
      $diff = $range;
      break;
    case 'large' :
      $diff = $item->value - $this->smallest_value;
      break;
    default :
      $diff = $this->largest_value - $item->value;
    }
    $amt = $range > 0 ? $diff / $range : 0;
    $iamt = $item->Data('explode');
    if(!is_null($iamt))
      $amt = $iamt;
    $explode = $this->explode_amount * $amt;

    $a = $angle_end - $angle_start;
    $a_centre = $angle_start + ($angle_end - $angle_start) / 2;
    $xo = $explode * cos($a_centre);
    $yo = $explode * sin($a_centre);
    if($this->reverse)
      $yo = -$yo;

    return array($xo, $yo);
  }

  /**
   * Returns the position for the label
   */
  public function DataLabelPosition($dataset, $index, &$item, $x, $y, $w, $h,
    $label_w, $label_h)
  {
    $pos = parent::DataLabelPosition($dataset, $index, $item, $x, $y, $w, $h,
      $label_w, $label_h);

    if(isset($this->slice_info[$index])) {
      list($xo, $yo) = $this->GetExplode($item,
        $this->slice_info[$index]->start_angle + $this->s_angle,
        $this->slice_info[$index]->end_angle + $this->s_angle);

      list($x1, $y1) = explode(' ', $pos);
      if(is_numeric($x1) && is_numeric($y1)) {
        $x1 += $xo;
        $y1 += $yo;
      } else {
        // this shouldn't happen, but just in case
        $x1 = $this->centre_x + $xo;
        $y1 = $this->centre_y + $yo;
      }

      $pos = "$x1 $y1";
    } else {
      $pos = 'middle centre';
    }
    return $pos;
  }

  /**
   * Checks that the data are valid
   */
  protected function CheckValues()
  {
    parent::CheckValues();

    $this->largest_value = $this->GetMaxValue();
    $this->smallest_value = $this->largest_value;

    // want smallest non-0 value
    foreach($this->values[0] as $item)
      if($item->value < $this->smallest_value)
        $this->smallest_value = $item->value;
  }

}