%  plot2gp.m
%  
%  Copyright 2013 Sebastian Vorköper <sv@fogman.de>
%  
%  Version: 24.10.2013
%
%  Redistribution and use in source and binary forms, with or without
%  modification, are permitted provided that the following conditions are
%  met:
%  
%  * Redistributions of source code must retain the above copyright
%    notice, this list of conditions and the following disclaimer.
%  * Redistributions in binary form must reproduce the above
%    copyright notice, this list of conditions and the following disclaimer
%    in the documentation and/or other materials provided with the
%    distribution.
%  
%  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
%  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
%  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
%  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
%  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
%  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
%  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
%  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
%  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
%  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
%  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
%  
% ----------------------------------------------------------------------
%
% The user definitions in user_defs are optional !
% All user definitions which are passed to the function will overwrite
% the matlab internal plot definitions !!!
%
% user_defs.size                = [x, y]  .. Gnuplot set size
% Set plot sizes and positions
% user_defs.plot(pl).plotsize   = [x, y]  .. Gnuplot size of the diferent plots
% user_defs.plot(pl).plotorigin = [x, y]  .. Gnuplot origins of the plots
% Set Margins
% user_defs.plot(pl).lmargin    = lmargin .. Gnuplot lmargin of the plot
% user_defs.plot(pl).rmargin    = rmargin .. Gnuplot rmargin of the plot
% user_defs.plot(pl).tmargin    = tmargin .. Gnuplot tmargin of the plot
% user_defs.plot(pl).bmargin    = bmargin .. Gnuplot bmargin of the plot
% Set ranges
% user_defs.plot(pl).xrange     = [min, max] .. Gnuplot xrange of the plot
% user_defs.plot(pl).x2range    = [min, max] .. Gnuplot x2range of the plot
% user_defs.plot(pl).yrange     = [min, max] .. Gnuplot yrange of the plot
% user_defs.plot(pl).y2range    = [min, max] .. Gnuplot y2range of the plot
% user_defs.plot(pl).zrange     = [min, max] .. Gnuplot zrange of the plot
% Set labels
% user_defs.plot(pl).xlabel     = string  .. Gnuplot xlabel of the plot
% user_defs.plot(pl).x2label    = string  .. Gnuplot x2label of the plot
% user_defs.plot(pl).ylabel     = string  .. Gnuplot ylabel of the plot
% user_defs.plot(pl).y2label    = string  .. Gnuplot y2label of the plot
% user_defs.plot(pl).zlabel     = string  .. Gnuplot zlabel of the plot
% User specific options, which are placed at the beginning at a Gnuplot
% document (global options)
% user_defs.user_specific       = string  .. Any Gnuplot command
% User specific options, which are placed before each Gnuplot plot
% user_defs.plot(pl).user_specific = string  .. Any Gnuplot command


function plot2gp( filename, figure_handle, user_defs )

  % check number of arguments
  if nargin > 3
    error( 'Wrong number of input arguments!' );
  end
  
  % Check if user_defs was NOT given
  if nargin == 2
    user_defs = [];
  end
  
  % check for filename
  if ~ischar(filename)
    error( 'First argument must be a string!' );
  end
  
  % check if the second argument is a figure handle
  if ~ishandle(figure_handle)
    error( 'Second argument must be a figure handle!' );
  end
  
  % Are we using Octave or Matlab
  if ~isempty( ver('MATLAB') )
     Env = 'MATLAB';
  else
     Env = 'OCTAVE';
  end
  
  % FIRST: Do some nasty things ;)
  % Normalize PaperSize and Units!!!
  set( figure_handle, 'PaperUnits','Normalized' );
  set( figure_handle, 'Units','Normalized' );
  % Show all hidden handles -> Needed to get the legend entries !!!
  % Afterwards, the annotation layer can be found!
  set(0, 'ShowHiddenHandles', 'On');
  ax_hndl_all     = findobj( figure_handle , 'Type', 'axes' );
  ax_hndl_legends = findobj( ax_hndl_all, 'Tag', 'legend' );
  ax_hndl         = setdiff( ax_hndl_all, ax_hndl_legends );
  % Show only the visible layers !!! 
  set(0, 'ShowHiddenHandles', 'Off');
  
  % Create an axes object handle, which includes all plots
  % (incl. suplots)
  ax_obj = get_axes_objects( ax_hndl, ax_hndl_legends, Env );
  
  % Open File
  fid = fopen( filename, 'w' );
  
  % Write Header information
  file_write_header( fid );
  
  % Write the axes information ( multiplot?, grid on/off?, etc. )
  file_write_axes( fid, ax_obj, user_defs );
  
  % Write Outer information
  file_write_outer( fid );
  
  % Close File
  fclose( fid );
end

