var video = use('sdl_video');

log.console("STATED VIDE")

// SDL Video Actor
// This actor runs on the main thread and handles all SDL video operations
var surface = use('surface');
var input = use('input')

var ren
var win

var default_window = {
  // Basic properties
  title: "Prosperon Window",
  width: 640,
  height: 480,
  
  // Position - can be numbers or "centered"
  x: null,  // SDL_WINDOWPOS_null by default
  y: null,  // SDL_WINDOWPOS_null by default
  
  // Window behavior flags
  resizable: true,
  fullscreen: false,
  hidden: false,
  borderless: false,
  alwaysOnTop: false,
  minimized: false,
  maximized: false,
  
  // Input grabbing
  mouseGrabbed: false,
  keyboardGrabbed: false,
  
  // Display properties
  highPixelDensity: false,
  transparent: false,
  opacity: 1.0,  // 0.0 to 1.0
  
  // Focus behavior
  notFocusable: false,
  
  // Special window types (mutually exclusive)
  utility: false,      // Utility window (not in taskbar)
  tooltip: false,      // Tooltip window (requires parent)
  popupMenu: false,    // Popup menu window (requires parent)
  
  // Graphics API flags (let SDL choose if not specified)
  opengl: false,       // Force OpenGL context
  vulkan: false,       // Force Vulkan context
  metal: false,        // Force Metal context (macOS)
  
  // Advanced properties
  parent: null,   // Parent window for tooltips/popups/modal
  modal: false,        // Modal to parent window (requires parent)
  externalGraphicsContext: false,  // Use external graphics context
  
  // Input handling
  textInput: true,     // Enable text input on creation
};

var config = Object.assign({}, default_window, arg[0] || {});
win = new video.window(config);

// Resource tracking
var resources = {
  texture: {},
  surface: {},
  cursor: {}
};

// ID counter for resource allocation
var next_id = 1;

// Helper to allocate new ID
function allocate_id() {
  return next_id++;
}

// Message handler
$_.receiver(function(msg) {
  if (!msg.kind || !msg.op) {
    send(msg, {error: "Message must have 'kind' and 'op' fields"});
    return;
  }
  
  var response = {};
  
  try {
    switch (msg.kind) {
      case 'window':
        response = handle_window(msg);
        break;
      case 'renderer':
        response = handle_renderer(msg);
        break;
      case 'texture':
        response = handle_texture(msg);
        break;
      case 'surface':
        response = handle_surface(msg);
        break;
      case 'cursor':
        response = handle_cursor(msg);
        break;
      case 'mouse':
        response = handle_mouse(msg);
        break;
      case 'keyboard':
        response = handle_keyboard(msg);
        break;
      case 'input':
        response = input.get_events();
        break;
      default:
        response = {error: "Unknown kind: " + msg.kind};
    }
  } catch (e) {
    log.console(typeof e)
    log.console(typeof null)
    response = {error: e};
    log.error(e)
  }
  
  send(msg, response);
});

// Window operations
function handle_window(msg) {
  switch (msg.op) {
    case 'destroy':
      win.destroy();
      win = null
      return {success: true};
      
    case 'show':
      win.visible = true;
      return {success: true};
      
    case 'hide':
      win.visible = false;
      return {success: true};
      
    case 'get':
      var prop = msg.data ? msg.data.property : null;
      if (!prop) return {error: "Missing property name"};
      
      // Handle special cases
      if (prop == 'surface') {
        var surf = win.surface;
        if (!surf) return {data: null};
        var surf_id = allocate_id();
        resources.surface[surf_id] = surf;
        return {data: surf_id};
      }
      
      return {data: win[prop]};
      
    case 'set':
      var prop = msg.data ? msg.data.property : null;
      var value = msg.data ? msg.data.value : null;
      if (!prop) return {error: "Missing property name"};
      
      // Validate property is settable
      var readonly = ['id', 'pixelDensity', 'displayScale', 'sizeInPixels', 'flags', 'surface'];
      if (readonly.indexOf(prop) != -1) {
        return {error: "Property '" + prop + "' is read-only"};
      }
      
      win[prop] = value;
      return {success: true};
      
    case 'fullscreen':
      win.fullscreen();
      return {success: true};
      
    case 'updateSurface':
      win.updateSurface();
      return {success: true};
      
    case 'updateSurfaceRects':
      if (!msg.data || !msg.data.rects) return {error: "Missing rects array"};
      win.updateSurfaceRects(msg.data.rects);
      return {success: true};
      
    case 'raise':
      win.raise();
      return {success: true};
      
    case 'restore':
      win.restore();
      return {success: true};
      
    case 'flash':
      win.flash(msg.data ? msg.data.operation : 'briefly');
      return {success: true};
      
    case 'sync':
      win.sync();
      return {success: true};
      
    case 'setIcon':
      if (!msg.data || !msg.data.surface_id) return {error: "Missing surface_id"};
      var surf = resources.surface[msg.data.surface_id];
      if (!surf) return {error: "Invalid surface id"};
      win.set_icon(surf);
      return {success: true};
      
    case 'makeRenderer':
      log.console("MAKE RENDERER")
      if (ren)
        return {reason: "Already made a renderer"}
      ren = win.make_renderer()
      return {success:true};
      
    default:
      return {error: "Unknown window operation: " + msg.op};
  }
}

