% Copyright 2005 2015 Ovidiu Gheorghies
% Licensed under the Apache License, Version 2.0.

if known _util_picture_mp:
  expandafter endinput
fi;
_util_picture_mp:=1;

% Sadly, this copy of the macro is needed to prevent multiple file loads being shown by MetaPost.
% The guard values (such as _metauml_mp) do ensure that the file isn't loaded multiple times, 
% but this macro makes sure that MetaPost won't try to load the file and display a message for that.
def inputonce text libraryFile=
	if not known scantokens ("_" & str libraryFile & "_mp"):
		%includeonce% show "Loading " & str libraryFile;
		scantokens ("input " & str libraryFile);
	else:
		%includeonce% show str libraryFile & " already loaded.";
	fi;
enddef;

inputonce util_log;
inputonce util_margins;

if not known _n_cur_:input boxes;fi;

vardef FontInfo@#(expr tname, tscale)=
  attributes(@#);
  var(string) className;
  var(string) name;
  var(numeric) scale;

  @#className := "FontInfo";

  @#name := tname;
  @#scale := tscale;
enddef;

vardef FontInfoCopy@#(text fontInfo)=
  FontInfo@#(fontInfo.name, fontInfo.scale);
  @#ignoreNegativeBase := fontInfo.ignoreNegativeBase;
enddef;

vardef FontInfo_toString@#=
  save ret; string ret;
  ret := "FontInfo(" & (str @#) & "): {" & (@#name) & ", " & (decimal @#scale) & "}";
  ret
enddef;

log "()()() Creating iFont";
FontInfo.iFont(defaultfont, defaultscale);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%                                                                 %
%                         PICTURE                                 %
%                                                                 %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

vardef PictureInfo@#(expr pleft, pright, ptop, pbottom)(text pfont)=
  attributes(@#);
  var(string) className;
  @#className := "PictureInfo";

  var(color)   borderColor;
  var(numeric) boxed;

  %% Used to set the base of a text to the natural base of the letters.
  %% If set to '1', 'y' and 'a' will have the same base line, the tail
  %% of 'y' being rendered below that line
  %%
  var(numeric) ignoreNegativeBase;

  %% If these attributes are set to -1, the height and width of the
  %% Picture object is deduced from the dimensions of the contained
  %% object (picture or text). If one of these parameters is set to
  %% a positive value, then the given value is used, regardless of the
  %% size of the contents.
  %%
  var(numeric) fixedWidth, fixedHeight;

  %% Controls how the contents of the picture is to be aligned relatively
  %% to the picture margins. Can be one of "left", "centered", and defaults to
  %% "left". The "centered" setting is useful for Picture-s whose fixedWidth
  %% is set to a given value.
  %%
  var(string) halign, valign;

  var(string) textDecoration;

  @#boxed := 0;
  @#borderColor := blue;

  @#ignoreNegativeBase := 0;

  @#fixedWidth  := -1;
  @#fixedHeight := -1;

  @#halign := "left";
  @#valign := "bottom";

  @#textDecoration := "none";

  Margins.@#(pleft, pright, ptop, pbottom);

  FontInfoCopy.@#iFont(pfont);
enddef;

vardef PictureInfoCopy@#(text src)=
  PictureInfo@#(src.left, src.right, src.top, src.bottom)(src.iFont);

  @#boxed := src.boxed;
  @#borderColor := src.borderColor;
  @#ignoreNegativeBase := src.ignoreNegativeBase;
  @#textDecoration := src.textDecoration;

  @#fixedWidth := src.fixedWidth;
  @#fixedHeight := src.fixedHeight;
  
  @#halign := src.halign;
  @#valign := src.valign;
enddef;

vardef PictureInfo_toString@#=
  save ret; string ret;
  ret := "PictureInfo(" & (str @#) & "): {" & (decimal @#left) & ", " & (decimal @#right) & ", " & (decimal @#top) & ", " & (decimal @#bottom) & "}" & " boxed: " & (decimal @#boxed);
  ret := ret & " Font: " & toString(@#iFont);
  ret
enddef;

PictureInfo.iPict(2, 2, 2, 2)(iFont);
PictureInfo.iPictNoMargins(0, 0, 0, 0)(iFont);

PictureInfoCopy.iPictBoxed(iPict);
iPictBoxed.boxed := 1;

%%
%% Contructs a Picture object by wrapping around the
%% low-level picture thePict.
%%
vardef EPicture@#(text iPict)(expr thePict) =
  ObjectEquations(@#);
  @#className := "Picture";

  var(pair)    contentShift;
  var(picture) pict;
  var(numeric) negativeBase;
  var(string)  contentAsString;
  var(picture) contentAsPicture;

  PictureInfoCopy.@#info(iPict);

  if string thePict:
    log "Picture " & (str @#) & " is a text";
    @#contentAsString := thePict;
    log @#contentAsString;
  elseif picture thePict:
    log "Picture " & (str @#) & " is a native pict";
    @#contentAsPicture := thePict;
    log @#contentAsPicture;
  else:
    log "Picture " & (str @#) & " [error]";
  fi;

enddef;

%%
%% Contructs a Picture object by wrapping around the
%% low-level picture thePict.
%%
vardef Picture@#(expr thePict) =
  EPicture@#(iPict)(thePict);
enddef;

%%
%% Lays out the Picture.
%%
vardef Picture_layout@# =
  if @#laidout = 1:
    log "Picture " & str @# & " already laid out.";
  else:
    log "Laying out " & (str @#);

    @#laidout := 1;

    if known @#contentAsPicture:
      log "Content is known to be a picture";
      @#pict = @#contentAsPicture;
    elseif known @#contentAsString:
      log "Content is known to be a string: " & @#contentAsString;
      @#pict = @#contentAsString infont @#info.iFont.name scaled @#info.iFont.scale;
    else:
      log "Show unknown parameter type in picture layout";
      2 = 3; % force an error
    fi;

    @#negativeBase = ypart llcorner @#pict;

    if @#info.ignoreNegativeBase = 0:
      negativeBaseAdjustement__ := 0;
    else:
      negativeBaseAdjustement__ := @#negativeBase;
    fi; 

    if @#info.fixedWidth = -1:
      @#width = pictWidth(@#pict) + @#info.left + @#info.right;
    else:
      @#width = @#info.fixedWidth;
    fi;

    if @#info.fixedHeight = -1:
      @#height = pictHeight(@#pict) + @#info.top + @#info.bottom + negativeBaseAdjustement__;
    else:
      @#height = @#info.fixedHeight;
    fi;

    if @#info.halign = "left":
      xdelta__ := @#info.left;
    elseif @#info.halign = "center":
      xdelta__ := (@#width-pictWidth(@#pict))/2;
    else:
      2 = 3; % throw exception illegal value for @#info.halign
    fi;

    if @#info.valign = "bottom":
      ydelta__ := @#info.bottom - @#negativeBase + negativeBaseAdjustement__;
    elseif @#info.valign = "center":
      ydelta__ := (@#height-pictHeight(@#pict))/2 - @#negativeBase + negativeBaseAdjustement__;
    else:
      2 = 3; % throw exception illegal value for @#info.valign
    fi;

    @#contentShift = @#sw + (xdelta__, ydelta__);
  fi;
enddef;

%%
%% Draws the Picture.
%%
vardef Picture_draw@# =
  Picture_layout@#;
  objectEnsurePositioning.@#;
  
  draw @#pict shifted @#contentShift;

  if (@#info.textDecoration = "underline"):
    save ydelta, height;
    height := pictHeight(@#pict) - @#negativeBase;

    ydelta := @#info.bottom - @#negativeBase - 0.8;

    draw @#sw + (@#info.left, ydelta) -- @#se + (-@#info.right, ydelta) withcolor black withpen pencircle scaled 0.2bp;
  fi;

  if (@#info.boxed = 1):
    draw objectBox(@#) withcolor @#info.borderColor;
  fi;
enddef;

vardef Picture_border@#=
    objectBox(@#)
enddef;

vardef aitem(text pictInfo)(text thePict)(text equation)=
  save obj;
  EPicture.obj(pictInfo)(thePict);
  equation;
  drawObject(obj);
enddef;

vardef item@#(text iPict)(text thePict)(text equation)=
  save itemName;
  string itemName;
  itemName := str @#;

  log "Creating an item with name: '" & itemName & "'";

  if itemName = "":
    log "Name is empty, creating an anonymous item!";
    aitem(iPict)(thePict)(equation);
  else:
    EPicture@#(iPict)(thePict);
    equation;
    drawObject(@#);
  fi;
enddef;