% -----------------------------------------------------------------
% Return an object with the axes(s) information
% -----------------------------------------------------------------
function ax_obj = get_axes_objects( axes_handles, ax_hndl_legends, Env )

  % From figure to axes to plot handle
  % axes_handles = get(figure_handle, 'Children');
  
  % Get all necessary information for the axes (incl. all plots)
  for ax_obj_idx = 1:length( axes_handles )
    % Get axes position (i.e. position of the plot inside the figure)
    ax_obj(ax_obj_idx).position = get( axes_handles(ax_obj_idx), 'Position' );
    ax_obj(ax_obj_idx).title    = get( get( axes_handles(ax_obj_idx), 'Title' ), 'String' );
    % Get axes labels properties
    ax_obj(ax_obj_idx).xlabel   = get( get( axes_handles(ax_obj_idx), 'XLabel' ), 'String' );
    ax_obj(ax_obj_idx).ylabel   = get( get( axes_handles(ax_obj_idx), 'YLabel' ), 'String' );
    ax_obj(ax_obj_idx).zlabel   = get( get( axes_handles(ax_obj_idx), 'ZLabel' ), 'String' );
    % Get xtick positions
    ax_obj(ax_obj_idx).xtick    = get( axes_handles(ax_obj_idx), 'XTick' );
    ax_obj(ax_obj_idx).ytick    = get( axes_handles(ax_obj_idx), 'YTick' );
    ax_obj(ax_obj_idx).ztick    = get( axes_handles(ax_obj_idx), 'ZTick' );
    % Get xticklabels
    ax_obj(ax_obj_idx).xticklabel = get( axes_handles(ax_obj_idx), 'XTickLabel' );
    ax_obj(ax_obj_idx).yticklabel = get( axes_handles(ax_obj_idx), 'YTickLabel' );
    ax_obj(ax_obj_idx).zticklabel = get( axes_handles(ax_obj_idx), 'ZTickLabel' );
    % Get grid on/off properties
    ax_obj(ax_obj_idx).xgrid    = get( axes_handles(ax_obj_idx), 'XGrid' );
    ax_obj(ax_obj_idx).ygrid    = get( axes_handles(ax_obj_idx), 'YGrid' );
    ax_obj(ax_obj_idx).zgrid    = get( axes_handles(ax_obj_idx), 'ZGrid' );
    % Get axis properties
    ax_obj(ax_obj_idx).xrange   = get( axes_handles(ax_obj_idx), 'XLim' );
    ax_obj(ax_obj_idx).yrange   = get( axes_handles(ax_obj_idx), 'YLim' );
    ax_obj(ax_obj_idx).zrange   = get( axes_handles(ax_obj_idx), 'ZLim' );
    % Get the viewing angles for gnuplot (Only used later for surface plots)
    ax_obj(ax_obj_idx).viewing  = ...
      convert_viewing_from_matlab_to_gnuplot( axes_handles(ax_obj_idx) );
    % Check if axes plot has legend entries
    % Returns a cell array of strings and corresponding handles for the
    % different plots/graphs
    ax_obj(ax_obj_idx).legend   = ...
      get_plot_legends( axes_handles(ax_obj_idx), ax_hndl_legends, Env );
    % Get the plot handle(s) and notify the plots about their legends
    % The last is necessary as the legend is set given to the plot/splot
    % command in gnuplot ! ( plot ... title 'legend' ... )
    ax_obj(ax_obj_idx).plots    = ...
      get_plot_objects( axes_handles(ax_obj_idx), ax_obj(ax_obj_idx).legend );
  end
end

% -----------------------------------------------------------------
% Return all legend entries for the given axes
% -----------------------------------------------------------------
function legends = get_plot_legends( axes_handle, ax_hndl_legends, Env )

  legends = [];

  for ax_hndl_legend_idx = 1:length( ax_hndl_legends )
    % If axes handle is equal to user data legend handle
    % then both match and the legend entries corresponds to the axes
    tmp = get( ax_hndl_legends(ax_hndl_legend_idx), 'UserData');
    
    if ~isempty( tmp ) && strcmpi( Env, 'octave' )
      if axes_handle == tmp.handle
        legends.text = get( ax_hndl_legends(ax_hndl_legend_idx), 'String');
        switch get( ax_hndl_legends(ax_hndl_legend_idx), 'Location')
          case 'northeast'
            legends.location = 'right top';
          case 'southeast'
            legends.location = 'right bottom';
          case 'northwest'
            legends.location = 'left top ';
          case 'southwest'
            legends.location = 'left bottom';
          otherwise
            legends.location = 'right top ';
        end
        tmp_delete_fcn = get( ax_hndl_legends(ax_hndl_legend_idx), 'Deletefcn' );
        legends.hndl   = tmp_delete_fcn{end};
      end
    elseif ~isempty( tmp ) && strcmpi( Env, 'matlab' )
      if axes_handle == tmp.PlotHandle
        legends.text = get( ax_hndl_legends(ax_hndl_legend_idx), 'String');
        switch lower( get( ax_hndl_legends(ax_hndl_legend_idx), 'Location') )
          case 'northeast'
            legends.location = 'right top';
          case 'southeast'
            legends.location = 'right bottom';
          case 'northwest'
            legends.location = 'left top ';
          case 'southwest'
            legends.location = 'left bottom';
          otherwise
            legends.location = 'right top ';
        end
        tmp_delete_fcn = get( ax_hndl_legends(ax_hndl_legend_idx), 'UserData' );
        legends.hndl   = tmp_delete_fcn.handles;
      end
    end
  end
end