// Renderer operation functions
var renderfuncs = {
  destroy: function(msg) {
    ren = null
    return {success: true};
  },
  
  clear: function(msg) {
    ren.clear();
    return {success: true};
  },
  
  present: function(msg) {
    ren.present();
    return {success: true};
  },
  
  flush: function(msg) {
    ren.flush();
    return {success: true};
  },
  
  get: function(msg) {
    var prop = msg.data ? msg.data.property : null;
    if (!prop) return {error: "Missing property name"};
    
    // Handle special getters that might return objects
    if (prop == 'drawColor') {
      var color = ren[prop];
      if (color && typeof color == 'object') {
        // Convert color object to array format [r,g,b,a]
        return {data: [color.r || 0, color.g || 0, color.b || 0, color.a || 255]};
      }
    }
    
    return {data: ren[prop]};
  },
  
  set: function(msg) {
    var prop = msg.prop
    var value = msg.value
    if (!prop) return {error: "Missing property name"};
    
    if (!value) return {error: "No value to set"}
    
    // Validate property is settable
    var readonly = ['window', 'name', 'outputSize', 'currentOutputSize', 'logicalPresentationRect', 'safeArea'];
    if (readonly.indexOf(prop) != -1) {
      return {error: "Property '" + prop + "' is read-only"};
    }
    
    // Special handling for render target
    if (prop == 'target' && value != null && value != null) {
      var tex = resources.texture[value];
      if (!tex) return {error: "Invalid texture id"};
      value = tex;
    }

    ren[prop] = value;
    return {success: true};
  },
  
  line: function(msg) {
    if (!msg.data || !msg.data.points) return {error: "Missing points array"};
    ren.line(msg.data.points);
    return {success: true};
  },
  
  point: function(msg) {
    if (!msg.data || !msg.data.points) return {error: "Missing points"};
    ren.point(msg.data.points);
    return {success: true};
  },
  
  rect: function(msg) {
    if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
    ren.rect(msg.data.rect);
    return {success: true};
  },
  
  fillRect: function(msg) {
    if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
    ren.fillRect(msg.data.rect);
    return {success: true};
  },
  
  rects: function(msg) {
    if (!msg.data || !msg.data.rects) return {error: "Missing rects"};
    ren.rects(msg.data.rects);
    return {success: true};
  },
  
  lineTo: function(msg) {
    if (!msg.data || !msg.data.a || !msg.data.b) return {error: "Missing points a and b"};
    ren.lineTo(msg.data.a, msg.data.b);
    return {success: true};
  },
  
  texture: function(msg) {
    if (!msg.data) return {error: "Missing texture data"};
    var tex_id = msg.data.texture_id;
    if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
    ren.texture(
      resources.texture[tex_id],
      msg.data.src,
      msg.data.dst,
      msg.data.angle || 0,
      msg.data.anchor || {x:0.5, y:0.5}
    );
    return {success: true};
  },
  
  copyTexture: function(msg) {
    if (!msg.data) return {error: "Missing texture data"};
    var tex_id = msg.data.texture_id;
    if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
    var tex = resources.texture[tex_id];
    
    // Use the texture method with normalized coordinates
    ren.texture(
      tex,
      msg.data.src || {x:0, y:0, width:tex.width, height:tex.height},
      msg.data.dest || {x:0, y:0, width:tex.width, height:tex.height},
      0,  // No rotation
      {x:0, y:0}  // Top-left anchor
    );
    return {success: true};
  },
  
  sprite: function(msg) {
    if (!msg.data || !msg.data.sprite) return {error: "Missing sprite data"};
    ren.sprite(msg.data.sprite);
    return {success: true};
  },
  
  geometry: function(msg) {
    if (!msg.data) return {error: "Missing geometry data"};
    var tex_id = msg.data.texture_id;
    var tex = tex_id ? resources.texture[tex_id] : null;
    ren.geometry(tex, msg.data.geometry);
    return {success: true};
  },
  
  geometry_raw: function geometry_raw(msg) {
    var geom = msg.data
    ren.geometry_raw(resources.texture[geom.texture_id], geom.xy, geom.xy_stride, geom.color, geom.color_stride, geom.uv, geom.uv_stride, geom.num_vertices, geom.indices, geom.num_indices, geom.size_indices);
  },
  
  debugText: function(msg) {
    if (!msg.data || !msg.data.text) return {error: "Missing text"};
    ren.debugText([msg.data.pos.x, msg.data.pos.y], msg.data.text);
    return {success: true};
  },
  
  clipEnabled: function(msg) {
    return {data: ren.clipEnabled()};
  },
  
  texture9Grid: function(msg) {
    if (!msg.data) return {error: "Missing data"};
    var tex_id = msg.data.texture_id;
    if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
    ren.texture9Grid(
      resources.texture[tex_id],
      msg.data.src,
      msg.data.leftWidth,
      msg.data.rightWidth,
      msg.data.topHeight,
      msg.data.bottomHeight,
      msg.data.scale,
      msg.data.dst
    );
    return {success: true};
  },
  
  textureTiled: function(msg) {
    if (!msg.data) return {error: "Missing data"};
    var tex_id = msg.data.texture_id;
    if (!tex_id || !resources.texture[tex_id]) return {error: "Invalid texture id"};
    ren.textureTiled(
      resources.texture[tex_id],
      msg.data.src,
      msg.data.scale || 1.0,
      msg.data.dst
    );
    return {success: true};
  },
  
  readPixels: function(msg) {
    var surf = ren.readPixels(msg.data ? msg.data.rect : null);
    if (!surf) return {error: "Failed to read pixels"};
    var surf_id = allocate_id();
    resources.surface[surf_id] = surf;
    return {id: surf_id};
  },
  
  loadTexture: function(msg) {
    if (!msg.data) throw new Error("Missing data")
    
    var tex;
    // Direct surface data
    var surf = new surface(msg.data)
    
    if (!surf)
      throw new Error("Must provide surface_id or surface data")
      
    tex = ren.load_texture(surf);
    
    if (!tex) throw new Error("Failed to load texture")
    var tex_id = allocate_id();
    resources.texture[tex_id] = tex;
    return {
      id: tex_id,
    };
  },
  
  coordsFromWindow: function(msg) {
    if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
    return {data: ren.coordsFromWindow(msg.data.pos)};
  },
  
  coordsToWindow: function(msg) {
    if (!msg.data || !msg.data.pos) return {error: "Missing pos"};
    return {data: ren.coordsToWindow(msg.data.pos)};
  },
  
  batch: function(msg) {
    if (!msg.data || !Array.isArray(msg.data)) return {error: "Missing or invalid data array"};
    
    for (var i = 0; i < msg.data.length; i++)
      handle_renderer(msg.data[i]);
    
    return {success:true};
  }
};

