## Copyright (C) 2009 Spofahl
## 
## 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 Street, Fifth Floor, Boston, MA 02110-1301 USA

## -*- texinfo -*-
## @deftypefn  {Function File} {[disturbed_grid, bad_grid, error] =} mylib_gnuplot_grid_data ()
## @deftypefnx {Function File} {[disturbed_grid, bad_grid, error] =} mylib_gnuplot_grid_data
##             (@var{file_name_string}, @var{Data Array}, @var{Index of x,y,z-column},
##              @var{norm z}, @var{Column Header}, @var{sortorder})
##
## @example
## @group
## Generate GnuPlot Grid Data for 3D-Plots. It needs at least two inputs.
## 1.) File Name (it may include the complete path)
## 2.) Data Array (it needs to be at least 3rows x 3columns large)
##     The data is expected to be organized in Columns.
##     The Array may include double x-y-pairs.
## If no x, y, z-columns are specified, we assume x to be in column one,
## y in 2, z in 3.
## The 4th if true, z_max will be normed to 1. ==> z in [0 .. 1]
## The 5th argument are the Column Headers as a String Vector.
## The 6th argument tells the sort order of z, it is a logical variable,
## default is true and means ascending sort order, that means:
## smallest z is preferred, if we have equal x-y-pairs, only the best
## (smallest) is saved to the Output file.
## Outputs:
## @code{bad_grid}:
## If Output bad_grid is false, than the Grid-Data-Output file is
## nicely structured - each X-isoline has the same length.
## @code{disturbed_grid}:
## If true at leasst one X-isoline has a different set of Y-Values.
##
## Requirement:
## You need "dlmwrite" from the @code{Octave-Forge} paket.
##
## Requirement for Gnuplot  -  Grid Data Format:
## The Input Grid Data needs for each X value the same number
## of Y values. In Gnuplot language:
## Each X-isoline needs to have the same length, otherwise the result of
## @code{> splot "file" with lines}
## is a series of X-Isolines (lines parallel to the Y-axis), -
## Y-isolines are omitted.
## Example (two X-isolines, each with tree points, devided by a blanc line):
##  # x ,  y ,  z
##    1 ,  1 ,  1
##    1 ,  2 ,  3
##    1 ,  3 ,  2
##
##    2 ,  1 ,  4
##    2 ,  2 ,  7
##    2 ,  3 ,  6
##
##
## @end group
## @end example
##
## This version is a Gamma version. Tested only on @code{MS-WIN}.
## @end deftypefn

## mylib_gnuplot_grid_data
## Octave 3.0 version, tested under MS-Windows

## Author: Spofahl <Stefan@pofahl.de>
## 
## 2009-12-05 Spofahl <Stefan@pofahl.de>
## * Initial revision
## 2009-12-06 Spofahl <Stefan@pofahl.de>
## * 0.2 Input norm_z and Output of disturbed_grid & bad_grid added.

## To do: 
## Improve it ;-)
## Maybe give info-output which X-Isoline is disturbed or bad structured