% -----------------------------------------------------------------
% Return an object with the plot(s) information
% -----------------------------------------------------------------
function pl_obj = get_plot_objects( axes_handle, legend_handle )

  % From figure to axes to plot handle
  plot_handles = get(axes_handle, 'Children');
  
  % Get all necessary information for the Plots
  for pl_obj_idx = 1:length( plot_handles )
    switch get( plot_handles(pl_obj_idx), 'Type' )
      case 'text'
        pl_obj(pl_obj_idx).type     = 'text';
        pl_obj(pl_obj_idx).text     = get( plot_handles(pl_obj_idx), 'String' );
        pl_obj(pl_obj_idx).position = get( plot_handles(pl_obj_idx), 'Position' );
        pl_obj(pl_obj_idx).rotation = get( plot_handles(pl_obj_idx), 'Rotation' );
      case 'line'
        pl_obj(pl_obj_idx).type       = 'line';
        pl_obj(pl_obj_idx).linestyle  = get( plot_handles(pl_obj_idx), 'LineStyle' );
        pl_obj(pl_obj_idx).linewidth  = get( plot_handles(pl_obj_idx), 'LineWidth' );
        pl_obj(pl_obj_idx).linecolor  = get( plot_handles(pl_obj_idx), 'Color' );
        pl_obj(pl_obj_idx).marker     = get( plot_handles(pl_obj_idx), 'Marker' );
        pl_obj(pl_obj_idx).markersize = get( plot_handles(pl_obj_idx), 'MarkerSize' );
        pl_obj(pl_obj_idx).xdata      = get( plot_handles(pl_obj_idx), 'XData' );
        pl_obj(pl_obj_idx).ydata      = get( plot_handles(pl_obj_idx), 'YData' );
        if ~isempty( legend_handle )
          [~, tmp_idx] = ismember( plot_handles(pl_obj_idx), legend_handle.hndl );
          if tmp_idx ~= 0
            pl_obj(pl_obj_idx).legend     = legend_handle.text{tmp_idx};
          else pl_obj(pl_obj_idx).legend  = [];
          end
        else pl_obj(pl_obj_idx).legend = [];
        end
      case 'patch'
        pl_obj(pl_obj_idx).type       = 'patch';
        pl_obj(pl_obj_idx).linestyle  = get( plot_handles(pl_obj_idx), 'LineStyle' );
        pl_obj(pl_obj_idx).linewidth  = get( plot_handles(pl_obj_idx), 'LineWidth' );
        pl_obj(pl_obj_idx).linecolor  = [];
        pl_obj(pl_obj_idx).marker     = get( plot_handles(pl_obj_idx), 'Marker' );
        pl_obj(pl_obj_idx).markersize = get( plot_handles(pl_obj_idx), 'MarkerSize' );
        pl_obj(pl_obj_idx).xdata      = get( plot_handles(pl_obj_idx), 'XData' );
        pl_obj(pl_obj_idx).ydata      = get( plot_handles(pl_obj_idx), 'YData' );
        pl_obj(pl_obj_idx).zdata      = get( plot_handles(pl_obj_idx), 'ZData' );
        if ~isempty( legend_handle )
          [~, tmp_idx] = ismember( plot_handles(pl_obj_idx), legend_handle.hndl );
          if tmp_idx ~= 0
            pl_obj(pl_obj_idx).legend     = legend_handle.text{tmp_idx};
          else pl_obj(pl_obj_idx).legend  = [];
          end
        else pl_obj(pl_obj_idx).legend = [];
        end
      case 'surface'
        pl_obj(pl_obj_idx).type       = 'surface';
        pl_obj(pl_obj_idx).linestyle  = get( plot_handles(pl_obj_idx), 'LineStyle' );
        pl_obj(pl_obj_idx).linewidth  = get( plot_handles(pl_obj_idx), 'LineWidth' );
        pl_obj(pl_obj_idx).linecolor  = [];
        pl_obj(pl_obj_idx).marker     = get( plot_handles(pl_obj_idx), 'Marker' );
        pl_obj(pl_obj_idx).markersize = get( plot_handles(pl_obj_idx), 'MarkerSize' );
        pl_obj(pl_obj_idx).xdata      = get( plot_handles(pl_obj_idx), 'XData' );
        pl_obj(pl_obj_idx).ydata      = get( plot_handles(pl_obj_idx), 'YData' );
        pl_obj(pl_obj_idx).zdata      = get( plot_handles(pl_obj_idx), 'ZData' );
        if ~isempty( legend_handle )
          [~, tmp_idx] = ismember( plot_handles(pl_obj_idx), legend_handle.hndl );
          if tmp_idx ~= 0
            pl_obj(pl_obj_idx).legend     = legend_handle.text{tmp_idx};
          else pl_obj(pl_obj_idx).legend  = [];
          end
        else pl_obj(pl_obj_idx).legend = [];
        end
        % Decide, whether it is a surface or mesh plot
        pl_obj(pl_obj_idx).facecolor  = get( plot_handles(pl_obj_idx), 'FaceColor' );
        pl_obj(pl_obj_idx).edgecolor  = get( plot_handles(pl_obj_idx), 'EdgeColor' );
        if ( strcmpi( pl_obj(pl_obj_idx).edgecolor, 'flat' ) && ...
          isequal( pl_obj(pl_obj_idx).facecolor, [1 1 1] ) ) || ...
          strcmpi( pl_obj(pl_obj_idx).facecolor,'none' )
          pl_obj(pl_obj_idx).splottype = 'mesh';
        else
          pl_obj(pl_obj_idx).splottype = 'surface';
        end
      end
  end
end

% -----------------------------------------------------------------
% Write Header Information
% -----------------------------------------------------------------
function file_write_header( fid )

  % Write Header
  fprintf( fid, '\\begin{gnuplot}[scale=1,terminal=lua,terminaloptions=tikz] \n' );
  fprintf( fid, '\n' );
  
end

% -----------------------------------------------------------------
% Write Outer Information
% -----------------------------------------------------------------
function file_write_outer( fid )

  % Write Outer
  fprintf(fid, '\\end{gnuplot}\n');
  
end

