-- Extends dire with theme handling
require "theme"
require "lib/dire/core"
require "lib/dire/layout"

local default_canvas = nil
local scr_width = nil
local scr_height = nil
local scr_inches = nil
local scr_width_adjusted = nil
local scr_height_adjusted = nil
local scaled_width = nil
local scaled_height = nil

local scale = 1
local control_scaling = false
local smooth = nil

local theme_changed = true

local background = nil

local gfx = {
  inv = {},
  win = {},
};
local gfx_sprite = {
  inv = {},
  win = {}
};

local bgsprite = nil
local default_cursor = nil;
local cursor_sprite = nil;
local default_cursor_use = nil;
local cursor_use_sprite = nil;
local menu_button_sprite = nil

local cursor_options = {};

local area_options = {
  inv={},
  text={},
  image={},
  menu={}
}

local draw_order = {'inv', 'menu', 'text', 'image'}

local function draw_pixels(src, tgt, rect, options)
  if not options then
    options = {};
  end;

  options.smooth = smooth;

  local mode = options.mode or 1

  if mode == 1 then
    dire_draw_center(src, tgt, rect, options);
  elseif mode == 2 then
    if options.xoffset == nil then
      options.xoffset = -1;
    end;
    if options.yoffset == nil then
      options.yoffset = -1;
    end;
    dire_draw_tile(src, tgt, rect, options);
  elseif mode == 3 then
    dire_draw_letterbox(src, tgt, rect, options);
  elseif mode == 4 then
    dire_draw_fill(src, tgt, rect, options);
  elseif mode == 5 then
    dire_draw_stretch(src, tgt, rect, options);
  elseif mode == 6 then
    if not options.drawmethod then
      options.drawmethod = 2;
    end;
    dire_draw_9(src, tgt, rect, options);
  end;
end;

local scaled_constraint_fields = {'wmax', 'wmin', 'hmax', 'hmin', 'lx', 'ty', 'rx', 'by', 'x', 'y', 'w', 'h'};
local no_scaled_constraint_fields = {'wf', 'hf', 'rmax', 'rmin', 'ax', 'ay', 'leftof', 'rightof', 'topof', 'bottomof', 'pf'};
local map_option_fields = {'x1', 'x2', 'x3', 'y1', 'y2', 'y3'}

local function __scale_pixels(pixels, scale, smooth)
  if scale and scale ~= 1 then
    if type(pixels) == 'table' then
      local rval = {}
      
      for i, v in pairs(pixels) do
        rval[i] = v:scale(scale, scale, smooth);
      end;
      
      return rval;
    else
      return pixels:scale(scale, scale, smooth);
    end;
  end;
  return pixels;
end;

local function __scale_map(map, scale, smooth)
  if not map then
    return nil;
  end;

  if scale and scale ~= 1 then
    local pixels = __scale_pixels(map.p, scale);
    local options;
    
    if map.o then
      options = {};
      for k, v in pairs(map.o) do
        options[k] = v;
      end;
      
      if map.o.parts then
        options.parts = {}
        for k, v in pairs(map.o.parts) do
          options.parts[k] = math.floor(v * scale);
        end;
      end;
    end;
    
    return {
      p = pixels,
      o = options,
      w = map.w * scale,
      h = map.h * scale
    }
  else
    return map;
  end;
end;

local function map_to_sprite(map, doscale, scalefactor)
  if not map then
    return nil;
  end;
  
  if type(map.p) == 'function' then
    error('Not implemented yet')
  else
    if control_scaling and doscale then
      if scalefactor then
        map = __scale_map(map, scalefactor, smooth);
      else
        map = __scale_map(map, scale, smooth);
      end;
    end;
    
    local trect = { x = 0, y = 0, w = map.w, h = map.h };
    local tgt_pixels = pixels.new(trect.w, trect.h);
  
    draw_pixels(map.p, tgt_pixels, trect, map.o)
    return tgt_pixels:tosprite();
  end;
end;

local function pixels_to_map(p)
  local w, h = p:size();
  return {
    p=p,
    options={},
    w=w,
    h=h
  }
end;

local function scale_rect(area, noscale)
  if noscale or scale == 1 then
    return area;
  end;

  local rval = {};

  for _, f in pairs(scaled_constraint_fields) do
    if area[f] then
      rval[f] = math.floor(area[f] * scale);
    end;
  end;

  for _, f in pairs(no_scaled_constraint_fields) do
    rval[f] = area[f];
  end;

  return rval;