// Renderer operations
function handle_renderer(msg) {
  if (!ren) return{reason:'no renderer!'}

  var func = renderfuncs[msg.op];
  if (func) {
    return func(msg);
  } else {
    return {error: "Unknown renderer operation: " + msg.op};
  }
}

// Texture operations
function handle_texture(msg) {
  // Special case: create needs a renderer
  if (msg.op == 'create') {
    if (!msg.data) return {error: "Missing texture data"};
    var ren_id = msg.data.renderer_id;
    if (!ren_id || !resources.renderer[ren_id]) return {error: "Invalid renderer id"};
    
    var tex;
    var renderer = resources.renderer[ren_id];
    
    // Create from surface
    if (msg.data.surface_id) {
      var surf = resources.surface[msg.data.surface_id];
      if (!surf) return {error: "Invalid surface id"};
      tex = new video.texture(renderer, surf);
    }
    // Create from properties
    else if (msg.data.width && msg.data.height) {
      tex = new video.texture(renderer, {
        width: msg.data.width,
        height: msg.data.height,
        format: msg.data.format || 'rgba8888',
        pixels: msg.data.pixels,
        pitch: msg.data.pitch
      });
    }
    else {
      log.console(json.encode(msg.data))
      return {error: "Must provide either surface_id or width/height"};
    }
    
    var tex_id = allocate_id();
    resources.texture[tex_id] = tex;
    return {id: tex_id, data: {size: tex.size}};
  }
  
  // All other operations require a valid texture ID
  if (!msg.id || !resources.texture[msg.id]) {
    return {error: "Invalid texture id: " + msg.id};
  }
  
  var tex = resources.texture[msg.id];
  
  switch (msg.op) {
    case 'destroy':
      delete resources.texture[msg.id];
      // Texture is automatically destroyed when all references are gone
      return {success: true};
      
    case 'get':
      var prop = msg.data ? msg.data.property : null;
      if (!prop) return {error: "Missing property name"};
      return {data: tex[prop]};
      
    case 'set':
      var prop = msg.data ? msg.data.property : null;
      var value = msg.data ? msg.data.value : null;
      if (!prop) return {error: "Missing property name"};
      
      // Validate property is settable
      var readonly = ['size', 'width', 'height'];
      if (readonly.indexOf(prop) != -1) {
        return {error: "Property '" + prop + "' is read-only"};
      }
      
      tex[prop] = value;
      return {success: true};
      
    case 'update':
      if (!msg.data) return {error: "Missing update data"};
      tex.update(
        msg.data.rect || null,
        msg.data.pixels,
        msg.data.pitch || 0
      );
      return {success: true};
      
    case 'lock':
      var result = tex.lock(msg.data ? msg.data.rect : null);
      return {data: result};
      
    case 'unlock':
      tex.unlock();
      return {success: true};
      
    case 'query':
      return {data: tex.query()};
      
    default:
      return {error: "Unknown texture operation: " + msg.op};
  }
}