% -----------------------------------------------------------------
% Write Axes Information
% -----------------------------------------------------------------
function file_write_axes( fid, ax_obj, user_defs )

  % Set normalized figure size
  if ~isempty(user_defs) && ~isempty( user_defs.size )
    fprintf( fid, '  set size %f, %f; \n', user_defs.size(1), user_defs.size(2) );
  else
    fprintf( fid, '  set size 1.0, 1.0; \n' );
  end
  fprintf( fid, '  set ticslevel 0.0; \n' );
  
  % Set user specific global options
  if ~isempty(user_defs) && isfield( user_defs, 'user_specific' ) && ...
    ~isempty( user_defs.user_specific )
    file_write_user_specific_defs( fid, user_defs.user_specific )
  end
  
  % If subplots -> multiplot
  if length( ax_obj ) > 1
    fprintf( fid, '  set multiplot; \n' );
  end
  
  for ax_obj_idx = 1:length( ax_obj )
    % Indicate the plot number
    fprintf( fid, '\n# ----- Plot Nr. %d -----\n\n', ax_obj_idx );
    
    % Unset almost everything
    file_write_unset_all( fid );
    
    % Check if grid is on
    if ( strcmp( ax_obj(ax_obj_idx).xgrid, 'on' ) || ...
         strcmp( ax_obj(ax_obj_idx).ygrid, 'on' ) || ...
         strcmp( ax_obj(ax_obj_idx).zgrid, 'on' ) )
      fprintf( fid, '  set grid;\n\n' );
    end

    % Set plot position
    if ~isempty(user_defs) && ~isempty( user_defs.plot(ax_obj_idx).plotorigin )
      fprintf( fid, '  set origin %f, %f;\n', ...
        user_defs.plot(ax_obj_idx).plotorigin(1), user_defs.plot(ax_obj_idx).plotorigin(2) );
    else
      fprintf( fid, '  set origin %2.4f, %2.4f;\n', ...
        ax_obj(ax_obj_idx).position(1), ax_obj(ax_obj_idx).position(2));
    end
    
    % Set plot size
    if ~isempty(user_defs) && isfield( user_defs.plot(ax_obj_idx), 'plotsize' ) ...
      && ~isempty( user_defs.plot(ax_obj_idx).plotsize )
      fprintf( fid, '  set size %f, %f;\n', ...
        user_defs.plot(ax_obj_idx).plotsize(1), user_defs.plot(ax_obj_idx).plotsize(2) );
    else
      fprintf( fid, '  set size %2.4f, %2.4f;\n', ...
        ax_obj(ax_obj_idx).position(3), ax_obj(ax_obj_idx).position(4));
    end
    
    % Set key (legend) position
    if isempty( ax_obj(ax_obj_idx).legend )
      fprintf( fid, '  set key right top;\n');
    else
      fprintf( fid, '  set key %s;\n', ax_obj(ax_obj_idx).legend.location );
    end
    
    % Set margins
    if ~isempty(user_defs) && ~isempty( user_defs.plot(ax_obj_idx) )
      file_write_margin_plot( fid, user_defs.plot(ax_obj_idx) );
    end

    % Write plot title
    if ~isempty( ax_obj(ax_obj_idx).title )
      fprintf( fid, '\n  set title ''%s''\n', ax_obj(ax_obj_idx).title );
    end
    
    % Write out all given (x,y,z)labels
    if ~isempty(user_defs) && ~isempty( user_defs.plot(ax_obj_idx) ) && ...
        isfield( user_defs.plot(ax_obj_idx), 'xlabel' )
      file_write_labels( fid, user_defs.plot(ax_obj_idx) );
    else
      file_write_labels( fid, ax_obj(ax_obj_idx) );
    end
    
    % Write X,Y,Z tick labels
    file_write_ticklabels( fid, ax_obj(ax_obj_idx) );
    
    % Write X,Y,Z ranges
    if ~isempty(user_defs) && ~isempty( user_defs.plot(ax_obj_idx) ) && ...
        isfield( user_defs.plot(ax_obj_idx), 'xrange' )
      file_write_ranges( fid, user_defs.plot(ax_obj_idx) );
    else
      file_write_ranges( fid, ax_obj(ax_obj_idx) );
    end
    
    % Set user specific local options
    if ~isempty(user_defs) && ~isempty( user_defs.plot(ax_obj_idx) ) && ...
      isfield( user_defs.plot(ax_obj_idx), 'user_specific' ) && ~isempty( user_defs.plot(ax_obj_idx).user_specific )
      user_defs_string = user_defs.plot(ax_obj_idx).user_specific;
    else
      user_defs_string = [];
    end
    
    fprintf( fid,'\n\n' );
    
    % Write (sub)plots to file
    file_write_plot( fid, ax_obj(ax_obj_idx), user_defs_string );
  end
  
end

% -----------------------------------------------------------------
% Write Plot (x,y,z)labels
% -----------------------------------------------------------------
function file_write_labels( fid, xyzlabel )

  fprintf( fid, '\n#--- Set XYZ-Labels ---\n' );
  
  % Write plot xlabel
  if isfield( xyzlabel, 'xlabel' ) && ~isempty( xyzlabel.xlabel )
      fprintf( fid, '  set xlabel ''%s'';\n', xyzlabel.xlabel );
  end
  % Look for x2label
  if isfield( xyzlabel, 'x2label' ) && ~isempty( xyzlabel.x2label )
    fprintf( fid, '  set x2tics; set x2label ''%s'';\n', xyzlabel.x2label );
  end
  % Write plot ylabel
  if isfield( xyzlabel, 'ylabel' ) && ~isempty( xyzlabel.ylabel )
      fprintf( fid, '  set ylabel ''%s'';\n', xyzlabel.ylabel );
  end
  % Look for y2label
  if isfield( xyzlabel, 'y2label' ) && ~isempty( xyzlabel.y2label )
    fprintf( fid, '  set y2tics; set y2label ''%s'';\n', xyzlabel.y2label );
  end
  % Write plot zlabel
  if isfield( xyzlabel, 'zlabel' ) && ~isempty( xyzlabel.zlabel )
    fprintf( fid, '  set zlabel ''%s'';\n', xyzlabel.zlabel );
  end
  
