#!/usr/bin/env python3

import argparse
import re
import os
import pathlib
import shutil
import sys
import subprocess
import time

'''
chooseviewer.py - choose a program for viewing a file, based either on the filename.extension, or
by an extension supplied on the command line. If --delete is set, delete the file after viewing.

The first arugment is a filename or URL. Usually, we can figure out
the type of file based on the file extension. However, if a second
argument is given, it is assumed to be a typical file extension
such as pdf, html, jpg etc. This is mainly for the case in which
a file doesn't have an extension, and we need to be able to tell
chooseviewer the type of file.

Since web browsers have existed, it was always possible to open a local
file using a URL that began with "file://...". Recent web browsers will
now refuse to open any file that is not within the user's home directory,
even files with world-read permissions. One could argue that a solution
could have been found that is less sweeping in scope. 

BIRCH has always had the ability to display HTML documentation through a
web server. Typically that was only done on a central Linux system that already
ran a web server. For the special case of a standalone PC that is used by 
a group of users (eg. a lab group) the solution is to run a web server that
is exposed to the local network. Typically the URL would be something like
"http://192.168.0.20/....." or something similar.

To set up a web server on a standalone PC with multiple users, see
https://home.cc.umanitoba.ca/~psgendb/birchadmin/inst.Web/inst.Web.html  

Synopsis: chooseviewer.py <filename> [--ext extension][--delete] [--wait] 

@modified: July 29, 2026
@author: Brian Fristensky
@contact: frist@cc.umanitoba.ca  
'''

PROGRAM = "chooseviewer.py : "
USAGE = "\n\tUSAGE: chooseviewer.py <filename> [--ext extension][--delete]"

DEBUG = True
if DEBUG :
    print('Debugging mode on')


# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class Parameters:
    """
      	Wrapper class for command line parameters
      	"""
    def __init__(self):
        """
     	  Initializes arguments:
		Target = ""
                Ext = ""
                Delete = False
                Wait = False

     	  Then calls read_args() to fill in their values from command line
          """

        # For HTML files, we will need the URL prefix for either local
        # files (file:///...) or web-based files (http://...)
        self.BIRCH = os.environ['BIRCH']
        FN = os.path.join(self.BIRCH,"local","admin","BIRCH.properties")
        if  os.path.exists(FN):
            print("chooseviewer.py: Reading URL prefixes from BIRCH.properties")
            self.BIRCHURL = ""
            self.HOMEDIRURL = ""
            FILE = open(FN, 'r')

            for LINE in FILE:
                TOKENS = LINE.split("=")
                # we have to remove the escape character from BirchProps lines
                if TOKENS[0] == 'BirchProps.birchHomeURL':
                    self.HOMEDIRURL = TOKENS[1].strip().replace('\\','')
                if TOKENS[0] == 'BirchProps.birchURL':
                    self.BIRCHURL = TOKENS[1].strip().replace('\\','')                    
            FILE.close()

        self.FilePath = sys.argv[1]
        self.Ext  = ""
        self.Delete  = False
        self.Wait = False 
        self.read_args()
        
        if DEBUG :
            print("$BIRCH: " + self.BIRCH)        
            print("BIRCH URL: " + self.BIRCHURL)
            print("BIRCH HOME DIR: " + self.HOMEDIRURL)
            print('------------ Parameters from command line ------')
            print('    Target: ' + self.FilePath)
            print('    Ext: ' + self.Ext)
            print('    Delete: ' + str(self.Delete))
            print('    Wait: ' + str(self.Wait))
            print()  

    def read_args(self):
        """
        	Read command line arguments into a Parameter object
    	"""	
        parser = argparse.ArgumentParser()
        parser.add_argument("infile", action="store", default="", help="input file")
        parser.add_argument("--ext", dest="ext", action="store", default="", help="file type")
        parser.add_argument("--delete", dest="delete", action="store_true", default=False, help="delete file after viewing")
        parser.add_argument("--wait", dest="wait", action="store_true", default=False, help="wait for viewer to terminate before terminating chooseviewer.py")
        args = parser.parse_args() 
        self.FilePath = args.infile
        self.Ext = args.ext
        if self.Ext == "" :
            e = pathlib.Path(self.FilePath).suffix
            if e in [".html",".htm",".shtml"] :
                self.Ext = e     
        self.Delete = args.delete
        self.Wait = args.wait