end;

local function draw(src, tgt, rect)
  if src.color then
    local bgcolorr, bgcolorg, bgcolorb = color2rgb(src.color);
    tgt:fill(rect.x, rect.y, rect.w, rect.h, bgcolorr, bgcolorg, bgcolorb, src.alpha or 255)
  end;

  if type(src.image) == 'function' then
    src.image(tgt, rect, src.options);
  end;
  
  if src._map then
    local map = src._map;
    if control_scaling and map.o.scale then
      map = __scale_map(map, scale, smooth);
    end;

    draw_pixels(map.p, tgt, rect, map.o);
  end;
end;

local function apply_font(data, prefix)
   if data and data.color then
    theme.set(prefix .. '.col.fg', data.color);
  else
    theme.reset(prefix .. '.col.fg');
  end;

  if data and data.link then
    theme.set(prefix .. '.col.link', data.link);
  else
    theme.reset(prefix .. '.col.link');
  end;

  if data and data.alink then
    theme.set(prefix .. '.col.alink', data.alink);
  else
    theme.reset(prefix .. '.col.alink');
  end;

  if data and data.font then
    theme.set(prefix .. '.fnt.name', data.font);
  else
    theme.reset(prefix .. '.fnt.name');
  end;
  
  if data and data.size then
    local size = data.size;

    if control_scaling and not data.noscale then
      size = math.floor(size * scale);
    end;

    theme.set(prefix .. '.fnt.size', size);
  else
    theme.reset(prefix .. '.fnt.size');
  end;

  if data and data.height then
    theme.set(prefix .. '.col.height', data.height);
  else
    theme.reset(prefix .. '.col.height');
  end;
end;

local function apply_area_options(area, leftof, rightof, topof, bottomof, px, prefix)
  local rect = area.rect;
  local crect = nil;

  if rect then
    rect.leftof = leftof;
    rect.rightof = rightof;
    rect.topof = topof;
    rect.bottomof = bottomof;

    rect = direlayout.constraint_to_rect(scale_rect(rect, rect.noscale), default_canvas);
  else
    theme.reset(prefix .. '.x');
    theme.reset(prefix .. '.y');
    theme.reset(prefix .. '.w');
    theme.reset(prefix .. '.h');

    rect = {
      x = tonumber(theme.get(prefix .. '.x')),
      y = tonumber(theme.get(prefix .. '.y')),
      w = tonumber(theme.get(prefix .. '.w')),
      h = tonumber(theme.get(prefix .. '.h')),
    };
  end;

  if area.contentrect then
    crect = direlayout.constraint_to_rect(scale_rect(area.contentrect, area.contentrect.noscale), rect);
  end;

  if crect == nil then
    crect = rect;
  end;

  if area.bg then
    draw(area.bg, px, rect);
  end;

  return rect, crect;
end;

