## Copyright (C) 2012-2026 Philip Nienhuis
## 
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
## 
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
## 
## You should have received a copy of the GNU General Public License
## along with Octave; see the file COPYING.  If not, see
## <http://www.gnu.org/licenses/>.

## __UNO_spsh_close__ - internal function: close a spreadsheet file using UNO

## Author: Philip Nienhuis <prnienhuis@users.sf.net>
## Created: 2012-10-12

function [ xls ] = __UNO_spsh_close__ (xls, force)

  if (isfield (xls, "app") && isfield (xls.app, "uno_launcher"))
    uno_launcher = xls.app.uno_launcher;
  else
    uno_launcher = "bootstrap";
  endif
  terminate_uno = strcmp (uno_launcher, "bootstrap");

  if (isfield (xls, "nfilename"))
    ## UNO needs a URL as target for storeAsURL().
    filename = uno_filename_to_url (xls.nfilename);
  else
    filename = xls.filename;
  endif

  try
    if (xls.changed > 0 && xls.changed < 3)
      do_store = (xls.changed == 2);
      if (! do_store)
        ## Workaround:
        unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.frame.XModel");
        xModel = xls.workbook.queryInterface (unotmp);
        unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.util.XModifiable");
        xModified = xModel.queryInterface (unotmp);
        do_store = xModified.isModified ();
      endif
      if (do_store)
        unotmp = ...
          javaObject ("com.sun.star.uno.Type", "com.sun.star.frame.XStorable");  # isReadonly() ?    
        xStore = xls.app.xComp.queryInterface (unotmp);
        if (xls.changed == 2)
          uno_remove_orphan_lock_file (filename);
          ## Some trickery as Octave Java cannot create non-numeric arrays
          lProps = javaArray ("com.sun.star.beans.PropertyValue", 2);
          ## Set file type property
          [ftype, filtnam] = __get_ftype__ (filename);
          if (isempty (filtnam))
            filtnam = "calc8";
          endif
          lProp = javaObject ...
            ("com.sun.star.beans.PropertyValue", "FilterName", 0, filtnam, []);
          lProps(1) = lProp;
          ## Set "Overwrite" property
          lProp = ...
            javaObject ("com.sun.star.beans.PropertyValue", "Overwrite", 0, true, []);
          lProps(2) = lProp;
          ## OK, store file
      #    if (isfield (xls, "nfilename"))
            ## Store in another file 
            ## FIXME check if we need to close the old file
      #      xStore.storeAsURL (xls.nfilename, lProps);
      #    else
      #      xStore.storeAsURL (xls.filename, lProps);
            xStore.storeAsURL (filename, lProps);
      #    endif
        else
          xStore.store ();
        endif
      endif
    endif
    xls.changed = -1;    ## Needed for check on properly shutting down the office process
    ## Workaround:
    unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.frame.XModel");
    xModel = xls.app.xComp.queryInterface (unotmp);
    unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.util.XCloseable");
    xClosbl = xModel.queryInterface (unotmp);
    xClosbl.close (true);
    unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.frame.XDesktop");
    xDesk = xls.app.aLoader.queryInterface (unotmp);
    if (terminate_uno)
      pause (0.25);
      xDesk.terminate();
    endif
    xls.changed = 0;
  catch err
    uno_discard_component (xls);
    if (force && terminate_uno)
      try
        ## Force closing the office process.
        unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.frame.XDesktop");
        xDesk = xls.app.aLoader.queryInterface (unotmp);
        xDesk.terminate();
      catch
      end_try_catch
    endif
    warning ("xlsclose: error closing xls pointer (UNO): %s", err.message);
    return
  end_try_catch

endfunction


function filename = uno_filename_to_url (filename)

  if (uno_is_url (filename))
    return
  endif

  if (ispc)
    fname = canonicalize_file_name (filename);
    if (isempty (fname))
      ## File doesn't exist yet? try make_absolute_filename()
      fname = make_absolute_filename (filename);
    endif
  else
    fname = make_absolute_filename (filename);
  endif

  ## On Windows, change backslash file separator into forward slash.
  if (strcmp (filesep, "\\"))
    tmp = strsplit (fname, filesep);
    flen = numel (tmp);
    tmp(2:2:2*flen) = tmp;
    tmp(1:2:2*flen) = "/";
    fname = [tmp{:}];
  endif

  filename = ["file://" fname];

endfunction


function tf = uno_is_url (filename)

  tf = ! isempty (regexp (filename, "^[[:alpha:]][[:alnum:]+.-]*://", "once"));

endfunction


function uno_discard_component (xls)

  if (! isfield (xls, "app") || ! isfield (xls.app, "xComp"))
    return
  endif

  try
    unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.frame.XModel");
    xModel = xls.app.xComp.queryInterface (unotmp);
    unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.util.XModifiable");
    xModified = xModel.queryInterface (unotmp);
    xModified.setModified (false);
  catch
  end_try_catch

  try
    unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.frame.XModel");
    xModel = xls.app.xComp.queryInterface (unotmp);
    unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.util.XCloseable");
    xClosbl = xModel.queryInterface (unotmp);
    xClosbl.close (true);
    return
  catch
  end_try_catch

  try
    unotmp = javaObject ("com.sun.star.uno.Type", "com.sun.star.lang.XComponent");
    xComp = xls.app.xComp.queryInterface (unotmp);
    xComp.dispose ();
  catch
  end_try_catch

endfunction


function uno_remove_orphan_lock_file (filename)

  fname = uno_file_url_to_path (filename);
  if (isempty (fname) || exist (fname, "file"))
    return
  endif

  [fpath, fnam, fext] = fileparts (fname);
  if (isempty (fpath))
    return
  endif

  lock_file = fullfile (fpath, [".~lock." fnam fext "#"]);
  if (! exist (lock_file, "file"))
    return
  endif

  try
    lock_text = fileread (lock_file);
  catch
    return
  end_try_catch

  ## Only clean locks left by old octave-io temporary UNO profiles.  Do not
  ## remove a lock that belongs to this Octave process or a normal office profile.
  tokens = regexp (lock_text, "octave-io-uno-profile-[^;]*-([0-9]+)", ...
                   "tokens", "once");
  if (isempty (tokens) || str2double (tokens{1}) == getpid ())
    return
  endif

  try
    delete (lock_file);
  catch
  end_try_catch

endfunction


function fname = uno_file_url_to_path (filename)

  fname = "";
  if (! strncmp (filename, "file://", 7))
    return
  endif

  fname = filename(8:end);
  if (ispc () && numel (fname) > 2 && fname(1) == "/" && fname(3) == ":")
    fname = fname(2:end);
  endif
  if (strcmp (filesep, "\\"))
    fname = strrep (fname, "/", filesep);
  endif

endfunction