end

% -----------------------------------------------------------------
% Write Plot (x,y,z)range
% -----------------------------------------------------------------
function file_write_ranges( fid, xyzranges )
  % Write the axes view layout
  % ( matlab: axis([xrange(1) xrange(2) yrange(1) yrange(2) zrange(1) zrange(2)]); )
  
  fprintf( fid, '\n#--- Set XYZ-Ranges ---\n' );

  % Write plot xrange
  if isfield( xyzranges, 'xrange' ) && ~isempty( xyzranges.xrange )
    fprintf( fid, '  set xrange [%f:%f];\n', xyzranges.xrange(1), xyzranges.xrange(2) );
  end
  % Look for x2range
  if isfield( xyzranges, 'x2range' ) && ~isempty( xyzranges.x2range )
    fprintf( fid, '  set x2range [%f:%f];\n', udplot.x2range(1), udplot.x2range(2) );
  end
  
  % Write plot yrange
  if isfield( xyzranges, 'yrange' ) && ~isempty( xyzranges.yrange )
    fprintf( fid, '  set yrange [%f:%f];\n', xyzranges.yrange(1), xyzranges.yrange(2) );
  end
  % Look for y2range
  if isfield( xyzranges, 'y2range' ) && ~isempty( xyzranges.y2range )
    fprintf( fid, '  set y2range [%f:%f];\n', xyzranges.y2range(1), xyzranges.y2range(2) );
  end
  
  % Write plot zrange
  if isfield( xyzranges, 'zrange' ) && ~isempty( xyzranges.zrange )
    fprintf( fid, '  set zrange [%f:%f];\n', xyzranges.zrange(1), xyzranges.zrange(2) );
  end
end

% -----------------------------------------------------------------
% Write Plot Margins
% -----------------------------------------------------------------
function file_write_margin_plot( fid, udplot )

  fprintf( fid, '\n#--- Set LRTB-Margins ---\n' );

    if isfield( udplot, 'lmargin' ) && ~isempty( udplot.lmargin )
      fprintf( fid, '  set lmargin %f\n', udplot.lmargin );
    end

    if isfield( udplot, 'rmargin' ) && ~isempty( udplot.rmargin )
      fprintf( fid, '  set rmargin %f\n', udplot.rmargin );
    end

    if isfield( udplot, 'tmargin' ) && ~isempty( udplot.tmargin )
      fprintf( fid, '  set tmargin %f\n', udplot.tmargin );
    end

    if isfield( udplot, 'bmargin' ) && ~isempty( udplot.bmargin )
      fprintf( fid, '  set bmargin %f\n', udplot.bmargin );
    end
  
end

% -----------------------------------------------------------------
% Write User specific information
% -----------------------------------------------------------------
function file_write_user_specific_defs( fid, user_defs_string )
  % Set user specific local options
  if ~isempty(user_defs_string)
    fprintf( fid,'# --- User specific options begin---\n' );
    fprintf( fid, user_defs_string );
    fprintf( fid,'\n# --- User specific options end ---\n\n' );
  end
end

% -----------------------------------------------------------------
% Write Plot Information
% -----------------------------------------------------------------
function file_write_plot( fid, ax_obj, user_defs_string )

  pl_obj = ax_obj.plots;
  
  [nlineplots, nsurfaceplots, ntextplots, npatchplots ] = ...
    get_number_of_different_plots( pl_obj );

  line_plot_counter = 1;
  surface_plot_counter = 1;
  
  % Write text information
  if ntextplots > 0
    % Set user specific local options
    file_write_user_specific_defs( fid, user_defs_string );
    % Write text plot
    file_write_text_plot( fid, pl_obj );
  end
  
  % Write the 2d plots information
  if nlineplots > 0
    % Set user specific local options
    file_write_user_specific_defs( fid, user_defs_string );
    % Write 2d plot
    file_write_line_plot( fid, pl_obj, nlineplots );
  end
  
  % Write the path-plots information
  if npatchplots > 0
    % Write Patch Plot specific commands
    
    fprintf( fid, '  set boxwidth 1 relative;\n' );
    fprintf( fid, '  set style data histograms;\n' );
    fprintf( fid, '  set style fill solid 1.0 border -1;\n\n' );
    
    % Set user specific local options
    file_write_user_specific_defs( fid, user_defs_string );
    % Write 2d plot
    file_write_patch_plot( fid, pl_obj, npatchplots );
  end
  
  % Write the 3d plots (surface, mesh etc.) information
  if nsurfaceplots > 0
    % Write 3D plots specific commands
    fprintf( fid, '  set view %f,%f;\n', ax_obj.viewing.xangle, ax_obj.viewing.zangle );
    
    [ row_size, col_size ] = get_largest_row_and_col_size_for_dgrid3d( pl_obj );
    fprintf( fid, '  set dgrid3d %d, %d, 16;\n', row_size, col_size );
    fprintf( fid, '  set hidden3d; \n' );
    fprintf( fid, '  set pm3d depthorder hidden3d; \n\n' );
    
    % Set user specific local options
    file_write_user_specific_defs( fid, user_defs_string );
    % Write 3d plot
    file_write_surface_plot( fid, pl_obj, nsurfaceplots );
  end
end

