; ════════════════════════════════════════════════════════════════════════════
; KUMOS.sbi — SpiderBasic SDK for KUMOS third-party apps
;
; Usage:
;   IncludeFile "KUMOS.sbi"
;   KUMOS::Init()          ; call once at startup, before anything else
;
; All async procedures accept a *Callback procedure pointer.
; Callbacks always have the signature:  Procedure Cb(Success.i, Data.s)
; Exception: KUMOS::Notify_Confirm uses:  Procedure Cb(Result.i)
;
; The "storage.*" namespace is always available.
; "fs.*"     requires the "fs.read" / "fs.write" permission in manifest.json.
; "notify.*" requires the "notify" permission in manifest.json.
; ════════════════════════════════════════════════════════════════════════════

DeclareModule KUMOS
  ; Toast types — pass to Notify_Toast()
  Enumeration
    #Info
    #Success
    #Warning
    #Error
  EndEnumeration

  ; ── Lifecycle ──────────────────────────────────────────────────────────
  Declare Init()
  Declare Ready(*Callback)           ; Cb(Success, Data) — Data: '{"app_id":"..."}'
  Declare Window_SetTitle(Title.s)
  Declare Window_Close()

  ; ── Private storage (always available) ────────────────────────────────
  Declare Storage_Read  (Path.s, *Callback)           ; Cb(Success, Content.s)
  Declare Storage_Write (Path.s, Content.s, *Callback); Cb(Success, "")
  Declare Storage_List  (Path.s, *Callback)           ; Cb(Success, JSON array)
  Declare Storage_Stat  (Path.s, *Callback)           ; Cb(Success, JSON object)
  Declare Storage_Mkdir (Path.s, *Callback)           ; Cb(Success, "")
  Declare Storage_Delete(Path.s, *Callback)           ; Cb(Success, "")

  ; ── User filesystem (requires fs.read / fs.write) ─────────────────────
  Declare FS_Read  (Path.s, *Callback)                ; Cb(Success, Content.s)
  Declare FS_Write (Path.s, Content.s, *Callback)     ; Cb(Success, "")
  Declare FS_List  (Path.s, *Callback)                ; Cb(Success, JSON array)
  Declare FS_Stat  (Path.s, *Callback)                ; Cb(Success, JSON object)

  ; ── Notifications (requires notify) ───────────────────────────────────
  Declare Notify_Toast   (Message.s, Type.i = #Info)  ; fire-and-forget
  Declare Notify_Confirm (Title_.s, Message.s, *Callback) ; Cb(Result.i)
EndDeclareModule

Module KUMOS
  EnableExplicit

  ; Inject the JS runtime once, guarded so multiple IncludeFile calls are safe.
  Procedure Init()
    !if (window._kumos_sb) { return; }
    !window._kumos_sb = {
    !  pending: {},
    !  uuid: function() {
    !    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    !      var r = Math.random() * 16 | 0;
    !      return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    !    });
    !  },
    !  send: function(action, args, onSuccess, onError) {
    !    var kmid  = window._kumos_sb.uuid();
    !    var timer = setTimeout(function() {
    !      var p = window._kumos_sb.pending[kmid];
    !      if (p) { delete window._kumos_sb.pending[kmid]; p.onError('Timeout: ' + action); }
    !    }, 15000);
    !    window._kumos_sb.pending[kmid] = { onSuccess: onSuccess, onError: onError, timer: timer };
    !    window.parent.postMessage(
    !      JSON.stringify({ kmid: kmid, action: action, args: args || {} }), '*'
    !    );
    !  }
    !};
    !window.addEventListener('message', function(e) {
    !  if (e.source !== window.parent) { return; }
    !  var msg;
    !  try { msg = JSON.parse(e.data); } catch(ex) { return; }
    !  if (!msg || !msg.kmid) { return; }
    !  var p = window._kumos_sb.pending[msg.kmid];
    !  if (!p) { return; }
    !  clearTimeout(p.timer);
    !  delete window._kumos_sb.pending[msg.kmid];
    !  if (msg.success) {
    !    var data = msg.data;
    !    if      (data === null || data === undefined) { data = ''; }
    !    else if (typeof data === 'object')            { data = JSON.stringify(data); }
    !    else if (typeof data === 'boolean')           { data = data ? 'true' : 'false'; }
    !    else                                          { data = String(data); }
    !    p.onSuccess(data);
    !  } else {
    !    p.onError(msg.error || 'Error');
    !  }
    !});
  EndProcedure

  ; ── Internal send helper ──────────────────────────────────────────────────
  ; *Callback must be Procedure Cb(Success.i, Data.s)
  ; *ConfirmCallback must be Procedure Cb(Result.i)  [used only for confirm]

  Procedure _Send(Action.s, ArgsJSON.s, *Callback)
    ! var cb = p_callback;
    !window._kumos_sb.send(
    !  v_action,
    !  (v_argsjson ? JSON.parse(v_argsjson) : {}),
    !  function(data) { cb(1, data); },
    !  function(err)  { cb(0, err);  }
    !);
  EndProcedure

  ; ── Args builders ─────────────────────────────────────────────────────────

  Procedure.s _PathArgs(Path.s)
    ProcedureReturn ~"{\"path\":\"" + ReplaceString(Path, ~"\"", ~"\\\"") + ~"\"}"
  EndProcedure

  Procedure.s _PathContentArgs(Path.s, Content.s)
    ProcedureReturn ~"{\"path\":\""    + ReplaceString(Path,    ~"\"", ~"\\\"") + ~"\"," +
                   ~"\"content\":\"" + ReplaceString(Content, ~"\"", ~"\\\"") + ~"\"}"
  EndProcedure

  Procedure.s _TitleMsgArgs(Title_.s, Message.s)
    ProcedureReturn ~"{\"title\":\""   + ReplaceString(Title_,  ~"\"", ~"\\\"") + ~"\"," +
                   ~"\"message\":\"" + ReplaceString(Message, ~"\"", ~"\\\"") + ~"\"}"
  EndProcedure

  Procedure.s _TypeStr(Type.i)
    Select Type
      Case #Success : ProcedureReturn "success"
      Case #Warning : ProcedureReturn "warning"
      Case #Error   : ProcedureReturn "error"
      Default       : ProcedureReturn "info"
    EndSelect
  EndProcedure

  ; ── Lifecycle ─────────────────────────────────────────────────────────────

  Procedure Ready(*Callback)
    _Send("window.ready", "{}", *Callback)
  EndProcedure

  Procedure Window_SetTitle(Title.s)
    ; Fire-and-forget — no callback needed
    ! window._kumos_sb.send('window.setTitle',
    !   { title: v_title }, function(){}, function(){});
  EndProcedure

  Procedure Window_Close()
    ! window._kumos_sb.send('window.close', {}, function(){}, function(){});
  EndProcedure

  ; ── Storage ───────────────────────────────────────────────────────────────

  Procedure Storage_Read(Path.s, *Callback)
    _Send("storage.read", _PathArgs(Path), *Callback)
  EndProcedure

  Procedure Storage_Write(Path.s, Content.s, *Callback)
    _Send("storage.write", _PathContentArgs(Path, Content), *Callback)
  EndProcedure

  Procedure Storage_List(Path.s, *Callback)
    _Send("storage.list", _PathArgs(Path), *Callback)
  EndProcedure

  Procedure Storage_Stat(Path.s, *Callback)
    _Send("storage.stat", _PathArgs(Path), *Callback)
  EndProcedure

  Procedure Storage_Mkdir(Path.s, *Callback)
    _Send("storage.mkdir", _PathArgs(Path), *Callback)
  EndProcedure

  Procedure Storage_Delete(Path.s, *Callback)
    _Send("storage.delete", _PathArgs(Path), *Callback)
  EndProcedure

  ; ── User filesystem ───────────────────────────────────────────────────────

  Procedure FS_Read(Path.s, *Callback)
    _Send("fs.read", _PathArgs(Path), *Callback)
  EndProcedure

  Procedure FS_Write(Path.s, Content.s, *Callback)
    _Send("fs.write", _PathContentArgs(Path, Content), *Callback)
  EndProcedure

  Procedure FS_List(Path.s, *Callback)
    _Send("fs.list", _PathArgs(Path), *Callback)
  EndProcedure

  Procedure FS_Stat(Path.s, *Callback)
    _Send("fs.stat", _PathArgs(Path), *Callback)
  EndProcedure

  ; ── Notifications ─────────────────────────────────────────────────────────

  Procedure Notify_Toast(Message.s, Type.i = #Info)
    Protected Args.s = ~"{\"message\":\"" + ReplaceString(Message, ~"\"", ~"\\\"") + ~"\"," +
                       ~"\"type\":\""     + _TypeStr(Type) + ~"\"}"
    ! window._kumos_sb.send('notify.toast',
    !   JSON.parse(v_args), function(){}, function(){});
  EndProcedure

  ; *Callback must be Procedure Cb(Result.i)  — 1 = OK, 0 = Cancel
  Procedure Notify_Confirm(Title_.s, Message.s, *Callback)
    Protected Args.s = _TitleMsgArgs(Title_, Message)
    ! var cb = p_callback;
    !window._kumos_sb.send(
    !  'notify.confirm',
    !  JSON.parse(v_args),
    !  function(data) { cb(data === 'true' ? 1 : 0); },
    !  function()     { cb(0); }
    !);
  EndProcedure

EndModule

; IDE Options = SpiderBasic 3.20 (Windows - x86)
; CursorPosition = 1
; Folding = ----
; iOSAppOrientation = 0
; AndroidAppCode = 0
; AndroidAppOrientation = 0
; EnableXP
; DPIAware
; CompileSourceDirectory