local function apply_content_options(area, crect, px, prefix)
  if area._up_map then
    gfx_sprite[prefix].up = map_to_sprite(area._up_map, area.scaleui);
  else
    gfx_sprite[prefix].up = map_to_sprite(gfx[prefix].up, area.scaleui);
  end;
  theme.set(prefix .. '.gfx.up', gfx_sprite[prefix].up);

  if area._down_map then
    gfx_sprite[prefix].down = map_to_sprite(area._down_map, area.scaleui);
  else
    gfx_sprite[prefix].down = map_to_sprite(gfx[prefix].down, area.scaleui);
  end;
  theme.set(prefix .. '.gfx.down', gfx_sprite[prefix].down);

  local upx = -1;
  local upy = -1;
  
  if area.uppos then
    local x,y = gfx_sprite[prefix].up:size();
  
    if area.uppos.x == nil then
      theme.reset(prefix .. '.up.x');
    elseif area.uppos.ax < 0 then
      theme.set(prefix .. '.up.x', crect.x + area.uppos.x);
    else
      theme.set(prefix .. '.up.x', crect.x + crect.w + area.uppos.x - x);
    end;
    
    if area.uppos.y == nil then
      theme.reset(prefix .. '.up.y');
    elseif area.uppos.ay < 0 then
      theme.set(prefix .. '.up.y', crect.y + area.uppos.y);
    else
      theme.set(prefix .. '.up.y', crect.y + crect.h + area.uppos.y - y);
    end;
  else
    theme.reset(prefix .. '.up.x');
    theme.reset(prefix .. '.up.y');
  end;
  
  if area.downpos then
    local x,y = gfx_sprite[prefix].down:size();
  
    if area.downpos.x == nil then
      theme.reset(prefix .. '.down.x');
    elseif area.downpos.ax < 0 then
      theme.set(prefix .. '.down.x', crect.x + area.downpos.x);
    else
      theme.set(prefix .. '.down.x', crect.x + crect.w + area.downpos.x - x);
    end;
    
    if area.downpos.y == nil then
      theme.reset(prefix .. '.down.y');
    elseif area.downpos.ay < 0 then
      theme.set(prefix .. '.down.y', crect.y + area.downpos.y);
    else
      theme.set(prefix .. '.down.y', crect.y + crect.h + area.downpos.y - y);
    end;
  else
    theme.reset(prefix .. '.down.x');
    theme.reset(prefix .. '.down.y');
  end;

  -- TODO Questionable
  local pad;
  if area.pad_scroll and theme.get(prefix .. '.up.x') == '-1' and theme.get(prefix .. '.up.y') == '-1' then
    pad = tonumber(theme.get('scr.gfx.pad'));

    local wscroll, _ = gfx_sprite[prefix].up:size();
    local wscroll2, _ = gfx_sprite[prefix].down:size();

    if wscroll2 > wscroll then
      wscroll = wscroll2;
    end;

    pad = pad + wscroll;

    if area.pad_scroll == 1 then
      crect.w = crect.w - pad
    elseif area.pad_scroll == 2 then
      crect.x = crect.x + pad
      crect.w = crect.w - 2 * pad
    end;
  end;

  theme.set(prefix .. '.x', crect.x);
  theme.set(prefix .. '.y', crect.y);
  theme.set(prefix .. '.w', crect.w);
  theme.set(prefix .. '.h', crect.h);
end;

local function apply_text(px, leftof, rightof, topof, bottomof)
  local area = area_options['text'];
  if area then
    local rect, crect = apply_area_options(area, leftof, rightof, topof, bottomof, px, 'win');
    
    apply_content_options(area, crect, px, 'win');
    apply_font(area.text, 'win');

    if area.align then
      theme.set('win.align', area.align);
    else
      theme.reset('win.align');
    end;

    if area.scrollmode then
      theme.set('win.scroll.mode', area.scrollmode);
    else
      theme.reset('win.scroll.mode');
    end;

    if area.waysmode then
      theme.set('win.ways.mode', area.waysmode);
    else
      theme.reset('win.ways.mode');
    end;

    if area.height then
      theme.set('win.fnt.height', area.height);
    else
      theme.reset('win.fnt.height');
    end;
    
    return rect;
  end;
end;

local function apply_inv(px, leftof, rightof, topof, bottomof)
  local area = area_options['inv'];
  if area then

    local rect, crect = apply_area_options(area, leftof, rightof, topof, bottomof, px, 'inv');
    apply_content_options(area, crect, px, 'inv');
    apply_font(area.text, 'inv');

    if area.mode then
      theme.set('inv.mode', area.mode);
    else
      theme.reset('inv.mode');
    end;
    
    if area.height then
      theme.set('inv.fnt.height', area.height);
    else
      theme.reset('inv.fnt.height');
    end;

    return rect;
  end;
end;

local function apply_image(px, leftof, rightof, topof, bottomof)
  local area = area_options['image'];
  if area then
    local rect, crect = apply_area_options(area, leftof, rightof, topof, bottomof, px, 'scr.gfx');
    if area.mode then
      theme.set('scr.gfx.mode', area.mode);
    else
      theme.reset('scr.gfx.mode');
    end;
    local mode = theme.get('scr.gfx.mode');
    if mode == 'embedded' or mode == 'fixed' then
      theme.set('scr.gfx.w', crect.w);
      theme.set('scr.gfx.h', crect.h);
    elseif mode == 'float' then
      theme.set('scr.gfx.x', crect.x);
      theme.set('scr.gfx.y', crect.y);
      theme.set('scr.gfx.w', crect.w);
      theme.set('scr.gfx.h', crect.h);
    end;
    return rect;
  end;
end;