// Surface operations (mainly for cleanup)
function handle_surface(msg) {
  switch (msg.op) {
    case 'destroy':
      if (!msg.id || !resources.surface[msg.id]) {
        return {error: "Invalid surface id: " + msg.id};
      }
      delete resources.surface[msg.id];
      return {success: true};
      
    default:
      return {error: "Unknown surface operation: " + msg.op};
  }
}

// Cursor operations
function handle_cursor(msg) {
  switch (msg.op) {
    case 'create':
      var surf = new surface(msg.data)
      
      var hotspot = msg.data.hotspot || [0, 0];
      var cursor = video.createCursor(surf, hotspot);
      
      var cursor_id = allocate_id();
      resources.cursor[cursor_id] = cursor;
      return {id: cursor_id};
      
    case 'set':
      var cursor = null;
      if (msg.id && resources.cursor[msg.id]) {
        cursor = resources.cursor[msg.id];
      }
      video.setCursor(cursor);
      return {success: true};
      
    case 'destroy':
      if (!msg.id || !resources.cursor[msg.id]) {
        return {error: "Invalid cursor id: " + msg.id};
      }
      delete resources.cursor[msg.id];
      return {success: true};
      
    default:
      return {error: "Unknown cursor operation: " + msg.op};
  }
}

// Utility function to create window and renderer
prosperon.endowments = prosperon.endowments || {};

