require "theme"

direlayout = obj {
  nam='@direlayout';
}

local function is_rect(c)
  return c.x and c.y and c.h and c.w;
end;

local function constraint_to_rect(constraint, canvas)
  if is_rect(constraint) then
    return constraint;
  end;

  if canvas == nil then
    canvas = {
      x = 0;
      y = 0;
      w = tonumber(theme.get('scr.w'));
      h = tonumber(theme.get('scr.h'));
    }
  end;

  local c
  if constraint.leftof then
    c = constraint_to_rect(constraint.leftof, canvas);

    canvas = {
      x = canvas.x,
      y = canvas.y,
      w = c.x - canvas.x;
      h = canvas.h;
    }
  end;

  if constraint.bottomof then
    c = constraint_to_rect(constraint.bottomof, canvas);

    canvas = {
      x = canvas.x,
      y = c.y + c.h,
      w = canvas.w,
      h = canvas.h - c.h - c.y + canvas.y;
    }
  end;

  if constraint.rightof then
    c = constraint_to_rect(constraint.rightof, canvas);

    canvas = {
      x = c.x + c.w,
      y = canvas.y,
      w = canvas.w - c.w - c.x + canvas.x;
      h = canvas.h;
    }
  end;

  if constraint.topof then
    c = constraint_to_rect(constraint.topof, canvas);

    canvas = {
      x = canvas.x,
      y = canvas.y,
      w = canvas.w;
      h = c.y - canvas.y;
    }
  end;

  -- if both lx and rx not nil -- center horizontally
  -- if both ty and by not nil -- center vertically
  -- aspect ratio requirement is always satisfied

  -- first attempt to take as much space as possible
  local x = 0;
  local y = 0;
  local w = canvas.w;
  local h = canvas.h;

  if constraint.wf then
    w = math.floor(w * constraint.wf);
  end;

  if constraint.hf then
    h = math.floor(h * constraint.hf);
  end;

  -- then limit width and height to maximum allowed
  if constraint.wmax ~= nil and w > constraint.wmax then
    w = constraint.wmax
  end;
  if constraint.hmax ~= nil and h > constraint.hmax then
    h = constraint.hmax
  end;

  -- then handle margins (lx, rx, ty, by)
  local llx = constraint.lx or 0;
  local tty = constraint.ty or 0;
  local rrx = constraint.rx or 0;
  local bby = constraint.by or 0;

  if w + llx + rrx > canvas.w then
    local wmin = constraint.wmin or 1
    if wmin >= canvas.w - llx - rrx then
      w = wmin;
    else
      w = canvas.w - llx - rrx
    end;
  end;

  if h + tty + bby > canvas.h then
    local hmin = constraint.hmin or 1
    if hmin >= canvas.h - tty - bby then
      h = hmin;
    else
      h = canvas.h - tty - bby
    end;
  end;

  -- then apply aspect ratio
  local aspect = w / h;
  if constraint.rmax ~= nil and aspect > constraint.rmax then
    w = math.floor(h * constraint.rmax);
  end;

  if constraint.rmin ~= nil and aspect < constraint.rmin then
    h = math.floor(w / constraint.rmin);
  end;

  -- then actually position elements
  -- if a < 0 then preserve left/top margin as much as possible
  -- if a = 0 then reduce both paddings equally
  -- if a > 0 then preserve right/bottom margin as much as possible
  if constraint.ax == nil or constraint.ax == 0 then
    x = llx - math.floor((w + llx + rrx - canvas.w) / 2);
  elseif constraint.ax > 0 then
    x = canvas.w - w - rrx;
    if constraint.ax < 2 and x < llx then
      x = llx;
    end;
  elseif constraint.ax < 0 then
    x = llx;
    if constraint.ax > -2 and x + w > canvas.w - rrx then
      x = canvas.w - rrx - w;
    end;
  end;

  if constraint.ay == nil or constraint.ay == 0 then
    y = tty - math.floor((h + tty + bby - canvas.h) / 2);
  elseif constraint.ay > 0 then
    y = canvas.h - h - bby;
    if constraint.ay < 2 and y < tty then
      y = tty;
    end;
  elseif constraint.ay < 0 then
    y = tty;
    if constraint.ay > -2 and y + h > canvas.h - bby then
      y = canvas.h - bby - h;
    end;
  end;
  
  if constraint.dx then
    x = x + constraint.dx;
  end;
  
  if constraint.dy then
    y = y + constraint.dy;
  end;
  
  x = x + canvas.x;
  y = y + canvas.y;
  
  if constraint.pf then
    return constraint.pf({
      x = x,
      y = y,
      w = w,
      h = h
    });
  end;

  return {
    x = x,
    y = y,
    w = w,
    h = h
  }
end;

direlayout.constraint_to_rect = constraint_to_rect;