% -----------------------------------------------------------------
% Get the information about the number of different plots,
% i.e. how many surface, line and text plots are present?
% -----------------------------------------------------------------
function [nlineplots, nsurfaceplots, ntextplots, npatchplots ] = get_number_of_different_plots( pl_obj )
  nlineplots    = 0;
  nsurfaceplots = 0;
  ntextplots    = 0;
  npatchplots   = 0;
  
  for pl_obj_idx = 1:length( pl_obj )
    switch pl_obj( pl_obj_idx ).type
      case 'line'
        nlineplots    = nlineplots    + 1;
      case 'text'
        ntextplots    = ntextplots    + 1;
      case 'patch'
        npatchplots   = npatchplots    + 1;
      case 'surface'
        nsurfaceplots = nsurfaceplots + 1;
    end
  end
  
  % check if 2d and 3d plots are mixed
  if nlineplots > 0 && nsurfaceplots > 0
    error( '2D and 3D plots cannot be mixed! Implementation is waiting!' );
  end
end

% -----------------------------------------------------------------
% Search the largest lengths for x and y in a surf/mesh plot
% This is needed for gp: set dgrid3d row_size,col_size,norm
% -----------------------------------------------------------------
function [ row_size, col_size ] = get_largest_row_and_col_size_for_dgrid3d( pl_obj )
  row_size = 10;
  col_size = 10;
  
  for pl_obj_idx = 1:length( pl_obj )
    if strcmpi( pl_obj( pl_obj_idx ).type, 'surface' )
      row_size = max( row_size, length( pl_obj( pl_obj_idx ).xdata ) );
      col_size = max( col_size, length( pl_obj( pl_obj_idx ).ydata ) );
    end
  end

  % Define a maximum row_size and col_size !
  % Reduce computational complexity for gnuplot
  row_size = min( row_size, 100 );
  col_size = min( col_size, 100 );
end

% -----------------------------------------------------------------
% Unset almost everything in the current plot
% -----------------------------------------------------------------
function file_write_unset_all( fid )
  % Unset grid
  fprintf( fid, '  unset grid;\n' );
  % Unset margins
  fprintf( fid, '  unset lmargin; unset rmargin; unset tmargin; unset bmargin;\n');
  % Unset xlabels and xtics and xranges
  fprintf( fid, '  unset xlabel; unset x2label; unset xtics; unset x2tics; unset xrange; unset x2range;\n' );
  % Unset ylabels and ytics and yranges
  fprintf( fid, '  unset ylabel; unset y2label; unset ytics; unset y2tics; unset yrange; unset y2range;\n' );
  % Unset zlabels and ztics and zranges
  fprintf( fid, '  unset zlabel; unset ztics; unset zrange;\n' );
  % Unset labels
  fprintf( fid, '  unset label;\n' );
end

% -----------------------------------------------------------------
% Write out the text information
% -----------------------------------------------------------------
function file_write_text_plot( fid, pl_obj )
  
  for pl_obj_idx = 1:length( pl_obj )
    if strcmp( pl_obj( pl_obj_idx ).type, 'text' )
      fprintf( fid, '  set label ''%s'' at %f,%f,%f rotate by %f;\n', ...
        pl_obj( pl_obj_idx ).text, pl_obj( pl_obj_idx ).position(1), ...
        pl_obj( pl_obj_idx ).position(2), pl_obj( pl_obj_idx ).position(3), ...
        pl_obj( pl_obj_idx ).rotation(1) );
    end
  end
  
  fprintf( fid, '\n' );
end

% -----------------------------------------------------------------
% Write line plot information (i.e. 2d Plots)
% -----------------------------------------------------------------
function file_write_line_plot( fid, pl_obj, nlineplots )
  
  line_plot_counter = 1;
  
  % Write the gnuplot commands for 2d plots
  for pl_obj_idx = 1:length( pl_obj )
    % Check for 2D plots
    if strcmp( pl_obj( pl_obj_idx ).type, 'line' )
      styles = convert_styles_from_matlab_to_gnuplot( pl_obj( pl_obj_idx ) );
      % Plot in 2D
      if line_plot_counter == 1
        fprintf(fid, '  plot ''-'' using 1:2 title ''%s'' %s', ...
          pl_obj( pl_obj_idx ).legend, styles.complete );
      else
        fprintf(fid, '       ''-'' using 1:2 title ''%s'' %s', ...
          pl_obj( pl_obj_idx ).legend, styles.complete );
      end
      
      % Check for last plot
      if line_plot_counter == nlineplots
        fprintf(fid, ';\n' );
      else
        fprintf(fid, ', \\ \n' );
      end
      line_plot_counter = line_plot_counter + 1;
    end
  end

  % After the gnuplot commands have been written,
  % the corresponding data can be written
  for pl_obj_idx = 1:length( pl_obj )
    % Check, whether the figure is 2D
    if strcmp( pl_obj( pl_obj_idx ).type, 'line' )
      % Write the data
      for x = 1:length( pl_obj(pl_obj_idx).xdata )
        fprintf(fid, '    %f %f\n', pl_obj(pl_obj_idx).xdata(x), ...
          pl_obj(pl_obj_idx).ydata(x) );
      end
      % Put an end of file at the end of the data (needed for inline data)
      fprintf(fid, '  EOF \n');
    end
  end
  
  fprintf( fid, '\n' );
end

