classdef Eat < handle
%
% PURPOSE:   Track daily calories and Weight Watcher (old system) points.
%
% INPUTS:    
%
% CHANGELOG: 1) 25 Dec 2015 rsm, initial.
%
% ToDo: 1) Put cals and accum cals columns next to each other.
%       2) Add a fn to write to eat.weight(). Have a varargin that is a
%          datenum for previous days.
%       3) Add weekend bars.  Use datetime?
%       4) Add icon plot of workouts.  Runs and rides.
%       5) Add lower bound to plot, so it defaults to plotting the last
%          year.
%       6) Try NaNs for no data.  It plots better.
%       7) Average and 1st order fit for selectable interval.
%       8) Generalize "cereal" so it can insert data.
%       9) Add day, e.g. "Tues", to header print on screen.
%      10) Move all the .mat data files to a subdirectory.
%      11) Auto-delete the older ones?
%      12) Have addNew insert its data in a line in the spreadsheet so it
%          persists.
%      13) Add "Smart Points" (SP) as a new column.
%
%          SP = .0305*Cal + .275SatFat + .12Sugar - .098Protein
%          
%          or  ( from https://wonkypie.com/ww-points-calculators)
%          SP = ( Cal + 4Sugar + 9SatFat - 3.2Protein)/33 (check this)
%          PP = ( 3.2Protein + 3.8Carbs + 9Fat - 2.8Fiber )/35  (Points Plus)
%      
%
  properties (SetAccess = private)
    startDateNum;
    len;              %Length of database array from Excel.
    nCols;            %Number of columns in the spreadsheet.
    ptr;              %Names of columns used here.
  end
  properties
    found;
    nfound;
    data;
    nweeks = 4;
    log;
    weightLog;
    todaysDate;
    indx;             %Index into today's items.
    indxDay;          %Index into the total for each day.
    indxW;            %Index into weekends.
    position;
    healthDataFig;
    thandle;
    ahandle;
    fileName;
    la;
    ra;
    lsqc;
    lsqw;
    textStrCal;
    textStrWt;
  end 
  methods
    function eat = Eat()
      eat.readData([]);
      eat.log = {'Food','Matlab Time','Grams','Calories','Points', ...
		       'Cum Cal', 'Cum Points'};
      eat.weightLog = { [], [] };
      eat.todaysDate = now;
      todaysDate = floor(now);
      eat.indx = find( todaysDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
      eat.fileName = [datestr(now,'yyyymmdd') '.mat'];
      eat.load;
      eat.sofar;
        
      co = [0 0 1;
	    0 0.5 0;
	    1 0 0;
	    0 0.75 0.75;
	    0.75 0 0.75;
	    0.75 0.75 0;
	    0.25 0.25 0.25];
      set(groot,'defaultAxesColorOrder',co)

      MonitorPositions = get(0,'MonitorPositions');
      nMon = size(MonitorPositions);
      nMon = nMon(1);
      %Select widest monitor
      position = MonitorPositions(1,:);
      if( nMon > 1 && MonitorPositions(2,3) > MonitorPositions(1,3) )  
	position =  MonitorPositions(2,:); 
      end
      eat.position(1:2) = position(1:2) + round([position(3)*.02 position(4)*.04]);
      eat.position(3:4) = round([position(3)*.95 position(4)*.90]);
    end
    %
    %
    function readData(eat, name)
      if( isempty(name) ) name = '~/Downloads/PointsChart.xlsx'; end
      if( exist(name,'file') )
	try
	  fprintf('Loading %s.\n', name);
	  %load(name);
	  [num txt eat.data]=xlsread(name);
	catch ME
%	  fprintf('Error opening or reading NtrlData: %s\n',...
%		  ME.ERRMSG);
	  return
	end
	sizeOfData = size(eat.data);
	eat.len   = sizeOfData(1);
	eat.nCols = sizeOfData(2);
      else
	fprintf('Couldn''nt find the data.\n');
	return
      end
      fprintf('Read %d records from %s.\n',eat.len, name);
      ii=1;
      while(ii<=1:eat.len) 
	if( ~ischar(eat.data{ii,1} ) )
	  fprintf('Line %d of %s contains non-character data.  Deleting.\n',...
		  ii, name );
	  eat.data(ii,:) = [];
	  eat.len=eat.len-1;
	end
	ii = ii+1;
      end
      %
      % Now match column numbers to the headings.  Do this, rather than
      % hardcoding column numbers, because eventually I'll add a new column
      % to the spreadsheet, and then harcoded numbers would be wrong.
      %
      % Note: I would have used a switch statement rather that if/elseif,
      % except "switch" can't take regular expressions.
      colCells = eat.data(1,:)';
      for( i=1:length(colCells) )
	if( isnan(cell2mat(colCells(i))) )
	  continue;
	end
	str = char(cell2mat(colCells(i)));
	if( regexp(str, 'Item') )
	  eat.ptr.item = i;
	elseif( regexp( str, 'Serving') )
	  eat.ptr.weight = i;
	elseif( regexp( str, 'Calories' ) )
	  eat.ptr.calories = i;
	elseif( regexp( str, 'Points' ) )
	  eat.ptr.points = i;
	elseif( regexp( str, 'Cal/100g' ) )
	  eat.ptr.calPer100g = i;
	elseif( regexp( str, 'Pt/100g') )
	  eat.ptr.ptsPer100g = i;
	elseif( regexp( str, '^[ ]*Fat') )
	  eat.ptr.fat = i;
	elseif( regexp( str, 'Sugar') )
	  eat.ptr.sugar = i;
	elseif( regexp( str, 'Sat Fat') )
	  eat.ptr.satFat = i;
	elseif( regexp( str, 'Carb') )
	  eat.ptr.carb = i;
	elseif( regexp( str, 'Fiber') )
	  eat.ptr.fiber = i;
	elseif( regexp( str, 'Protein') )
	  eat.ptr.protein = i;
	end
      end

    end
    function plot(eat)
      indx  = diff(floor(cell2mat(eat.log(3:end,2))));
      indxx = find(indx.*[1:length(indx)]')+2;     %Account for the header
      %indxW = isweekend(cell2mat(eat.log(3:end,2)));
      %indxW = find(indxW.*[1:length(indxW)]')+1;   %Account for the header
      %eat.indxW=indxW;
      %
      %Add yesterday's if there is no data for today in the log
      if( floor(cell2mat(eat.log(end,2))) ~= floor(now) )
       indxx = [indxx; length(eat.log)];  
      end
      eat.indxDay = indxx;

      if( isempty(eat.healthDataFig) )
	eat.healthDataFig = figure;
	set( eat.healthDataFig, 'Name', 'Weight Control Data', 'DeleteFcn', 'eat.healthDataFig=[];');
	set(gcf,'position',eat.position)
	fontsize = 20;
	fontweight = 'normal';
	set(gca,'fontsize',fontsize,'fontweigh',fontweight)
	set(gcf,'defaultaxesfontsize',fontsize,'defaulttextfontsize',fontsize)
      end
      figure(eat.healthDataFig);

      markersize=15;
      subplot(2,1,1)
      %plot(cell2mat(eat.log(indxx,2)),cell2mat(eat.log(indxx,6))/50,'ro-','markersize',markersize)
      useDateTime = 1;
      if(useDateTime)
	taxis = datetime(cell2mat(eat.log(indxx,2)), 'convertFrom', 'datenum');
      else
	taxis = cell2mat(eat.log(indxx,2));
      end
      plot(taxis, cell2mat(eat.log(indxx,6))/50,'ro-','markersize',markersize)
      hold on;
      grid on;
      %plot(cell2mat(eat.log(indxx,2)),cell2mat(eat.log(indxx,7)),'bx-','markersize',markersize)
      plot(taxis, cell2mat(eat.log(indxx,7)),'bx-','markersize',markersize)
      title('Weight Control Data');
      %ylabel('Calories/50, WW Points, Weight, lbf')
      ylabel('Calories/50, WW Points, Weight, lbf')
      legend('Calories/50','WW Points','location','best');
      %
      % Annotations:
      % Replace datetime below with datenum if useDateTime == 0.
      eat.thandle.Cienaga   = text(datetime(2016,3,11), 68, 'Cienaga');
      eat.thandle.Gville    = text(datetime(2016,3,18), 99, 'GeyserV');
      eat.thandle.Fremont   = text(datetime(2016,3,24), 67, 'Fremont');
      eat.thandle.HMB       = text(datetime(2016,4,1),  107, 'HMB');
      eat.thandle.Primavera = text(datetime(2016,4,16), 103, 'Primavera');
      eat.thandle.MCharlie  = text(datetime(2016,4,22), 90, 'Mtn. Charlie');
      eat.thandle.TB        = text(datetime(2016,4,29), 105, 'TB27');
      eat.thandle.TUC       = text(datetime(2016,5,6),  105, 'TUC22');
      eat.thandle.Diablo    = text(datetime(2016,5,13), 90,  'Diablo');
      eat.thandle.Gizditch  = text(datetime(2016,5,20), 88,  'Gizditch');
      eat.thandle.HamLDT    = text(datetime(2016,5,30), 77,  'Ham-LDT');
      eat.thandle.Sequoia   = text(datetime(2016,6,5),  103, 'Sequoia');
      eat.thandle.HamBack   = text(datetime(2016,7,5),  82,  'Ham-Back');
      eat.thandle.TomReyes  = text(datetime(2016,8,18), 82,  'Tomales-Reyes');
      eat.thandle.LaHGazos  = text(datetime(2016,9,3),  78,  'LaHonda-Gazos');
      eat.thandle.Flu2017   = text(datetime(2017,1,20), 25,  'Type A Influenza');
      eat.thandle.TB2017    = text(datetime(2017,4,1),  100, 'TB28');
      eat.thandle.MCharlie2017  = text(datetime(2017,4,15), 82, 'Mtn. Charlie');
      eat.thandle.Pescad2017= text(datetime(2017,4,22), 75,  'Pescadero');
      eat.thandle.Bache2017 = text(datetime(2017,4,28), 82,  'Mt.Bache');
      eat.thandle.GPC2017   = text(datetime(2017,5,7) , 90,  'GPC23');

      if( 0 )
	ylim = get(gca,'ylim');
	fill(taxis, isweekend(taxis)*ylim,...
	[0.9 0.9 0.9], 'Linestyle', '-', 'EdgeColor', [0.5 0.5 0.5], 'Linewidth', 1);
      end
      
      if( ~useDateTime ) 
	datetick('x',1);
      end
      %
      % sampling period = 1 day, 1/2 sampling freq = 2 days.  10 day
      % cutoff frequency, thus 2/10 is normalized omegan.
      [bb aa]= butter(4,2/10);
      eat.la=subplot(2,1,2);
      plot(taxis, cell2mat(eat.log(indxx,6)),'ro-', 'markersize',markersize)
      hold on;
      %tcdays = cell2mat(eat.log(indxx,2));
      tcdays = taxis;
      tccals = cell2mat(eat.log(indxx,6));
      %
      % Remove Nans:
      nindx = ~isnan(tccals);
      tcdays = tcdays(nindx);
      tccals = tccals(nindx);
      %
      plot(eat.la, tcdays, filtfilt(bb,aa,tccals), 'g');
      %
      posn=get(eat.la,'position');
      xlim=get(eat.la,'xlim');
      if( useDateTime )
	%
	% Because xlim above chops off the last day of weightLog.
	xlim(2) = xlim(2)+3;
	xlim(1) = datetime(datenum(2016,1,1), 'convertFrom', 'datenum');
	%
	% Convert to datenum for "axes" just below
	xlimNum = datenum(xlim);
      else
	xlim(1) = 736330; % 1 Jan 2016
	xlimNum = xlim;
      end
      eat.ra=axes('position',posn,'yaxislocation','right',...
      		  'ylim',[160 176],'xlim',xlimNum);
      %		  'ylim',[165 176],'xlim',xlimNum);
      % 	  'ylim',[171 177],'xlim',xlimNum);
      set(eat.ra,'color','none','xtick',[]);
      % [170 1;175 1]\[2500;4500] = [ 400.00;  -65500.00]
      %plot(eat.ra,cell2mat(eat.weightLog(:,1)), ...
      %	   cell2mat(eat.weightLog(:,2))*400-65500 ,'bd-')
      hold on
      if( useDateTime )
	raAxis = datetime(cell2mat(eat.weightLog(:,1)),'convertFrom', 'datenum');
      else
	raAxis = cell2mat(eat.weightLog(:,1));
      end
      plot(eat.ra, raAxis, cell2mat(eat.weightLog(:,2)),'bd-')
      %
      % Fit a line to the last 3 weeks with least squares and plot it.
      AA=cell2mat(eat.weightLog(end-21:end,:));
      lsqw=[AA(:,1) ones(length(AA),1)]\AA(:,2);
      lslinew = 2;
      plot(eat.ra,datetime(AA(:,1),'convertfrom','datenum'),...
	   AA(:,1)*lsqw(1)+lsqw(2),'k','linewidth',lslinew);
      eat.lsqw = lsqw;
      eat.textStrWt = sprintf('%.1f lbf %.2f lbf/Month', ...
	  mean(cell2mat(eat.weightLog(end-20:end,2))), lsqw(1)*30.42);
      text(eat.la,datetime(AA(1,1)-12,'convertfrom','datenum'),4200, eat.textStrWt,'color','k');
      %
      % Now filter weightLog:
      tdays = cell2mat(eat.weightLog(:,1));
      wdays = cell2mat(eat.weightLog(:,2));
      %
      % Remove Nans:
      nindx = ~isnan(wdays);
      tdays = tdays(nindx);
      if( useDateTime )
	tdays = datetime(tdays,'convertFrom', 'datenum');
      end
      wdays = wdays(nindx);
      plot(eat.ra, tdays, filtfilt(bb,aa,wdays), 'm');
      %
      % Fit a line to the last 3 weeks with least squares and plot it.
      AA=cell2mat([eat.log(eat.indxDay(end-21:end),2) ...
		   eat.log(eat.indxDay(end-21:end),6)]);
      lsqc=[AA(:,1) ones(length(AA),1)]\AA(:,2);
      plot(eat.la,datetime(AA(:,1),'convertfrom','datenum'),...
	   AA(:,1)*lsqc(1)+lsqc(2),'k','linewidth',lslinew);
      plot(eat.la,datetime(AA(:,1),'convertfrom','datenum'),...
	   AA(:,1)*lsqc(1)+lsqc(2),'g:','linewidth',lslinew);
      eat.lsqc = lsqc;
      eat.textStrCal = sprintf('%d Cal %d Cal/Month',...
	  round(mean(cell2mat(eat.log(eat.indxDay(end-20:end),6)))), round(lsqc(1)*30.42));
      %text(eat.la,datetime(AA(1,1)-10,'convertfrom','datenum'),AA(1,1)*lsqc(1)+lsqc(2)+100, eat.textStrCal);
      text(eat.la,datetime(AA(1,1)-12,'convertfrom','datenum'),4500, eat.textStrCal,'color','g');
      ylabel(eat.ra,'Weight, Blue')
      ylabel(eat.la,'Calories, Red')
      % str1=sprintf(['\bar{x}-2\sigma \bar{x}-\sigma \bar{x} ' ...
      % 		     '\bar{x}+\sigma \bar{x}+2\sigma'],'interpreter',...
      % 		     'latex');
      %fprintf(labelStr)
      for( ii=91:90:length(eat.weightLog) )
	data =(cell2mat(eat.weightLog(ii-90:ii,2))); 
	nanIndx = find(~isnan(data));
	md=mean(data(nanIndx));	sd=std(data(nanIndx)); ...
	fprintf('Three Months: %s - %s  [%.1f %.1f %.1f %.1f %.1f], sigma = %.1f\n',...	    
        datestr(cell2mat(eat.weightLog(ii-90,1))),datestr(cell2mat(eat.weightLog(ii,1))),...   
	md-2*sd, md-sd, md, md+sd, md+2*sd,sd);
      end
		
      data=(cell2mat(eat.weightLog(end-364:end,2))); md=mean(data); sd=std(data); ...
	   fprintf( 'Last Year:       [%.1f %.1f %.1f %.1f %.1f], sigma = %.1f\n',...
		   md-2*sd, md-sd, md, md+sd, md+2*sd,sd);
      data =(cell2mat(eat.weightLog(end-20:end,2))); md=mean(data);	sd=std(data); ...
	    fprintf('Last Three Weeks:[%.1f %.1f %.1f %.1f %.1f], sigma = %.1f\n',...
		    md-2*sd, md-sd, md, md+sd, md+2*sd,sd);
      mdays=0;
      data=cell2mat(eat.log(eat.indxDay(end-364-mdays:end-mdays),6)); ...
	   md=mean(data);sd=std(data);...
	   fprintf('One Year:   [%.0f %.0f %.0f %.0f %.0f], sigma = %.0f\n',...
		   md-2*sd, md-sd, md, md+sd, md+2*sd,sd);
      data=cell2mat(eat.log(eat.indxDay(end-20-mdays:end-mdays),6)); ...
	   md=mean(data);sd=std(data);...
	   fprintf('Three Weeks:[%.0f %.0f %.0f %.0f %.0f], sigma = %.0f\n',...
		   md-2*sd, md-sd, md, md+sd, md+2*sd,sd);
      xlabel(eat.la,'Time, Days');
      set(eat.la,'ygrid', 'on', 'xgrid','on');
      if( ~useDateTime )
	datetick(eat.la,'x',1);
      end
      set(eat.ra,'xtick',[]);
      set(eat.la,'ygrid', 'on', 'xgrid','on');
      set(eat.la,'ylim', [1500 5500]);
      %legend('Calories','Weight','location','best');
      
      
      linkaxes(get(gcf,'children'),'x')
      
    end
    function find(eat, food)
      if( isempty(food) )
	fprintf('find(food) - Finds the string ''food'' in the database.\n');
	return
      end
      if( ~ischar(food) )
	fprintf('Add - Error: Please profide a character string.\n');
	return
      end
      jj=0;
      eat.found = [];
      for(ii=1:eat.len) 
	name = eat.data{ii,eat.ptr.item};
	if( ~ischar(name) )
	  continue;
	end
	%if(regexp(char(eat.data{ii,eat.ptr.item}),food))
	if(regexp(name,food))
	  jj = jj + 1;
	  eat.found(jj,1) = ii;
	  fprintf(['%s\n',...
		   '    Weight   = %.1f\n',...
		   '    Calories = %.1f\n',...
		   '    Points   = %.1f\n',...
		   '    Cal/100g = %.1f\n',... 
		   '    Points/100g = %.1f\n'],...
		  eat.data{ii,eat.ptr.item}, eat.data{ii,eat.ptr.weight}, ...
		  eat.data{ii,eat.ptr.calories}, ...
		  eat.data{ii,eat.ptr.points}, ...
		  eat.data{ii,eat.ptr.calPer100g}, ...
		  eat.data{ii,eat.ptr.ptsPer100g});
	end
      end
      if( isempty(eat.found) )
	fprintf('%s not found.\n', food);
	eat.nfound = 0;
	return
      end
      eat.nfound = length(eat.found);
      if( eat.nfound > 1 )
	fprintf('Found %d of them.\n', eat.nfound);
      end
    end
    function add(eat, food, varargin)
    %
    % add( foodString, optional-grams, optional-date/time )
    %   foodString - Name of a food that is in the spreadsheet. If not, use
    %                addNew(). 
    %   grams      - Weight in grams.  If omitted, the algorim defaults to 
    %                the value in the spreadsheet.
    %   date       - Matlab's serial date number, most easily computed by
    %                datenum. If omitted, the algorithm defaults to now.
    %
    %   Note: You may omit both date and weight, or use either or both, in
    %   any order.
    %
      find(eat, food);
      if( eat.nfound > 1)
	fprintf('Please select only one.\n')
	return
      end
      grams = eat.data{eat.found,eat.ptr.weight};
      when = [];
      for( i=1:length(varargin) )
	if( varargin{i} < 10000 )
	  grams = varargin{i};
	  fprintf('add:grams = %.2f\n',varargin{i} );
	else
	  when = varargin{i};
	  fprintf('add:when = %.2f\n',varargin{i} );
	end
      end
      if( length(varargin) > 0 )
	fprintf('Debug:  eat.add has %d arguments.\n', length(varargin));
      end
      addg(eat, grams, when);
      eat.sofar
    end
    function insertg(eat, food, varargin)
    %
    % 'Name', grams, datenum.
      find(eat, food);
      if( eat.nfound > 1)
	fprintf('Please select only one.\n')
	return
      elseif( eat.nfound == 0)
	return
      end
      when = now;
      if( length(varargin) == 0 )
	grams = eat.data{eat.found,eat.ptr.weight};
      elseif( length(varargin) == 1 )
	grams = varargin{1};
      elseif( length(varargin) == 2 )
	if( isempty(varargin{1}) )
	  grams = eat.data{eat.found,eat.ptr.weight};
	else
	  grams = varargin{1};
	end
	when = varargin{2};
	%
	% varargin{2} must be a datenum.
	locIndx = indxfn2(when,cell2mat(eat.log(2:end,2)))+1;
	newrow = {eat.data{eat.found,eat.ptr.item}, when, grams, ...
		  eat.data{eat.found,eat.ptr.calPer100g}*grams/100, ...
		  eat.data{eat.found,eat.ptr.ptsPer100g}*grams/100, [] []};
	eat.log = [eat.log(1:locIndx,:); newrow; eat.log(locIndx+1:end,:)];
	indx = find( floor(when) == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	lenIndx = length(indx);
	len = length(eat.log(:,1));
	if( len == 1 || lenIndx == 0 )
	  fprintf('No data yet.\n');
	  return
	end
	%
	% Recompute columns that accumulate:
	%
	if( len == 1 || lenIndx == 0 )
	  return
	elseif( len == 2 || lenIndx < 2 )
	  eat.log{indx,6} = eat.log{indx,4};
	  eat.log{indx,7} = eat.log{indx,5};
	  return
	end
	for( ii=2:lenIndx )       %Already deleted a row.
	  eat.log{indx(ii),6} = eat.log{indx(ii),4} + ...
	      eat.log{indx(ii-1),6};
	  eat.log{indx(ii),7} = eat.log{indx(ii),5} + ...
	      eat.log{indx(ii-1),7};
	end
      else
	fprintf('Warning:  eat.insertg has %d arguments.\n', length(varargin));
      end
      eat.sofar(when)
    end
    function insertRow(eat, food, when, grams, cal, pts)
    %
    % insertRow(eat, food, when, grams, cal, pts)
    %    Insert a new row into the log, in a past day.
    %        food    - A string describing the food.
    %        when    - The date and time, in Matlab's serial date form. 
    %        grams   - Weight of the food in grams, assuming
    %                  g=9.806~m/s^2. 
    %        cal     - Food calories (science kCals).
    %        pts     - Weight watcher points.
    %
      if( ~ischar(food) ) 
	fprintf('insertRow - Error.  Please enter a string.\n');
	return
      end
      newrow = {food, when, grams, cal, pts, [] []};
      locIndx = indxfn2(when,cell2mat(eat.log(2:end,2)))+1;
      eat.log = [eat.log(1:locIndx,:); newrow; eat.log(locIndx+1:end,:)];
      indx = find( floor(when) == floor(cell2mat(eat.log(2:end,2))) ) + 1;
      lenIndx = length(indx);
      len = length(eat.log(:,1));
      if( len == 1 || lenIndx == 0 )
	fprintf('No data in the log yet.\n');
	return
      end
      %
      % Recompute columns that accumulate:
      %
      if( len == 1 || lenIndx == 0 )
	return
      elseif( len == 2 || lenIndx < 2 )
	eat.log{indx,6} = eat.log{indx,4};
	eat.log{indx,7} = eat.log{indx,5};
	return
      end
      for( ii=2:lenIndx )       %Already deleted a row.
	eat.log{indx(ii),6} = eat.log{indx(ii),4} + ...
	    eat.log{indx(ii-1),6};
	eat.log{indx(ii),7} = eat.log{indx(ii),5} + ...
	    eat.log{indx(ii-1),7};
      end
    end
    function addg(eat, grams, when)
    %
    % Assumes "find" has already been called.
    %
    if( isempty(when) )
      len = length(eat.log(:,1));
      len = len+1;
      eat.log{len,1} = eat.data{eat.found,eat.ptr.item};
      %eat.log{len,2} = datestr(now,'HH:MM:SS');
      eat.log{len,2} = now;
      eat.log{len,3} = grams;
      eat.log{len,4} = eat.data{eat.found,eat.ptr.calPer100g}*grams/100; %Cal
      eat.log{len,5} = eat.data{eat.found,eat.ptr.ptsPer100g}*grams/100; %Pts
      if( len == 2 || length(eat.indx) == 0 )
	eat.log{len,6} = eat.log{len,4};
	eat.log{len,7} = eat.log{len,5};
      else
	eat.log{len,6} = eat.log{len,4} + ...
	    eat.log{len-1,6};
	eat.log{len,7} = eat.log{len,5} + ...
	    eat.log{len-1,7};
      end
    else
      food  = eat.data{eat.found,eat.ptr.item};
      cal   = eat.data{eat.found,eat.ptr.calPer100g} *grams/100;
      pts   = eat.data{eat.found,eat.ptr.ptsPer100g}*grams/100;
      insertRow( eat, food, when, grams, cal, pts );
    end
    end
    function addi(eat, food, varargin)
    %
    % addi( food, optional-number, optional-date )
    %
    %   food    - Food name that exists in the spreadsheet.
    %   number  - Number of these.
    %   date    - Date at which to insert.
    %
    % If you omit "number", one is assumed.
    % If you omit "date", now is assumed.
    % 
    % You must specify a number to specify a date.  These must be in the
    % order specified above.
    %
      find(eat, food);
      when = [];
      if( length(varargin) == 0 )
	grams = eat.data{eat.found,eat.ptr.weight};
      elseif( length(varargin) == 1 )
	grams = varargin{1}*eat.data{eat.found,eat.ptr.weight};
      elseif( length(varargin) == 2 )
	grams = varargin{1}*eat.data{eat.found,eat.ptr.weight};
	when  = varargin{2};
      end
      addg(eat, grams, when);
      if( length(varargin) == 1 )
	eat.log{end,1} = ['(',num2str(varargin{1}),') ',eat.log{end,1}];
	eat.sofar
      elseif( length(varargin) == 2 )
	locIndx = indxfn2(when,cell2mat(eat.log(2:end,2)))+2;
	eat.log{locIndx,1} = ['(',num2str(varargin{1}),') ',eat.log{end,1}];
	eat.sofar(when)
      else
	eat.sofar
      end
    end
    function addNew(eat, food, grams, cal, pts, varargin)
    %
    % addNew( food, grams, cal, pts, optional-datenum)
    %   food    - Text string describing the food.
    %   grams   - Weight in grams (assuming g=9.806 m/s^2).
    %   cal     - Calories per gram * 100.
    %   pts     - Points per gram * 100.
    %   datenum - (Optional) date and time to insert, if not now.
    %
      if( ~ischar(food) ) 
	fprintf('Please enter a string.\n');
	return
      end
      %
      % If the item is for today:
      if( length(varargin) == 0 )
	when = now;
	len = length(eat.log(:,1));
	len = len+1;
	eat.log{len,1} = food;
	eat.log{len,2} = when;
	eat.log{len,3} = grams;
	eat.log{len,4} = grams*cal/100; %Cal
	eat.log{len,5} = grams*pts/100; %Pts
	if( len == 2 || length(eat.indx) == 0 )
	  eat.log{len,6} = eat.log{len,4};
	  eat.log{len,7} = eat.log{len,5};
	else
	  eat.log{len,6} = eat.log{len,4} + ...
	      eat.log{len-1,6};
	  eat.log{len,7} = eat.log{len,5} + ...
	      eat.log{len-1,7};
	end
	eat.sofar
	% If you ate the item yesterday or before:
      elseif( length(varargin) == 1 )
	when = varargin{1};
	%
	% varargin{2} must be a datenum.
	insertRow( eat, food, when, grams, grams*cal/100, grams*pts/100 );
	% locIndx = indxfn2(when,cell2mat(eat.log(2:end,2)))+1;
	% newrow = {food, when, grams, ...
	% 	  cal*grams/100, ...
	% 	  pts*grams/100, [] []};
	% eat.log = [eat.log(1:locIndx,:); newrow; eat.log(locIndx+1:end,:)];
	% indx = find( floor(when) == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	% lenIndx = length(indx);
	% len = length(eat.log(:,1));
	% if( len == 1 || lenIndx == 0 )
	%   fprintf('No data yet.\n');
	%   return
	% elseif( len == 2 || lenIndx < 2 )
	%   eat.log{indx,6} = eat.log{indx,4};
	%   eat.log{indx,7} = eat.log{indx,5};
	%   return
	% end
	% for( ii=2:lenIndx )       %Already deleted a row.
	%   eat.log{indx(ii),6} = eat.log{indx(ii),4} + ...
	%       eat.log{indx(ii-1),6};
	%   eat.log{indx(ii),7} = eat.log{indx(ii),5} + ...
	%       eat.log{indx(ii-1),7};
	% end
      else
	fprintf('Warning:  eat.addNew has %d arguments.\n', length(varargin));
      end
      eat.sofar(when)
    end
    function sofar(eat, varargin)
      todaysDate = floor(now);
      notToday=0;
      eat.indx = find( todaysDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
      if( isempty(varargin) )
	indx = eat.indx;
      else
	notToday=1;
	if( ischar(varargin{1}) )
	  %
	  % Can't find "vectorized" way to do this, yet.
	  %indx     = find( (regexp(eat.log(:,1), varargin{1} )));
	  indxcell = regexp(eat.log(:,1), varargin{1} );
	  for( i=1:length(indxcell) )
	    if( ~isempty(indxcell{i}) )
	      indxcell{i} = i;
	    end
	  end
	  indx = cell2mat(indxcell);
	elseif( isnumeric(varargin{1}) )
	  indx = find( varargin{1} == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	end
      end
      lenIndx = length(indx);
      if( isempty(lenIndx) || lenIndx == 0)
	fprintf('No data yet.\n');
	return
      end
      %fprintf('\n  %s\n\n',eat.todaysDate);
      if( notToday & isnumeric(varargin{1}) )
	datestring = datestr(floor(varargin{1}));
	dateday    = datestr(floor(varargin{1}), 8);
      else
	datestring = datestr(todaysDate);
	dateday    = datestr(todaysDate,8);
      end
      fprintf('\n                    %s  %s\n\n',datestring, dateday);
      fprintf('%3s %-25s %20s %10s %10s %10s %10s %10s\n',...
	      'No','      Food','Time      ','Weight','Calories','Points',...
	      'Cum Cals','Cum Pts');
      accumCals=0;
      accumPts =0;
      for( ii=1:lenIndx )
	name=eat.log{indx(ii),1};
	if( length(name) > 25 )
	  name = name(1:25);
	end
	if( notToday & ~isnumeric(varargin{1}) )
	  %datestring = datestr(eat.log{indx(ii),2},'yy-mmm-dd HH:MM:SS');
	  datestring = datestr(eat.log{indx(ii),2});
	  accumCals = accumCals + eat.log{indx(ii),4};
	  accumPts  = accumPts  + eat.log{indx(ii),5};
	else
	  datestring = datestr(eat.log{indx(ii),2},'HH:MM:SS');
	  accumCals = eat.log{indx(ii),6};
	  accumPts  = eat.log{indx(ii),7};
	end
	fprintf('%3d %-25s %20s %10d %10d %10.2f %10d %10.2f\n',...
	ii, ...                            %No
	name,...                           %Name
	datestring,...                     %Time
	round(eat.log{indx(ii),3}),...     %Weight
	round(eat.log{indx(ii),4}),...     %Cals
	eat.log{indx(ii),5},...            %Pts
	round(accumCals),...
	accumPts);
	%round(eat.log{indx(ii),6}),...     %Accum Cals
	%eat.log{indx(ii),7});              %Accum Pts.
      end
      eat.save;
    end
    function save( eat )
      log = eat.log;
      weightLog = eat.weightLog;
      SLog=whos('log');
      SWLog=whos('weightLog');
      eat.fileName = [datestr(now,'yyyymmdd') '.mat'];
      pathToFile = ['~/Documents/MATLAB/logs/' eval('eat.fileName')];
      save(pathToFile,'log','weightLog');
      fprintf('The log has %d bytes, %d records.\n',...
	      SLog.bytes, length(log));
      fprintf('The weight log has %d bytes, %d records.\n',...
	      SWLog.bytes, length(weightLog));
      [stat str] = system(['du -h ~/Documents/MATLAB/logs/' eat.fileName]);
      fprintf('The log file (both the above), consumes %s on disk.\n', strtok(str));
      % if( exist('/Volumes/rob/rob/data') == 7 ) 
      % 	tic
      % 	save(['/Volumes/rob/rob/data/' eat.fileName],'log','weightLog');
      % 	fprintf('The save to Atlas took %.1f seconds.\n',toc);
      % end
    end
    function load( eat, varargin )
    %
    % load( optional-date )
    %
    % PURPOSE: Load the food/calorie log.
    %
    % ARGUMENTS:
    %
    %  date  - String of the form yyyymmdd.  If you pass an empty string,
    %          it loads the most recent.
    %
      if( isempty(varargin) )
        %fileName = datestr(now,'yyyymmdd');
	%if( exist([fileName,'.mat']) ~= 2 )
	fileName = eat.fileName;
	%if( exist(eat.fileName) ~= 2 )
	if( exist(['~/Documents/MATLAB/logs/' eval('fileName')]) ~= 2)
	  fileName = datestr(now-1,'yyyymmdd');
	  fileName = [fileName,'.mat'];
	  if( exist(['~/Documents/MATLAB/logs/' eval('fileName')]) ~= 2)
	  %if( exist([fileName,'.mat']) ~= 2 )
	    fprintf('Can''t find yesterday''s data. Skip the load.\n');
	    return;
	  end
	  %
	  % Load yesterday's data:
	  fprintf('Loading yesterday''s data.\n');
	end
      else
	%
	% Assume the user inputs a datenum;
        fileName = varargin{1};
        fileName = [fileName,'.mat'];
      end
      pathToFile = ['~/Documents/MATLAB/logs/' eval('fileName')];
      load(pathToFile, 'log');
      %load(fileName,'log');
      S=whos('log');
      eat.log=log;
      fprintf('The log has %d bytes.\n', S.bytes);
      if( ~isempty(whos('-file',pathToFile,'weightLog')))
      %if( ~isempty(whos('-file',fileName,'weightLog')))
	load(pathToFile,'weightLog');
	SW=whos('weightLog');
	eat.weightLog = weightLog;
	clear weightLog;
	fprintf('The weight log has %d bytes.\n', SW.bytes);
	clear weightLog;
      end
      clear log;
      eat.fileName  = fileName;
    end
    function delete( eat, row, varargin )
    %
    % delete( row, date )
    %   row    - Row number.
    %   date   - Matlab serial date number of the year, month and day.
    %            This is most easily computed with datenum().
    %
    %            If you omit the date, it assumes today.
    %
    % This is a companion function to sofar().  To use this, You must
    % first execute sofar() to find the row number of the item you want to
    % delete. Then pass "delete()" that row number and the serial date
    % number computed by datenum().  Hours, minutes and seconds are
    % ignored and instead the algorithm uses the row number.
    %
      %row = row+1;     %Make it correspond to the cell array.
      indx = eat.indx;
      lenIndx = length(indx);
      if( length(varargin) ~= 0 )
	pastDate = floor(varargin{1});
	indx = find( pastDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	lenIndx = length(indx);
      end
      len = length(eat.log(:,1));
      if( len == 1 || lenIndx == 0 )
	fprintf('No data yet.\n');
	return
      end
      if( row > len || row < 1 || row > lenIndx )
	fprintf('Row %d is out of bounds.\n',row);
	return
      end
      eat.log(indx(row),:) = [];
      len=len-1;
      todaysDate = floor(now);
      eat.indx = find( todaysDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
      %
      % If deleting an item today:
      indx = eat.indx;
      lenIndx = length(indx);
      %
      % If the item is before today:
      if( length(varargin) ~= 0 )
	indx = find( pastDate == floor(cell2mat(eat.log(2:end,2))) ) + 1;
	lenIndx = length(indx);
      end
      %
      % Recompute columns that accumulate:
      %
      if( len == 1 || lenIndx == 0 )
	return
      elseif( len == 2 || lenIndx < 2 )
	eat.log{indx,6} = eat.log{indx,4};
	eat.log{indx,7} = eat.log{indx,5};
	return
      elseif( row == 1 )
	eat.log{indx(row),6} = eat.log{indx(row),4};
	eat.log{indx(row),7} = eat.log{indx(row),5};
      end
      for( ii=2:lenIndx )       %Already deleted a row.
        eat.log{indx(ii),6} = eat.log{indx(ii),4} + ...
	                      eat.log{indx(ii-1),6};
	eat.log{indx(ii),7} = eat.log{indx(ii),5} + ...
	                      eat.log{indx(ii-1),7};
      end
      if( length(varargin) == 0 )
	eat.sofar
      else
	fprintf('eat.delete - pastDate = %.2f\n',pastDate);
	eat.sofar(pastDate)
      end
    end
    function cereal( eat, bf, wg, m, bb )
    
      indx = eat.indx;
      lenIndx = length(indx);
    
     %Calories:          Points:
     %cbf = bf* 90/29;     pbf = bf*.036;       %Raleys
      cbf = bf* 160/41;    pbf = bf*.0685;      %Replace bf's with cin
      cwg = wg* 50/13;     pwg = wg*.0554;      
      cm = m * 130/242.6;  pm  = m *.0116;      
      cbb = bb* 39/72;     pbb = bb*.0067;      
      total = pbf+pwg+pm+pbb;  
      totalCal = cbf+cwg+cm+cbb;
      totalMass = bf+wg+m+bb;

     %fprintf('%3d bf   =  %5.1f cal = %3.1f ~= %d', ...
      fprintf('%3d cin  =  %5.1f cal = %3.1f ~= %d', ...
	      bf, cbf, pbf, eat.intPart(pbf));
      eat.printQ(pbf); fprintf('\n');
      fprintf('%3d wg   =  %5.1f cal = %3.1f ~= %d', ...
	      wg, cwg, pwg, eat.intPart(pwg));
      eat.printQ(pwg);fprintf('\n');
      fprintf('%3d milk =  %5.1f cal = %3.1f ~= %d', ...
	      m, cm, pm, eat.intPart(pm));
      eat.printQ(pm);fprintf('\n');
      fprintf('%3d blue =  %5.1f cal = %3.1f ~= %d', ...
	      bb, cbb, pbb, eat.intPart(pbb));
      eat.printQ(pbb);fprintf('\n');
      fprintf('    total=  %5.1f cal = %3.1f ~= %d', ...
	      totalCal, total, eat.intPart(total));
      eat.printQ(total);fprintf('\n');
      
      len = length(eat.log(:,1));
      len = len+1;
      if( bb == 0 || isempty(bb) )
	cerealStr = sprintf('Cereal(%d,%d,%d)',round(bf),round(wg), ...
			    round(m));
      else
	cerealStr = sprintf('Cereal(%d,%d,%d,%d)',round(bf),round(wg), ...
			    round(m),round(bb));
      end
      eat.log{len,1} = cerealStr;
      eat.log{len,2} = now;
      eat.log{len,3} = totalMass;
      eat.log{len,4} = totalCal;
      eat.log{len,5} = total;
      if( len == 2 || lenIndx == 0 )
	eat.log{len,6} = eat.log{len,4};
	eat.log{len,7} = eat.log{len,5};
      else
	eat.log{len,6} = eat.log{len,4} + ...
	                       eat.log{len-1,6};
	eat.log{len,7} = eat.log{len,5} + ...
	                       eat.log{len-1,7};
      end
      eat.sofar
    end
function nq=quarters(eat, value)
%
% Find the nearest 1/4
%
nq = mod(round(value*4),4);
end

function ip = intPart(eat, value)
%
ip = floor(value);
%
% Handle the case where the nearest quarter is the next higher integer,
% i.e. 5.9 -> 6.
%
if(value > 0 & eat.quarters(value) == 0)
    ip = round(value);
end
end

function printQ(eat,value)
%
nq = eat.quarters(value);
%
if(nq)
  if(nq == 2)
    fprintf(' 1/2');
  else
    fprintf(' %d/4', nq);
  end
else
  fprintf('    ');
end
end
    
    % function add(wb, stander)
    %   if( ~isempty(stander))
    % 	if(ischar(stander)) 
    % 	  wb.standers{end+1}=stander;
    % 	  wb.standers  = sort(wb.standers);
    % 	  wb.nstanders = length(wb.standers);
    % 	else
    % 	  fprintf('Add - Error: Please profide a character string.\n');
    % 	  return
    % 	end
    %   end
    % end
    %
    %
    function remove(wb, stander)
      if( ~isempty(stander) & ischar(stander) )
	indx=strcmp(wb.standers,stander);
	if( sum(indx)==0 )
	  fprintf('remove - Error: stander %s is not in the roster.\n', ...
		  stander);
	  return
	end
	wb.standers(indx) = [];
	wb.standers  = sort(wb.standers);
	wb.nstanders = length(wb.standers);
      else
	fprintf('remove - Error: Please profide a character string.\n');
	return
      end
    end
    %
    %
    function create(wb, varargin)
      if( ~isempty(varargin))
	if(ischar(varargin{1})) 
	  try
	    wb.startDateNum = datenum(varargin{1});
	  catch ME
	    fprintf('create - Error: Can''t read the start date %s, %s\n',...
		    varargin{1}, ME.ERRMSG);
	    return
	  end
	end
      end
      wb.nstanders = length(wb.standers);
      if( ~mod(length(wb.standers),3) )
	fprintf(['Watchbill:  The number of standers is a multiple of three.' ...
		 ' Implementing rotation.\n'])
	wb.rotate=1;   
      else
	wb.rotate=0;   
      end
      %
      % Ensure that the standers are alphabetical order:
      wb.standers = sort(wb.standers);
      %
      % Setup statistics array:
      wb.stats = cell(wb.nstanders+2,5);
      wb.stats{1,1} = 'Watch Standers';
      wb.stats{1,2} = 'Number of Day Shifts';
      wb.stats{1,3} = 'Number of Swing Shifts';
      wb.stats{1,4} = 'Number of Graveyard Shifts';
      wb.stats{1,5} = 'Total Number of Shifts';
      wb.stats(3:end,1)     = wb.standers;
      for( i=1:wb.nstanders )
	for(j=1:4)
	  wb.stats{i+2,j+1} = 0;
	end
      end
      %
      % Setup watch bill array:
      wb.bill = cell(wb.nrows*wb.nweeks,8);
      dayno = weekday(wb.startDateNum);
      for( i=0:wb.nweeks )
	wb.bill(i*wb.nrows+1,2:8) = ...
	    {'Sunday','Monday','Tuesday','Wednesday','Thursday',...
	     'Friday','Saturday'};
	wb.bill(i*wb.nrows+3,1) = {'Day  '};
	wb.bill(i*wb.nrows+4,1) = {'Swing'};
	wb.bill(i*wb.nrows+5,1) = {'Grave'};
	j=0;
	while( j<7 )
	  if( i == 0 & j == 0 ) 
	    j=dayno-1;
	    daycntr=wb.startDateNum-1;
	  end
	  daycntr = daycntr+1;
	  wb.bill{i*wb.nrows+2,j+2} = datestr(daycntr,1);
	  j=j+1;
	end
      end
      %
      rowset=0;
      k  = 0;
      %
      %i   = the watch number
      %ii  = corresponding row in the bill
      %kk  = the watchstander index, accounting for rotations.  It should
      %      always point to the same person.
      %k   = the number of rotations
      %j   = the watchstander array index.  When there are no rotations, j and
      %      kk are always the same.
      for( i=1:wb.nweeks*21 )                     
	ii = i+2-floor((i-1)/3)*3 + rowset;       
	j  = i-floor((i-1)/wb.nstanders)*wb.nstanders;
	wb.bill(ii,dayno+1)= wb.standers(j);
	kk = j+k;                        %index back to original list
	if( kk > wb.nstanders ) kk = kk-wb.nstanders;end
	wb.stats{kk+2,5} = wb.stats{kk+2,5} + 1;
	if( ~mod(i-1,3) ) wb.stats{kk+2,4} = wb.stats{kk+2,4} + 1;end  %Grave
	if( ~mod(i-2,3) ) wb.stats{kk+2,2} = wb.stats{kk+2,2} + 1;end  %Day
	if( ~mod(i-3,3) ) wb.stats{kk+2,3} = wb.stats{kk+2,3} + 1;end  %Swing
	if( ~mod(i,3) ) dayno=dayno+1;end
	if( dayno > 7 ) 
	  dayno=1;
	  rowset=rowset+wb.nrows;
	end
	if( wb.rotate && j == wb.nstanders )
	  temp = wb.standers(2:end);
	  temp(end+1) = wb.standers(1);
	  wb.standers=temp;
	  %
	  k=k+1;
	  if( k >= wb.nstanders ) k = 0;end
	end
      end
      %
      % Create cell array suitable for Excel.
      xlswrite('Watchbill.xls',wb.bill);
      xlswrite('WatchbillStats.xls',wb.stats);
    end
  end  %methods
end %classdef