#---------------------------------------------------------------
# On macos, open no longer supports PostScript files. 
# We therefore have to convert PostScript to PDF
# However, this function could also be used for other
# platforms
def PStoPDF(FilePath,Delete) :
    fn = FilePath.rsplit('.')[0]
    ofn = fn + '.pdf'
    if os.environ.get("BIRCH_PLATFORM") in ["osx-x86_64","macos-arm64"] :
        COMMAND = ["pstopdf", FilePath, "-o", ofn]
    else :
        COMMAND = ["ps2pdf", FilePath, ofn]
    p = subprocess.Popen(COMMAND)
    p.wait()
    if Delete :
        os.remove(FilePath)
    return ofn

#---------------------------------------------------------------
def LaunchBrowser(P) :

    # We have to handle several types of URLs:
    # filepath
    # file:///filepath
    # $variable/filepath (begins with a BIRCH env. variale like $dat
    # http://URL/filepath

    # local files, served through the web server
    # eg. if command line was passed an environment varialbe
    # such as $doc, $dat, $tutorial etc. we expect that the\
    # shell would have substituted in the $BIRCH directory, so
    # chooseviewer.py gets a path beginning with the BIRCH HOME
    # directory.   
    
    # eg. if command line was passed an environment varialbe
    # such as $doc, $dat, $tutorial etc. we expect that the\
    # shell would have substituted in the $BIRCH directory, so
    # chooseviewer.py gets a path beginning with the BIRCH HOME
    # directory.        

    BIRCHWebDir = os.path.join(P.BIRCH,"public_html/")
    
    # files are accessed through web server
    if P.BIRCHURL.startswith("http") :
        # Command line filepath references a local file, so we need
        # to put http://.... in front of the filepath
        if P.FilePath.startswith(BIRCHWebDir) :
            print("1")
            #Target = os.path.join(BIRCHURL,FilePath.replace(BIRCHWebDir,""))
            # os.path.join will only work with file paths, not URLs
            Target = P.BIRCHURL + os.path.sep + P.FilePath.replace(BIRCHWebDir,"")
        elif P.FilePath.startswith(P.BIRCH) : 
            print("2")
            Target = P.HOMEDIRURL + os.path.sep + P.FilePath.replace(P.BIRCH,"")        
        # remote files served through web server
        else :
            print("3")
            # file path is a normal URL that already begins with http://...
            Target = P.FilePath
                
    # files are accessed in local directories    
    else:
        # file:/// only works if the filepath is within the user's
        # $HOME directory
        if P.FilePath.startswith("file:///") :
            print("4")
            Target = P.FilePath
        elif P.FilePath.startswith(P.BIRCH) :
            Target = P.HOMEDIRURL + os.path.sep + P.FilePath.replace(P.BIRCH,"")         
        else :
            print("5")
            Target = P.FilePath

    Program = os.environ.get("BL_Browser")
    #Program = os.environ.get("BL_Browser") + " -new-instance"
    if DEBUG :
        print ("chooseviewer.py - Opening file: " + Target)
    """    
    Treat the Program variable as a list of tokens 
    We do it this way to accommodate MacOSX, which launches 
    viewers using the open command.
    For example, to open a file using firefox

    open -a firefox filename

    """
    comstr = Program.split() #Break command into a list of tokens
    comstr.append(Target)
    
    if P.Wait :
        p = subprocess.Popen(comstr)
        p.wait()
        #os.remove(Target)
    else:
        # I tried lots of permutations of subprocess, but was never able to get the
        # browser to open, and wait, and then delete the file, except by using os.system.
        #p = subprocess.Popen(comstr)
        #time.sleep(20)
        #os.remove(Target)
        #COMMAND = '(' + Program + ' ' + Target + '; sleep 120; rm ' + Target + ')&'
        COMMAND = '(' + Program + ' ' + Target +')&'
        #p = subprocess.run(COMMAND, shell=True)
        os.system(COMMAND)    