% -----------------------------------------------------------------
% Write patch plot information (i.e. Histogram Plots)
% -----------------------------------------------------------------
function file_write_patch_plot( fid, pl_obj, npatchplots )

  patch_plot_counter = 1;
  
  % Write the gnuplot commands for 2d plots
  for pl_obj_idx = 1:length( pl_obj )
    % Check for Patch plots
    if strcmp( pl_obj( pl_obj_idx ).type, 'patch' )
      styles = convert_styles_from_matlab_to_gnuplot( pl_obj( pl_obj_idx ) );
      % Plot Patch
      if patch_plot_counter == 1
        fprintf(fid, '  plot ''-'' using 1:2 title ''%s'' %s', ...
          pl_obj( pl_obj_idx ).legend, styles.complete );
      else
        fprintf(fid, '       ''-'' using 1:2 title ''%s'' %s', ...
          pl_obj( pl_obj_idx ).legend, styles.complete );
      end
      
      % Check for last plot
      if patch_plot_counter == npatchplots
        fprintf(fid, ';\n' );
      else
        fprintf(fid, ', \\ \n' );
      end
      patch_plot_counter = patch_plot_counter + 1;
    end
  end

  % After the gnuplot commands have been written,
  % the corresponding data can be written
  for pl_obj_idx = 1:length( pl_obj )
    % Check, whether the figure is 2D
    if strcmp( pl_obj( pl_obj_idx ).type, 'patch' )
      % Write the data
      for x = 1:length( pl_obj(pl_obj_idx).xdata(1,:) )
        fprintf(fid, '    %f %f\n', ...
          mean( pl_obj(pl_obj_idx).xdata(2:3,x) ), ...
          mean( pl_obj(pl_obj_idx).ydata(2:3,x) ) );
      end
      % Put an end of file at the end of the data (needed for inline data)
      fprintf(fid, '  EOF \n');
    end
  end
  
  fprintf( fid, '\n' );

end

% -----------------------------------------------------------------
% Write surface plot information (i.e. 3d Plots)
% -----------------------------------------------------------------
function file_write_surface_plot( fid, pl_obj, nsurfaceplots )
  
  surface_plot_counter = 1;
  
  % Write the gnuplot commands for 3d plots
  for pl_obj_idx = 1:length( pl_obj )
    % Check for 3D plots
    if strcmp( pl_obj( pl_obj_idx ).type, 'surface' )
      styles = convert_styles_from_matlab_to_gnuplot( pl_obj( pl_obj_idx ) );
      % Plot in 3D
      if surface_plot_counter == 1
        fprintf(fid, '  splot ''-'' using 1:2:3 title ''%s'' %s', ...
          pl_obj( pl_obj_idx ).legend, styles.complete );
      else
        fprintf(fid, '       ''-'' using 1:2:3 title ''%s'' %s', ...
          pl_obj( pl_obj_idx ).legend, styles.complete );
      end
      
      % Check for last plot
      if surface_plot_counter == nsurfaceplots
        fprintf(fid, ';\n' );
      else
        fprintf(fid, ', \\ \n' );
      end
      surface_plot_counter = surface_plot_counter + 1;
    end
  end

  % After the gnuplot commands have been written,
  % the corresponding data can be written
  for pl_obj_idx = 1:length( pl_obj )
    % Check, whether the figure is 3D
    if strcmp( pl_obj( pl_obj_idx ).type, 'surface' )
      % Write the data
      for x = 1:length( pl_obj(pl_obj_idx).xdata )
        for y = 1:length( pl_obj(pl_obj_idx).ydata )
          fprintf(fid, '    %f %f %f\n', pl_obj(pl_obj_idx).xdata(x), ...
            pl_obj(pl_obj_idx).ydata(y), pl_obj(pl_obj_idx).zdata(x,y) );
        end
      end
      % Put an end of file at the end of the data (needed for inline data)
      fprintf(fid, '  EOF \n');
    end
  end
  
  fprintf( fid, '\n' );
end

% -----------------------------------------------------------------
% Write X,Y,Z tick labels
% -----------------------------------------------------------------
function file_write_ticklabels( fid, ax_obj )

  % Write XTickLabel
  if ~isempty( ax_obj.xticklabel ) && ~iscell( ax_obj.xticklabel )
    fprintf( fid, '  set xtics (');
    xlog10 = floor( log10( ax_obj.xrange(2)-ax_obj.xrange(1) ) );
    if xlog10 ~= 0 xticklabelext = ['e', num2str(xlog10)]; else xticklabelext=''; end;
    for i = 1:length( ax_obj.xtick )
      xticklabel = [strtrim(ax_obj.xticklabel(i,:)), xticklabelext ];
      % If ax_obj.xtick is already string, num2str should return a string!
      fprintf( fid, '"%s" %f', xticklabel, ax_obj.xtick(i) );
      if i < length( ax_obj.xtick ) fprintf( fid, ', ' ); end
    end
    fprintf( fid, ');\n');
  else
    fprintf( fid, '  set xtics;\n');
  end
  
  % Write YTickLabel
  if ~isempty( ax_obj.yticklabel ) && ~iscell( ax_obj.yticklabel )
    fprintf( fid, '  set ytics (');
    ylog10 = floor( log10( ax_obj.yrange(2)-ax_obj.yrange(1) ) );
    if ylog10 ~= 0 yticklabelext = ['e', num2str(ylog10)]; else yticklabelext=''; end;
    for i = 1:length( ax_obj.ytick )
      yticklabel = [strtrim(ax_obj.yticklabel(i,:)), yticklabelext ];
      % If ax_obj.ytick is already string, num2str should return a string!
      fprintf( fid, '"%s" %f', yticklabel, ax_obj.ytick(i) );
      if i < length( ax_obj.ytick ) fprintf( fid, ', ' ); end
    end
    fprintf( fid, ');\n');
  else
    fprintf( fid, '  set ytics;\n');
  end

  % Write ZTickLabel
  if ~isempty( ax_obj.zticklabel ) && ~iscell( ax_obj.zticklabel )
    fprintf( fid, '  set ztics (');
    zlog10 = floor( log10( ax_obj.zrange(2)-ax_obj.zrange(1) ) );
    if zlog10 ~= 0 zticklabelext = ['e', num2str(zlog10)]; else zticklabelext=''; end;
    for i = 1:length( ax_obj.ztick )
      zticklabel = [strtrim(ax_obj.zticklabel(i,:)), 'e', num2str(zlog10) ];
      % If ax_obj.ztick is already string, num2str should return a string!
      fprintf( fid, '"%s" %f', zticklabel, ax_obj.ztick(i) );
      if i < length( ax_obj.ztick ) fprintf( fid, ', ' ); end
    end
    fprintf( fid, ');\n');
  else
    fprintf( fid, '  set ztics;\n');
  end
