#!/usr/bin/env python
#
# Copyright 2008 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Code for pie charts."""

import warnings

from mapreduce.lib.graphy import common
from mapreduce.lib.graphy import util


class Segment(common.DataSeries):
  """A single segment of the pie chart.

  Object attributes:
    size: relative size of the segment
    label: label of the segment (if any)
    color: color of the segment (if any)
  """
  def __init__(self, size, label=None, color=None):
    if label is not None and util._IsColor(label):
      warnings.warn('Your code may be broken! '
                    'Label looks like a hex triplet; it might be a color.  '
                    'The old argument order (color before label) is '
                    'deprecated.',
                    DeprecationWarning, stacklevel=2)
    style = common._BasicStyle(color)
    super(Segment, self).__init__([size], label=label, style=style)
    assert size >= 0

  def _GetSize(self):
    return self.data[0]

  def _SetSize(self, value):
    assert value >= 0
    self.data[0] = value

  size = property(_GetSize, _SetSize,
                  doc = """The relative size of this pie segment.""")

  # Since Segments are so simple, provide color for convenience.
  def _GetColor(self):
    return self.style.color

  def _SetColor(self, color):
    self.style.color = color

  color = property(_GetColor, _SetColor,
                   doc = """The color of this pie segment.""")


class PieChart(common.BaseChart):
  """Represents a pie chart.

  The pie chart consists of a single "pie" by default, but additional pies
  may be added using the AddPie method. The Google Chart API will display
  the pies as concentric circles, with pie #0 on the inside; other backends
  may display the pies differently.
  """

  def __init__(self, points=None, labels=None, colors=None):
    """Constructor for PieChart objects.

    Creates a pie chart with a single pie.

    Args:
      points: A list of data points for the pie chart;
              i.e., relative sizes of the pie segments
      labels: A list of labels for the pie segments.
              TODO: Allow the user to pass in None as one of
              the labels in order to skip that label.
      colors: A list of colors for the pie segments, as hex strings
              (f.ex. '0000ff' for blue). If there are less colors than pie
              segments, the Google Chart API will attempt to produce a smooth
              color transition between segments by spreading the colors across
              them.
    """
    super(PieChart, self).__init__()
    self.formatters = []
    self._colors = None
    if points:
      self.AddPie(points, labels, colors)

  def AddPie(self, points, labels=None, colors=None):
    """Add a whole pie to the chart.

    Args:
      points: A list of pie segment sizes
      labels: A list of labels for the pie segments
      colors: A list of colors for the segments. Missing colors will be chosen
          automatically.
    Return:
      The index of the newly added pie.
    """
    num_colors = len(colors or [])
    num_labels = len(labels or [])
    pie_index = len(self.data)
    self.data.append([])
    for i, pt in enumerate(points):
      label = None
      if i < num_labels:
        label = labels[i]
      color = None
      if i < num_colors:
        color = colors[i]
      self.AddSegment(pt, label=label, color=color, pie_index=pie_index)
    return pie_index

  def AddSegments(self, points, labels, colors):
    """DEPRECATED."""
    warnings.warn('PieChart.AddSegments is deprecated. Call AddPie instead. ',
                  DeprecationWarning, stacklevel=2)
    num_colors = len(colors or [])
    for i, pt in enumerate(points):
      assert pt >= 0
      label = labels[i]
      color = None
      if i < num_colors:
        color = colors[i]
      self.AddSegment(pt, label=label, color=color)

  def AddSegment(self, size, label=None, color=None, pie_index=0):
    """Add a pie segment to this chart, and return the segment.

    size: The size of the segment.
    label: The label for the segment.
    color: The color of the segment, or None to automatically choose the color.
    pie_index: The index of the pie that will receive the new segment.
      By default, the chart has one pie (pie #0); use the AddPie method to
      add more pies.
    """
    if isinstance(size, Segment):
      warnings.warn("AddSegment(segment) is deprecated.  Use AddSegment(size, "
                    "label, color) instead",  DeprecationWarning, stacklevel=2)
      segment = size
    else:
      segment = Segment(size, label=label, color=color)
    assert segment.size >= 0
    if pie_index == 0 and not self.data:
      # Create the default pie
      self.data.append([])
    assert (pie_index >= 0 and pie_index < len(self.data))
    self.data[pie_index].append(segment)
    return segment

  def AddSeries(self, points, color=None, style=None, markers=None, label=None):
    """DEPRECATED

    Add a new segment to the chart and return it.

    The segment must contain exactly one data point; all parameters
    other than color and label are ignored.
    """
    warnings.warn('PieChart.AddSeries is deprecated.  Call AddSegment or '
                  'AddSegments instead.', DeprecationWarning)
    return self.AddSegment(Segment(points[0], color=color, label=label))

  def SetColors(self, *colors):
    """Change the colors of this chart to the specified list of colors.

    Note that this will completely override the individual colors specified
    in the pie segments. Missing colors will be interpolated, so that the
    list of colors covers all segments in all the pies.
    """
    self._colors = colors