function [ disturbed_grid, bad_grid, errorcode ] = mylib_gnuplot_grid_data (file_name, data, xyz_indices, norm_z, columnheader, sortorder)
  errorcode = 0;
  fflush(stdout);
  if (nargin < 2)
    printf("\n*************************************************************************\n");
    disp("Usage: You need at least two arguments: An Output File Name and a Data Array");
    printf("\n*************************************************************************\n");
    fflush(stdout);
    errorcode = 1;
    error("we need at least a output file name and a data array!");
  endif
  if (!(ischar(file_name) && isvector(file_name)))
    printf("\n*************************************************************************\n");
    errorcode = errorcode + 2;
    error("We need a character vector as first argument!");
  endif
  if (min(size(data)) < 3)
    printf("\n*************************************************************************\n");
    errorcode = errorcode + 4;
    error("Data array to small, it needs to be at least 3x3!");
  endif
  if((nargin > 2) && isnumeric(xyz_indices) )
    ## if the third argument is a numeric variable we still have to check something
    ## e.g. we do not want to have double entries
    [v1, indx] = sort(round(xyz_indices));
    v2 = shift(v1,1);
    if( isvector(xyz_indices) && (min(min(xyz_indices)) > 0.5) ...
      && (round(max(max(xyz_indices))) <= size(data, 2) ) && (all(v1-v2)) )
      xyz_indices = round(xyz_indices);
    else
    printf("\n*************************************************************************\n");
      disp("We take the default index numbers: 1, 2, 3");
    printf("*************************************************************************\n\n");
      xyz_indices = [1,2,3];
    endif
  elseif(! isnumeric(xyz_indices))
    printf("\n*************************************************************************\n");
    errorcode = errorcode + 8;
    error("Index argument needs to be a number!");
  else
    printf("\n*************************************************************************\n");
    disp(">>>  We take the default index numbers: 1, 2, 3");
    printf("*************************************************************************\n\n");
    xyz_indices = [1,2,3];
  endif
  ## Check value of 4th argument
  if(nargin < 4)
    norm_z = false;
  endif
  if (nargin > 3)
    if( !(isscalar(norm_z) && (islogical(norm_z))) )
      errorcode = errorcode + 16;
      error("4th argument needs to be a boolean scalar!");
    endif
  endif
  ## Check for 5th argument, last char needs to be a end of line char
  if( ( (nargin > 4) && !(ischar(file_name) && isvector(file_name)) ) ...
      || (nargin <= 3)   )
    columnheader = ["# X              , Y               , Z                     \n"];
  else
    ## If last char is no "new line chracter" add one
    if(! strcmp("\n", columnheader(length(columnheader))))
      columnheader = [columnheader, "\n"];
    endif
  endif
  ## Check if 6th argument is logical scalar argument
  if(nargin < 6)
    sortorder = true;
  elseif(!(isscalar(sortorder) && (islogical(sortorder))))
    errorcode = errorcode + 32;
    error("6th argument needs to be a boolean scalar!");
  endif
  #### Constants:           ----------------------------------------------------------------
  column_formatstring = "%20.12G";
  cl_no = 3;      # Column Numbers
  #### --- Now we can start ----------------------------------------------------------------
  ## We reduce column of input array to 3 columns
  data = data(:, xyz_indices);
  ## DBG:
  # printf("xyz_indices: %g, %g, %g\n", xyz_indices);
  ## Open File an write first line
  fid = fopen(file_name, "w");
  if(fid != -1)
    fprintf(fid, columnheader);
    fflush(fid);
    fclose(fid);
  else
    disp("\nError message:");
    error(["\nFile ", file_name, " is blocked\n"]);
  endif
  ## --
  sort_cn = 2;
  sort_cn2nd = 1;
  ## First we sort the Y-Column
  [s,indx] = sort(data (:, sort_cn));
  sorted = data(indx, :);
  
  ## Now we sort the X-column
  [s, indx] = sort(sorted (:, sort_cn2nd));
  sorted = sorted(indx, :);
  ## we want zeros in the first raw
  dummy = shift(sorted, 1);
  dummy(1, :) = zeros(1, cl_no);
  ## know we find the index numbers, where Data changes to the next value, we add the number of (rows+1)
  change_index = [find(sorted(:,sort_cn) - dummy(:,sort_cn));size(sorted,1)+1];
  for ii = 2:size(change_index, 1)
    ## in the sub-arrays we find the raw with the best quality
    sort_dummy = sorted(change_index(ii-1):change_index(ii)-1, :);
    [s,indx] = sort(sort_dummy (:,1));
    if(sortorder)
      sorted(ii-1,:) = sort_dummy(indx(1),:);
    else
      sorted(ii-1,:) = sort_dummy(indx(length(indx)),:);
    endif
  endfor
  sorted_best = sorted(1:ii-1, :);
  if(norm_z)
    sorted_best(:, 3) = sorted_best(:, 3) ./ max(sorted_best(:, 3));
  endif

  ## To add empty raws we find the section boarders
  dummy = shift(sorted_best, 1);
  dummy(1,:) = zeros(1, cl_no);
  
  ## know we find the index numbers, where X changes to the next value, we add the number of (rows+1)
  change_index = [find(sorted_best(:,sort_cn2nd) - dummy(:,sort_cn2nd)); size(sorted_best,1)+1];
  next_first_raw = 1;
  build_dummy = zeros(1, cl_no);
  ## change_index(2) is the begin of next value in column "sort_cn2nd", so we have to substrate the value of 1
  y_set = sorted_best(1:change_index(2) - 1, 2);
  disturbed_grid = false;
  bad_grid = false;
  for zero_line = change_index(2:size(change_index,1))'
    dlmwrite(file_name, sorted_best(next_first_raw:zero_line-1, :), "-append", "precision", column_formatstring);
    fid = fopen(file_name, "a");
    fprintf(fid, "\n");
    fflush(fid);
    fclose(fid);
    if( length(y_set) == length(sorted_best(next_first_raw:zero_line-1, 2)) )
      bad_grid = (false || bad_grid);
      disturbed_grid = ((y_set != sorted_best(next_first_raw:zero_line-1, 2)) || disturbed_grid);
    else
      bad_grid = true;
      disturbed_grid = true;
    endif
    next_first_raw = zero_line;
  endfor
  # if(disturbed_grid)
  #   printf("\n*************************************************************************\n");
  #   disp("Grid Data is disturbed!")
  #   printf("*************************************************************************\n\n");
  # endif
  if(bad_grid)
    printf("\n*************************************************************************\n");
    disp("Grid Data is bad structured!")
    printf("*************************************************************************\n\n");
  endif
  ## --- End ---
  fflush(stdout);
  fflush(stderr);

endfunction