end

% -----------------------------------------------------------------
% Function, which returns marker type, linecolor etc. in a gnuplot 
% readable fashion
%
% Please note: These line types and marker types depend on the
% gnuplot terminal! (in our case lua)
% There might exists a different relation?
% -----------------------------------------------------------------
function styles = convert_styles_from_matlab_to_gnuplot( pl_obj )
  % Get the line width
  styles.linewidth = pl_obj.linewidth;
  % Convert the line style 
  switch pl_obj.linestyle
    case '-' % solid line
      styles.linestyle = 1;
    case '--' % dashed line
      styles.linestyle = 2;
    case ':' % dotted line
      styles.linestyle = 3;
    case '-.' % dash-dot line
      styles.linestyle = 5;
    case 'none' % no line
      styles.linestyle = 0;
    otherwise
      styles.linestyle = 1;
  end
  % Convert matlab to gnuplot color
  % Matlab/Octave color is normalized, thus convert to hex
  if ~isempty( pl_obj.linecolor )
    R = dec2hex(round(pl_obj.linecolor(1)*255), 2);
    G = dec2hex(round(pl_obj.linecolor(2)*255), 2);
    B = dec2hex(round(pl_obj.linecolor(3)*255), 2);
    styles.colorrgb = [R, G, B];
  else styles.colorrgb = [];
  end
  % Convert the marker style 
  switch pl_obj.marker
    case '+' % plus sign
      styles.marker = 1;
    case 'x' % cross
      styles.marker = 2;
    case '*' % asterisk
      styles.marker = 3;
    case 's' % square
      styles.marker = 4;
    case 'o' % circle
      styles.marker = 6;
    case '^' % upward pointing triangle
      styles.marker = 8;
    case 'v' % downward pointing triangle
      styles.marker = 9;
    otherwise
      styles.marker = 0;
  end
  % Get the marker size
  % Matlab marker is 6 times greater then gnuplot marker -> Divide by 6
  % This assumption is taken from intuition !?
  styles.markersize = pl_obj.markersize / 6;
  % Decide, whether it is a line or a linespoints style
  if strcmpi( pl_obj.type, 'surface' ) && strcmpi( pl_obj.splottype, 'surface' )
    style = 'pm3d';
  elseif styles.marker == 0 && strcmpi( pl_obj.type, 'patch' )
    style = 'boxes';
  elseif styles.marker == 0
    style = 'lines';
  else
    style = 'linespoints';
  end
  
  if ~strcmpi( style, 'linespoints' )
    if ~isempty( styles.colorrgb )
      styles.complete = ['with ',style,' lc rgb "#',styles.colorrgb, ...
        '" lt ',num2str(styles.linestyle),' lw ',num2str(styles.linewidth)];
    else
      styles.complete = ['with ',style, ...
        ' lt ',num2str(styles.linestyle),' lw ', num2str(styles.linewidth)];
    end
  else
    if ~isempty( styles.colorrgb )
      styles.complete = ['with ',style,' lc rgb "#',styles.colorrgb, ...
        '" lt ',num2str(styles.linestyle),' lw ', num2str(styles.linewidth), ...
        ' pt ', num2str(styles.marker), ' ps ', num2str(styles.markersize)];
    else
      styles.complete = ['with ',style, ...
        ' lt ',num2str(styles.linestyle),' lw ', num2str(styles.linewidth), ...
        ' pt ', num2str(styles.marker), ' ps ', num2str(styles.markersize)];
    end
  end

end

% -----------------------------------------------------------------
% Function, which returns the gnuplot view for the current axes
%
% Please note: There are more possibilities inside matlab
% (CameraPosition, CameraTarget, CameraUpVector and CameraViewAngle)
% as it is used here. We only want the angles for the gnuplot view
% command !
% -----------------------------------------------------------------
function viewing = convert_viewing_from_matlab_to_gnuplot( axes_handle )
  %tmp_CamPosition = get( axes_handle, 'CameraPosition' );
  %tmp_CamTarget   = get( axes_handle, 'CameraTarget' );
  
  %% Move CameraTarget to origin and thus move CameraPosition
  %tmp_NewCamPosition = tmp_CamPosition - tmp_CamTarget;
  
  %% Make things more readable
  %xcp = tmp_NewCamPosition(1);
  %ycp = tmp_NewCamPosition(2);
  %zcp = tmp_NewCamPosition(3);
  
  %% I hope, i am doing it right here?
  %% Needs testing and/or more mathematical backgroung on that topic
  %[viewing.zangle, viewing.xangle, tmp] = ...
    %cart2sph2d(xcp, ycp, zcp);
  
  %viewing.xangle = viewing.xangle + 180;
  %viewing.xangle(viewing.xangle < 0) = viewing.xangle + 360;
  %viewing.xangle(viewing.xangle > 360) = viewing.xangle - 360;
  
  %viewing.zangle = viewing.zangle + 180;
  %viewing.zangle(viewing.zangle < 0) = viewing.zangle + 360;
  %viewing.zangle(viewing.zangle > 360) = viewing.zangle - 360;
  
  % For now, return standard values !!!
  viewing.xangle = 60;
  viewing.zangle = 30;
end