// Mouse operations
function handle_mouse(msg) {
  var mouse = video.mouse;
  
  switch (msg.op) {
    case 'show':
      if (msg.data == null) return {error: "Missing show parameter"};
      mouse.show(msg.data);
      return {success: true};
      
    case 'capture':
      if (msg.data == null) return {error: "Missing capture parameter"};
      mouse.capture(msg.data);
      return {success: true};
      
    case 'get_state':
      return {data: mouse.get_state()};
      
    case 'get_global_state':
      return {data: mouse.get_global_state()};
      
    case 'get_relative_state':
      return {data: mouse.get_relative_state()};
      
    case 'warp_global':
      if (!msg.data) return {error: "Missing position"};
      mouse.warp_global(msg.data);
      return {success: true};
      
    case 'warp_in_window':
      if (!msg.data || !msg.data.window_id || !msg.data.pos) 
        return {error: "Missing window_id or position"};
      var window = resources.window[msg.data.window_id];
      if (!window) return {error: "Invalid window id"};
      mouse.warp_in_window(window, msg.data.pos);
      return {success: true};
      
    case 'cursor_visible':
      return {data: mouse.cursor_visible()};
      
    case 'get_cursor':
      var cursor = mouse.get_cursor();
      if (!cursor) return {data: null};
      // Find or create cursor ID
      for (var id in resources.cursor) {
        if (resources.cursor[id] == cursor) {
          return {data: id};
        }
      }
      // Not tracked, add it
      var cursor_id = allocate_id();
      resources.cursor[cursor_id] = cursor;
      return {data: cursor_id};
      
    case 'get_default_cursor':
      var cursor = mouse.get_default_cursor();
      if (!cursor) return {data: null};
      // Find or create cursor ID
      for (var id in resources.cursor) {
        if (resources.cursor[id] == cursor) {
          return {data: id};
        }
      }
      // Not tracked, add it
      var cursor_id = allocate_id();
      resources.cursor[cursor_id] = cursor;
      return {data: cursor_id};
      
    case 'create_system_cursor':
      if (msg.data == null) return {error: "Missing cursor type"};
      var cursor = mouse.create_system_cursor(msg.data);
      var cursor_id = allocate_id();
      resources.cursor[cursor_id] = cursor;
      return {id: cursor_id};
      
    case 'get_focus':
      var window = mouse.get_focus();
      if (!window) return {data: null};
      // Find window ID
      for (var id in resources.window) {
        if (resources.window[id] == window) {
          return {data: id};
        }
      }
      // Not tracked, add it
      var win_id = allocate_id();
      resources.window[win_id] = window;
      return {data: win_id};
      
    default:
      return {error: "Unknown mouse operation: " + msg.op};
  }
}

// Keyboard operations
function handle_keyboard(msg) {
  var keyboard = video.keyboard;
  
  switch (msg.op) {
    case 'get_state':
      return {data: keyboard.get_state()};
      
    case 'get_focus':
      var window = keyboard.get_focus();
      if (!window) return {data: null};
      // Find window ID
      for (var id in resources.window) {
        if (resources.window[id] == window) {
          return {data: id};
        }
      }
      // Not tracked, add it
      var win_id = allocate_id();
      resources.window[win_id] = window;
      return {data: win_id};
      
    case 'start_text_input':
      var window = null;
      if (msg.data && msg.data.window_id) {
        window = resources.window[msg.data.window_id];
        if (!window) return {error: "Invalid window id"};
      }
      keyboard.start_text_input(window);
      return {success: true};
      
    case 'stop_text_input':
      var window = null;
      if (msg.data && msg.data.window_id) {
        window = resources.window[msg.data.window_id];
        if (!window) return {error: "Invalid window id"};
      }
      keyboard.stop_text_input(window);
      return {success: true};
      
    case 'text_input_active':
      var window = null;
      if (msg.data && msg.data.window_id) {
        window = resources.window[msg.data.window_id];
        if (!window) return {error: "Invalid window id"};
      }
      return {data: keyboard.text_input_active(window)};
      
    case 'get_text_input_area':
      var window = null;
      if (msg.data && msg.data.window_id) {
        window = resources.window[msg.data.window_id];
        if (!window) return {error: "Invalid window id"};
      }
      return {data: keyboard.get_text_input_area(window)};
      
    case 'set_text_input_area':
      if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
      var window = null;
      if (msg.data.window_id) {
        window = resources.window[msg.data.window_id];
        if (!window) return {error: "Invalid window id"};
      }
      keyboard.set_text_input_area(msg.data.rect, msg.data.cursor || 0, window);
      return {success: true};
      
    case 'clear_composition':
      var window = null;
      if (msg.data && msg.data.window_id) {
        window = resources.window[msg.data.window_id];
        if (!window) return {error: "Invalid window id"};
      }
      keyboard.clear_composition(window);
      return {success: true};
      
    case 'screen_keyboard_shown':
      if (!msg.data || !msg.data.window_id) return {error: "Missing window_id"};
      var window = resources.window[msg.data.window_id];
      if (!window) return {error: "Invalid window id"};
      return {data: keyboard.screen_keyboard_shown(window)};
      
    case 'reset':
      keyboard.reset();
      return {success: true};
      
    default:
      return {error: "Unknown keyboard operation: " + msg.op};
  }
}
 