# plot_style.rb : an abstraction for styles of whole plots
# Copyright (C) 2008  Vincent Fourmond
# 
# 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 2 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, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA

require 'CTioga/utils'
require 'CTioga/debug'
require 'CTioga/log'
require 'CTioga/axes'
require 'CTioga/legends'

module CTioga

  Version::register_svn_info('$Revision: 875 $', '$Date: 2009-02-12 21:34:25 +0100 (Thu, 12 Feb 2009) $')

  # The PlotStyle class is an abstraction for whole-plot styles,
  # storing for instance information about axes/ticks (delegated
  # to an EdgesAndAxes object), background color, and so on...
  class PlotStyle

    include Debug
    include Log
    
    # An EdgesAndAxes object attached to the plot
    attr_accessor :edges

    # Various textual objects laying around:
    attr_accessor :title, :xlabel, :ylabel

    # X and Y tick labels
    attr_accessor :xticks, :yticks

    # The target SubPlot object
    attr_accessor :target_plot

    # The background color, if applicable
    attr_accessor :background_color

    # The style with which to display legends
    attr_accessor :legend_style

    # A watermark in the background
    attr_accessor :watermark_text

    # The color of the watermark
    attr_accessor :watermark_color

    def initialize(target_plot = nil, subplot = false)
      @title = Label.new(:title)
      @title.label = "A nice plot" unless subplot
      
      @xlabel = Label.new(:xlabel)
      @xlabel.label = "$x$" unless subplot
      
      @ylabel = Label.new(:ylabel)
      @ylabel.label = "$y$" unless subplot

      @xticks = TickLabels.new(:xaxis_numeric_label)
      @yticks = TickLabels.new(:yaxis_numeric_label)

      @edges = EdgesAndAxes.new(@xticks, @yticks)

      @target_plot = target_plot
      
      @background_color = false

      @legend_style = LegendStyle.new
    end

    # Displays edges and ticks for the given object
    def show_edges(t, container = @target_plot)
      @edges.setup(t, container)
      for l in [@xticks, @yticks]
        l.show(t)
      end
    end

    # Sets up the various parameters for titles and labels
    def show_labels(t, container = @target_plot)
      # Show labels
      for l in [@title, @xlabel, @ylabel]
        l.show(t)
        debug "Extension -> #{l.extension(t).inspect}"
      end
    end

    # Displays background (background color, grids)
    def show_background(t, container = @target_plot)
      if @background_color
        t.fill_color = @background_color
        t.fill_frame
      end

      # We draw a watermark text at the back of the plot.
      if @watermark_text
        x = t.convert_frame_to_figure_x(0.5)
        y = t.convert_frame_to_figure_y(0.5)

        delta_y = t.convert_frame_to_figure_dy(0.15)
        text_scale = delta_y/t.default_text_height_dy

        lines = @watermark_text.split(/\n|\\n/)
        i = + (lines.size-1)/2.0
        for text in lines 
          t.show_marker('string' => text,
                        'color' => @watermark_color || [0.5,0.5,0.5],
                        'x' => x, 'y' => y + delta_y * i,
                        'scale' => text_scale)
          i -= 1
        end
      end
      
      edges.show_axis_lines(t, container)
    end

    # Hides axis and all edges for the given sides.
    # Careful, as this also disables the children's axes and edges
    def disable_all_axis_and_edges(*which)
      for w in which
        @edges.set_edges_visibility(w, false)
        @edges.axis(w).visible = false
      end
    end


    # Sets quickly all X and Y labels
    def set_xy_labels(xlabel, ylabel)
      @xlabel.label = xlabel
      @ylabel.label = ylabel
    end
    

    def hide_axis_and_edges(*which)
      for w in which
        @edges.set_axis_and_edges_style(w, AXIS_HIDDEN)
      end
    end


    # TODO (IMPORTANT !): simplify the notion of edges and axes: the user
    # shouldn't need to know the difference between edges and axes.


    # A very nice-and-convenient way to set quickly axes properties.
    # _which_ is either :x or :y, *or* :left, :top, :right, and :bottom,
    # in which case the style applies only to the given edge or axis
    #
    def set_axis_style(which, style)
      # We set various parameters according to the given style
      for s in style.split(/,/)
        case s
        when /none/i
          hide_axis_and_edges(which)
        when /[xy]?=?0/i, /ori?g(in)?/i
          @edges.set_edges_visibility(which, false)
          @edges.axis(which).ticks_inside = false
          @edges.axis(which).ticks_outside = true
          @edges.axis(which).loc = (which == :x ? AT_Y_ORIGIN : AT_X_ORIGIN)
        when /both/             # Both sides visible
          # We make sure the edges are visible
          @edges.set_edges_visibility(which, true)
        when /left/i, /right/i
          if which == :x
            warn "Axis style #{s} can only apply to the Y axis, ignoring"
          else
            @edges.set_edges_visibility(which, false)
            @edges.axis(which).loc = ( s =~ /left/i ? LEFT : RIGHT)
          end
        when /top/i, /bottom/i
          if which == :y
            warn "Axis style #{s} can only apply to the X axis, ignoring"
          else
            @edges.set_edges_visibility(which, false)
            @edges.axis(which).loc = ( s =~ /top/i ? TOP : BOTTOM)
          end
          # Now, stylistic information rather than position:
        when /hidden/i
          @edges.set_axis_and_edges_style(which, AXIS_HIDDEN)
        when /line/i
          @edges.set_axis_and_edges_style(which, AXIS_LINE_ONLY)
        when /ticks/i
          @edges.set_axis_and_edges_style(which, AXIS_WITH_TICKS_ONLY)
        when /majornum/i
          @edges.
            set_axis_and_edges_style(which, 
                                     AXIS_WITH_MAJOR_TICKS_AND_NUMERIC_LABELS)
        when /major/i
          @edges.set_axis_and_edges_style(which, AXIS_WITH_MAJOR_TICKS_ONLY)
        when /full/i
          @edges.set_axis_and_edges_style(which, 
                                          AXIS_WITH_TICKS_AND_NUMERIC_LABELS)
        else
          warn "Axis style #{s} not understood, ignoring"
        end
      end
    end

    # Creates a deep copy of the style object, and give it a new
    # container.
    def deep_copy(new_target = nil)
      old_target = @target_plot
      @target_plot = nil
      new_object = Marshal::load(Marshal::dump(self))
      new_object.target_plot = new_target
      @target_plot = old_target
      return new_object
    end

      

  end

end