local function apply_menu(px, leftof, rightof, topof, bottomof)
  local area = area_options.menu;
  if area then
    local rect = area.buttonrect;

    if rect then
      rect.leftof = leftof;
      rect.rightof = rightof;
      rect.topof = topof;
      rect.bottomof = bottomof;
      rect = direlayout.constraint_to_rect(scale_rect(rect, rect.noscale), default_canvas);
      theme.set('menu.button.x', rect.x);
      theme.set('menu.button.y', rect.y);

      if area.button then
        menu_button_sprite = map_to_sprite(area.button._map, area.button.options.scale);
        theme.set('menu.gfx.button', menu_button_sprite);
      else
        theme.reset('menu.gfx.button');
      end;
    else
      theme.reset('menu.button.x');
      theme.reset('menu.button.y');
      theme.reset('menu.gfx.button');
    end;

    if area.bg and area.bg.color then
      theme.set('menu.col.bg', area.bg.color);
    else
      theme.reset('menu.col.bg');
    end;

    if area.bg and area.bg.alpha then
      theme.set('menu.col.alpha', area.bg.alpha);
    else
      theme.reset('menu.col.alpha');
    end;

    if area.border and area.border.color then
      theme.set('menu.col.border', area.border.color);
    else
      theme.reset('menu.col.border');
    end;

    if area.border and area.border.width then
      theme.set('menu.bw', area.border.width);
    else
      theme.reset('menu.bw');
    end;

    if area.height then
      theme.set('menu.fnt.height', area.height);
    else
      theme.reset('menu.fnt.height');
    end;
    
    apply_font(area.text, 'menu');

    return rect;
  end;
end;