#---------------------------------------------------------------
def LaunchViewer(FilePath,Ext,Delete,Wait) :

    # Get rid of a leading period from a file extension. This means it doesn't
    # matter whether or not sys.argv[2]  begins with a dot.
    if Ext[0] == '.' :
        Ext = Ext[1:]

    # Some programs can't figure out how to open a file if it doesn't have the right file 
    # extension, so we make a duplicate file with a recognizable extension, and work with
    # that.
    Target = 'chooseviewer' + str(os.getpid()) + "." + Ext
    shutil.copy(FilePath,Target)

    if DEBUG :
        print ("chooseviewer.py - Opening file: " + Target)

    #Most of these external calls have been switched over to subprocess, from birchscript.forkrun.
    # What seems to happen is that birchscript.forkrun runs in the background, allowing chooseviewer.py to 
    # terminate. This means that if the script that called chooseviewer deletes the input file as its
    # next step, that file will be deleted before the ace* scripts have time to make a copy. 
    # We probably also need a similar fix for web pages, but that would require a separate script
    # that opened a web browser.
            
    if Ext == "pdf":
        # -------- PDF
        Program = os.environ.get("BL_PDFViewer")

    elif Ext in ("ps", "eps"):
        Target = PStoPDF(FilePath,Delete)
        Program = os.environ.get("BL_PDFViewer")

    elif Ext in ("xls", "xlsx", "ods", "sxc", "csv", "tsv", "dif", "dbf","ctab"):
        # -------- Spreadsheet
        Program = os.environ.get("BL_Spreadsheet")

    elif Ext in ("odt", "sxw", "doc", "docx", "rtf"):
        # -------- Document
        Program = os.environ.get("BL_Document")

    elif Ext in ("gif", "jpg", "jpeg", "png", "bmp", "tif", "tiff"):
        # -------- Bitmap graphics
        Program = os.environ.get("BL_ImageViewer")

    else:
        # -------- Default is text editor
        Program = os.environ.get("BL_TextEditor")

    """    
    Treat the Program variable as a list of tokens 
    We do it this way to accommodate MacOSX, which launches 
    viewers using the open command.
    For example, to open a file using firefox

    open -a firefox filename

    """
    comstr = Program.split() #Break command into a list of tokens
    comstr.append(Target)

    if Wait :
        p = subprocess.Popen(comstr)
        p.wait()
        os.remove(Target)
    else:
        # I tried lots of permutations of subprocess, but was never able to get the
        # browser to open, and wait, and then delete the file, except by using os.system.
        #p = subprocess.Popen(comstr)
        #time.sleep(20)
        #os.remove(Target)
        COMMAND = '(' + Program + ' ' + Target + '; sleep 120; rm ' + Target + ')&'
        #p = subprocess.run(COMMAND, shell=True)
        os.system(COMMAND)
        

    # Remove the original, except if it is an external URL
    if Delete :
        if DEBUG :
            print ('Deleting ' + FilePath)
        time.sleep(5)
        os.remove(FilePath)    

#======================== MAIN PROCEDURE ==========================

def chooseviewer():
    """
    	Called when not in documentation mode.
    """
    print ('Running chooseviewer.py')
    # Read parameters from command line
    P = Parameters()

    OKAY = True
    # If the Target is an external web page, we set Ext to Web. 
    # Launchviewer will not try to delete an external web page.
    if (re.search('^http:|^https:|^ftp:|^ftps:', P.FilePath)):
        P.Ext = "Web"

    # This is for files on the local filesystem.
    else :    
        if (os.path.isdir(P.FilePath)):
            OKAY = False
            print( "chooseviewer.py: cannot open directories!")
        elif (not os.path.exists(P.FilePath)):
            OKAY = False
            print ('chooseviewer.py: file not found')    

        if OKAY :
            if P.Ext == "" :
                P.Ext=os.path.splitext(P.FilePath)[1]
            P.Ext = P.Ext.lower()   
    if OKAY :
        if DEBUG :
            print('------------ Final Parameter Values ------')
            print('    FilePath: ' + P.FilePath)
            print('    Ext: ' + P.Ext)
            print('    Delete: ' + str(P.Delete)) 
            print('    Wait: ' + str(P.Wait)) 
            
        if P.Ext in ["Web", ".html", ".shtml", ".htm"] :    
            LaunchBrowser(P)
        else:
            LaunchViewer(P.FilePath,P.Ext,P.Delete,P.Wait)	

if __name__=="__main__":    
        chooseviewer()

