require "sprite"
require "lib/dire/color2rgb"

local drawmethods;

-- most lowlevel dire(dragon's instead rendering extensions) routines
-- position {x, y, w, h}

-- Draw src centered to position on dst. Clip to position
function dire_draw_center(src, dst, position)
  if not src then return end;
  local sw, sh = src:size()
  local sx = 0
  local sy = 0
  local dx = 0
  local dy = 0

  if sw > position.w then
    sx = math.floor((sw - position.w) / 2)
    sw = position.w
  elseif sw < position.w then
    dx = math.floor((position.w - sw) / 2)
  end

  if sh > position.h then
    sy = math.floor((sh - position.h) / 2)
    sh = position.h
  elseif sh < position.h then
    dy = math.floor((position.h - sh) / 2)
  end
  
  src:blend(sx, sy, sw, sh, dst, position.x + dx, position.y + dy)
end

-- Draw src tiled to position on dst. Clip to position
-- offset: 0 or nil -- left/top, -1 -- center/center, -2 -- right/bottom
function dire_draw_tile(src, dst, position, options)
  local offset_x = options.xoffset
  local offset_y = options.yoffset
  local sw, sh = src:size()

  if offset_x == nil then
    offset_x = 0
  elseif offset_x == -1 then
    offset_x = math.floor((math.ceil(position.w / sw) * sw - position.w) / 2)
  elseif offset_x == -2 then
    offset_x = (math.ceil(position.w / sw) * sw - position.w)
  elseif offset_x > 0 then
    offset_x = offset_x - sw
  end

  if offset_y == nil then
    offset_y = 0
  elseif offset_y == -1 then
    offset_y = math.floor((math.ceil(position.h / sh) * sh - position.h) / 2)
  elseif offset_y == -2 then
    offset_y = (math.ceil(position.h / sh) * sh - position.h)
  elseif offset_y > 0 then
    offset_y = offset_y - sw
  end

  local rem_h = position.h;
  while rem_h > 0 do
    local coffset_x = offset_x
    local rem_w = position.w
    while rem_w > 0 do
      local w = sw - coffset_x
      if w > rem_w then w = rem_w end
      local h = sh - offset_y
      if h > rem_h then h = rem_h end
      src:blend(coffset_x, offset_y, w, h, dst, position.x + position.w - rem_w, position.y + position.h - rem_h)
      rem_w = rem_w - sw + coffset_x
      coffset_x = 0
    end;
    rem_h = rem_h - sh + offset_y
    offset_y = 0
  end
end

-- Draw src letterboxed to position on dst. Clip to position
function dire_draw_letterbox(src, dst, position, options)
  local sw, sh = src:size();
  local scalex = position.w / sw
  local scaley = position.h / sh
  if scalex < scaley then
    scaley = scalex;
  else
    scalex = scaley;
  end;
  local src_scaled;
  if scalex == 1 then
    src_scaled = src;
  else
    src_scaled = src:scale(scalex, scaley, options.smooth);
    sw, sh = src_scaled:size();
  end;
  local dx = math.floor((position.w - sw) / 2)
  local dy = math.floor((position.h - sh) / 2)
  src_scaled:blend(0, 0, sw, sh, dst, position.x + dx, position.y + dy);
end

-- Draw src filling position on dst. Clip to position
function dire_draw_fill(src, dst, position, options)
  local sw, sh = src:size();
  local scalex = position.w / sw
  local scaley = position.h / sh
  if scalex > scaley then
    scaley = scalex;
  else
    scalex = scaley;
  end;
  local src_scaled;
  if scalex == 1 then
    src_scaled = src;
  else
    src_scaled = src:scale(scalex, scaley, options.smooth);
    sw, sh = src_scaled:size();
  end;
  local dx = math.floor((sw - position.w) / 2)
  local dy = math.floor((sh - position.h) / 2)
  src_scaled:blend(dx, dy, sw - 2 * dx, sh - 2 * dy, dst, position.x, position.y);
end

-- Draw src stretching over position on dst. Clip to position
function dire_draw_stretch(src, dst, position, options)
  local sw, sh = src:size();
  local scalex = position.w / sw
  local scaley = position.h / sh
  local src_scaled;
  if scalex == 1 and scaley == 1 then
    src_scaled = src;
  else
    src_scaled = src:scale(scalex, scaley, options.smooth);
    sw, sh = src_scaled:size();
  end;
  src_scaled:blend(0, 0, sw, sh, dst, position.x, position.y);
end

-- Draw src 9-scaled over position on dst. Clip to position
function dire_map_9(src, dst, position, options)
  local slice = options.parts;

  local rval = {};

  local x, y;
  if position.w >= slice.x1 + slice.x3 then
    x = {
      slice.x1;
      position.w - slice.x1 - slice.x3;
      slice.x3;
    };
  else
    x = {
      math.floor(position.w / 2);
      0;
      position.w - math.floor(position.w / 2);
    };
  end;

  if position.h >= slice.y1 + slice.y3 then
    y = {
      slice.y1;
      position.h - slice.y1 - slice.y3;
      slice.y3;
    };
  else
    y = {
      math.floor(position.h / 2);
      0;
      position.h - math.floor(position.h / 2);
    };
  end;

  local dx = {
    position.x,
    position.x + x[1],
    position.x + x[1] + x[2];
  }

  local dy = {
    position.y,
    position.y + y[1],
    position.y + y[1] + y[2];
  }

  for i = 1, 9 do
    local iy = math.floor((i - 1) / 3);
    local ix = (i - 1) % 3;

    local drawmethod = dire_draw_tile;

    if options.drawmethod then
      if type(options.drawmethod) == 'table' then
        drawmethod = drawmethods[options.drawmethod[i]];
      else
        drawmethod = drawmethods[options.drawmethod];
      end;
    end;

    local drawoptions = nil

    if options.drawoptions then
      if typeof(options.drawoptions) == 'table' then
        drawoptions = drawmethods[options.drawmethod[i]]
      else
        drawoptions = drawmethods[options.drawmethod]
      end;

      if drawoptions.xoffset == nil then
        drawoptions.xoffset=-ix
      end;

      if drawoptions.yoffset == nil then
        drawoptions.yoffset=-iy
      end;
    else
      drawoptions = {
        smooth=options.smooth,
        xoffset=-ix,
        yoffset=-iy
      }
    end;

    rval[#rval + 1] = {
      drawmethod, src[i], dst, {x = dx[ix + 1], y = dy[iy + 1], w = x[ix + 1], h = y[iy + 1]}, drawoptions
    }
  end;

  return rval;
end

function dire_draw_9(src, dst, position, options)
  local fields;
  if dst == nil then
    fields = src;
  else
    fields = dire_map_9(src, dst, position, options);
  end;

  for k, f in pairs(fields) do
    f[1](f[2], f[3], f[4], f[5]);
  end;
end;

-- pixels: pixels, slice: position
-- return 9 new pixels objects, sliced by slice
function dire_slice_9(src, slice)
  local pw, ph = src:size();
  if not slice then
    local pw3 = math.floor(pw / 3);
    local ph3 = math.floor(ph / 3);
    slice = {
      x = pw3,
      y = ph3,
      w = pw - 2 * pw3,
      h = ph - 2 * ph3
    };
  end;
  local pw = pw - slice.w - slice.x;
  local ph = ph - slice.h - slice.y;
  local rpixels = {
    pixels.new(slice.x, slice.y),
    pixels.new(slice.w, slice.y),
    pixels.new(pw, slice.y),
    pixels.new(slice.x, slice.h),
    pixels.new(slice.w, slice.h),
    pixels.new(pw, slice.h),
    pixels.new(slice.x, ph),
    pixels.new(slice.w, ph),
    pixels.new(pw, ph)
  };

  src:blend(0, 0, slice.x, slice.y, rpixels[1], 0, 0);
  src:blend(slice.x, 0, slice.w, slice.y, rpixels[2], 0, 0);
  src:blend(slice.x + slice.w, 0, pw, slice.y, rpixels[3], 0, 0);
  src:blend(0, slice.y, slice.x, slice.h, rpixels[4], 0, 0);
  src:blend(slice.x, slice.y, slice.w, slice.h, rpixels[5], 0, 0);
  src:blend(slice.x + slice.w, slice.y, pw, slice.h, rpixels[6], 0, 0);
  src:blend(0, slice.y + slice.h, slice.x, ph, rpixels[7], 0, 0);
  src:blend(slice.x, slice.y + slice.h, slice.w, ph, rpixels[8], 0, 0);
  src:blend(slice.x + slice.w, slice.y + slice.h, pw, ph, rpixels[9], 0, 0);


  return rpixels, {
    x1=slice.x,
    x2=slice.w,
    x3=pw,
    y1=slice.y,
    y2=slice.h,
    y3=ph
  };
end;

drawmethods = {
  dire_draw_center,
  dire_draw_tile,
  dire_draw_letterbox,
  dire_draw_fill,
  dire_draw_stretch
}