local function __draw_order(dep_tree, inuse, stack, key, value)
  if inuse[key] then
    error('Loop in components\' draw order');
  end;
  stack[#stack + 1] = key;
  inuse[key] = 1;

  for key2, value2 in pairs(value) do
    __draw_order(dep_tree, inuse, stack, value2, dep_tree[value2] or {});
  end;

  inuse[key] = nil;
  assert(stack[#stack] == key);
  stack[#stack] = nil;

  for i = 1, #draw_order do
    if draw_order[i] == key then return end;
  end;

  draw_order[#draw_order + 1] = key;
end;

local function calculate_draw_order()
  draw_order = {};
  local dep_tree = {};
  local inuse = {};
  local stack = {};

  for key, value in pairs(area_options) do
    dep_tree[key] = {}
    if value.topof then
      dep_tree[key][#dep_tree[key] + 1] = value.topof
    end;

    if value.leftof then
      dep_tree[key][#dep_tree[key] + 1] = value.leftof
    end;

    if value.rightof then
      dep_tree[key][#dep_tree[key] + 1] = value.rightof
    end;

    if value.bottomof then
      dep_tree[key][#dep_tree[key] + 1] = value.bottomof
    end;
  end;

  for key, value in pairs(dep_tree) do
    if not inuse[key] then
      __draw_order(dep_tree, inuse, stack, key, value);
    end;
  end;
end;

local function get_picture_map(image, options)
  local px = nil;
  local parts = nil;
  local istable = false;
  local w = nil;
  local h = nil;
  if type(image) == 'string' then
    px = pixels.new(image);
    w, h = px:size();
  elseif type(image) == 'userdata' then
    px = image;
    w, h = px:size();
  elseif type(image) == 'function' then
    return {
      p=image,
      o=options,
      w=1, -- TODO think of default size in this case
      h=1 -- TODO think of default size in this case
    }
  elseif image == nil then
    return nil;
  else
    error('Invalid image parameter');
  end;

  if options and options.mode == 6 then
    if not istable then
      px, options.parts = dire_slice_9(px, options.slice);
    end;
  end;

  return {
    p=px, 
    o=options,
    w=w,
    h=h
  }
end;

local area_functions = {
  text = apply_text;
  inv = apply_inv;
  image = apply_image;
  menu = apply_menu;
}

local function set_theme()
  local px = pixels.new(scr_width, scr_height);
  if background then
    draw(background, px, {x = 0, y = 0, w = scr_width_adjusted, h = scr_height_adjusted});
  else
    draw({color=theme.get('scr.col.bg')}, px, {x = 0, y = 0, w = scr_width_adjusted, h = scr_height_adjusted});
  end;

  local areas = {};

  for i = 1, #draw_order do
    local area = draw_order[i];
    areas[area] = area_functions[area](px, areas[area_options[area].leftof], areas[area_options[area].rightof], areas[area_options[area].topof], areas[area_options[area].bottomof]);
  end;

  bgsprite = px:tosprite();
  theme.set('scr.gfx.bg', bgsprite);

  local s = scale;
  if cursor_options.scalefactor then
    s = cursor_options.scalefactor;
  end;

  if cursor_options.x then
    if control_scaling and s ~= 1 and cursor_options.scale then
      theme.set('scr.gfx.cursor.x', math.floor(cursor_options.x * s));
    else
      theme.set('scr.gfx.cursor.x', cursor_options.x);
    end;
  else
    theme.reset('scr.gfx.cursor.x');
  end;

  if cursor_options.y then
    if control_scaling and s ~= 1 and cursor_options.scale then
      theme.set('scr.gfx.cursor.y', math.floor(cursor_options.y * s));
    else
      theme.set('scr.gfx.cursor.y', cursor_options.y);
    end;
  else
    theme.reset('scr.gfx.cursor.y');
  end;

  if cursor_options._normal_map then
    cursor_sprite = map_to_sprite(cursor_options._normal_map, cursor_options.scale, s);
  else
    cursor_sprite = map_to_sprite(default_cursor, cursor_options.scale, s);
  end;

  if cursor_options._use_map then
    cursor_use_sprite = map_to_sprite(cursor_options._use_map, cursor_options.scale, s);
  else
    cursor_use_sprite = map_to_sprite(default_cursor_use, cursor_options.scale, s);
  end;
  
  theme.set('scr.gfx.cursor.normal', cursor_sprite);
  theme.set('scr.gfx.cursor.use', cursor_use_sprite);
  
  theme_changed = false;
end;

std.mod_init(function()
  local scalemode = theme.get('scr.gfx.scalable');
  smooth = (scalemode % 2) == 1;

  scr_width = tonumber(theme.get('scr.w'));
  scr_height = tonumber(theme.get('scr.h'));
  
  
  local dpi = 96;
  if instead.screen_dpi then
    dpi = instead.screen_dpi();
    if not dpi or dpi == 0 then
      -- Just in case
      dpi = 96;
    end;
  end;
  
  local phys_width = scr_width / dpi;
  local phys_height = scr_height / dpi;
  
  scr_inches = math.sqrt(phys_width * phys_width + phys_height * phys_height);

  if default_canvas == nil then
    default_canvas = {
      x = 0,
      y = 0,
      w = scr_width,
      h = scr_height
    }
  end;

  if theme.get('scr.scale_aware') == '2' then
    control_scaling = true;
    scale = tonumber(theme.get('scr.gfx.scale'));
    scaled_width = math.ceil(scr_width / scale);
    scaled_height = math.ceil(scr_height / scale);
    scr_width_adjusted = math.ceil(scaled_width * scale);
    scr_height_adjusted = math.ceil(scaled_height * scale);
  else
    scaled_width = 1;
    scaled_height = 1;
    scr_width_adjusted = scr_width;
    scr_height_adjusted = scr_height;
  end;

  gfx.win.up = pixels_to_map(pixels.new(theme.get('win.gfx.up')));
  gfx.win.down = pixels_to_map(pixels.new(theme.get('win.gfx.down')));

  gfx_sprite.win.up = map_to_sprite(gfx.win.up);
  gfx_sprite.win.down = map_to_sprite(gfx.win.down);

  theme.set('win.gfx.up', gfx_sprite.win.up);
  theme.set('win.gfx.down', gfx_sprite.win.down);

  gfx.inv.up = pixels_to_map(pixels.new(theme.get('inv.gfx.up')));
  gfx.inv.down = pixels_to_map(pixels.new(theme.get('inv.gfx.down')));

  gfx_sprite.inv.up = map_to_sprite(gfx.inv.up);
  gfx_sprite.inv.down = map_to_sprite(gfx.inv.down);

  theme.set('inv.gfx.up', gfx_sprite.inv.up);
  theme.set('inv.gfx.down', gfx_sprite.inv.down);

  default_cursor = pixels_to_map(pixels.new(theme.get('scr.gfx.cursor.normal')));
  default_cursor_use = pixels_to_map(pixels.new(theme.get('scr.gfx.cursor.use')));
  set_theme();
end);

std.mod_step(function(state)
  if theme_changed then
    set_theme();
  end;
end);

diretheme = obj {
  nam = '@diretheme'
}

diretheme.set_theme = set_theme;

diretheme.get_canvas = function()
  return default_canvas;
end;

diretheme.set_canvas = function(canvas)
  theme_changed = true;
  local width = tonumber(theme.get('scr.w'));
  local height = tonumber(theme.get('scr.h'));
  if canvas then
    default_canvas = direlayout.constraint_to_rect(canvas, {x=0, y=0, w=width, h=height});
  else
    default_canvas = {x=0, y=0, w=width, h=height};
  end;
  return default_canvas
end;

diretheme.set_background = function(color, alpha, image, options)
  theme_changed = true;
  if color or image then
    background = {
      color=color,
      alpha=alpha,
      image=image,
      options=options
    };
    if image and type(image) ~= 'function' then
      if not options then
        options = {};
      end;

      background._map = get_picture_map(image, options);
    end;
  else
    background = nil;
  end;
end;

diretheme.set_text_area = function(options, reset)
  theme_changed = true;

  if options == nil then
    options = {}
  end;

  if options.bg and options.bg.image and type(options.bg.image) ~= 'function' then
    options.bg._map = get_picture_map(options.bg.image, options.bg.options);
  end;

  if reset then
    area_options['text'] = options;
  else
    for k,v in pairs(options) do area_options['text'][k] = v end
    options = area_options['text']
  end;

  options._up_map = get_picture_map(options.up);
  options._down_map = get_picture_map(options.down);

  calculate_draw_order();
end;

diretheme.set_image_area = function(options, reset)
  theme_changed = true;

  if options == nil then
    options = {}
  end;

  if options.bg and options.bg.image and type(options.bg.image) ~= 'function' then
    options.bg._map = get_picture_map(options.bg.image, options.bg.options);
  end;

  if reset then
    area_options['image'] = options;
  else
    for k,v in pairs(options) do area_options['image'][k] = v end
  end;

  calculate_draw_order();
end;

diretheme.set_inventory_area = function(options, reset)
  theme_changed = true;

  if options == nil then
    options = {}
  end;

  if reset then
    area_options['inv'] = options;
  else
    for k,v in pairs(options) do area_options['inv'][k] = v end

    options = area_options['inv'];
  end;

  if options.bg and options.bg.image and type(options.bg.image) ~= 'function' then
    options.bg._map = get_picture_map(options.bg.image, options.bg.options);
  end;

  options._up_map = get_picture_map(options.up);
  options._down_map = get_picture_map(options.down);

  calculate_draw_order();
end;

diretheme.set_menu = function(options, reset)
  theme_changed = true;

  if options == nil then
    options = {}
  end;

  if options.button and options.button.image then
    options.button._map = get_picture_map(options.button.image, options.button.options);
  end;

  if reset then
    area_options['menu'] = options;
  else
    for k,v in pairs(options) do area_options['menu'][k] = v end
  end;

  calculate_draw_order();
end;

diretheme.set_cursor = function(options, reset)
  reset = reset or not options
  theme_changed = true

  if not options then
    options = {}
  end;

  if options.normal then
    options._normal_map = get_picture_map(options.normal)
  end;

  if options.use then
    options._use_map = get_picture_map(options.use)
  end;

  if reset then
    cursor_options = options
  else
    for k,v in pairs(options) do cursor_options[k] = v end
  end
end

diretheme.set_scale = function(s)
  if control_scaling then
    theme_changed = true
    if not s then
      theme.reset('scr.gfx.scale')
      scale = tonumber(theme.get('scr.gfx.scale'))
    else
      scale = s
      theme.set('scr.gfx.scale', s)
    end;
    scaled_width = math.ceil(scr_width / scale);
    scaled_height = math.ceil(scr_height / scale);
    scr_width_adjusted = math.ceil(scaled_width * scale);
    scr_height_adjusted = math.ceil(scaled_height * scale);
  end
end

diretheme.get_scale = function(s)
  if control_scaling then
    return scale;
  else
    return tonumber(theme.get('scr.gfx.scale'));
  end;
end;

diretheme.get_smooth = function(s)
  return smooth;
end;

diretheme.get_screen = function(s)
  return scr_width, scr_height
end;

diretheme.get_inches = function(s)
  return scr_inches;
end

diretheme.get_scaled_screen = function(s)
  return scaled_width, scaled_height
end;

