#! /usr/bin/python3
# vim: set filetype=python:

# recomp-jpg: recompress JPEG files to a specified quality level, only keeping
# the new version if it is actually *smaller*.

# Copyright (C) 2004-2026 by Brian Lindholm.  This file is part of the
# littleutils utility set.
#
# The recomp-jpg utility 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, or (at your option) any later
# version.
#
# The recomp-jpg utility 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
# the littleutils.  If not, see <https://www.gnu.org/licenses/>.

import getopt, os, subprocess, sys, tempfile

### GET INPUT ARGUMENTS ###
# print online help
def usage(rc: int) -> None:
    print('recomp-jpg 1.4.0')
    print('usage: recomp-jpg [-a(rithmetic)] [-d DCT_method] [-f filelist] [-h(elp)]')
    print('         [-p(ipe)] [-q(uiet)] [-t target_quality] [-T threads]')
    sys.exit(rc)
# load list of files
def load_list_from_file() -> None:
    if not os.path.isfile(opt_f):  # abort if file does not exist
        print('recomp-jpg error: file list %s does not exist' % opt_f, file=sys.stderr)
        sys.exit(1)
    try:
        FILE = open(opt_f, 'r')
    except:  # abort if file cannot be opened for read
        print('recomp-jpg error: file list %s cannot be opened' % opt_f, file=sys.stderr)
        sys.exit(1)
    filelist.extend(FILE.read().splitlines())
    FILE.close()
# load list of files from stdin
def load_list_from_stdin() -> None:
    filelist.extend(sys.stdin.read().splitlines())
    sys.stdin.close()
# set defaults
filelist = []
opt_a = False   # perform trials with arithmetic coding
opt_d = 'float' # type of DCT arithmetic to use
opt_f = None    # file containing list of files to process
opt_p = False   # read list of files to process from stdin
opt_q = False   # be quiet
opt_t = None    # target quality (1 to 100)
opt_T = None    # requested thread-count
# get command-line options
try:
    opts, filelist = getopt.getopt(sys.argv[1:], 'ad:f:hpqt:T:', 'help')
except getopt.error as msg:
    # print help if bad opts used, then quit
    print(msg)
    usage(1)
# parse options
for o, v in opts:
    if o in ('-h', '--help'): usage(0)
    elif o == '-a': opt_a = True
    elif o == '-d': opt_d = str(v)
    elif o == '-f': opt_f = str(v)
    elif o == '-p': opt_p = True
    elif o == '-q': opt_q = True
    elif o == '-t': opt_t = str(v)
    elif o == '-T': opt_T = int(v)
# load file list from file and/or stdin if requested
if opt_f != None: load_list_from_file()
if opt_p: load_list_from_stdin()
# make sure we have at least one file to process
if len(filelist) == 0:
    if (not opt_f) and (not opt_p): usage(1)
    sys.exit(0)
# remove leading './' and trim list to unique items
filelist = [x.removeprefix('./') for x in filelist]
seen = set()
unique_filelist = [x for x in filelist if x not in seen and (seen.add(x) or True)]
# pick a reasonable default if thread-count is unspecified
if opt_T == None: opt_T = max(1, min(os.cpu_count() // 2, len(unique_filelist)))

### HAND-OFF to OPT-JPG ###
TMPFILE = tempfile.SpooledTemporaryFile(mode='w+')
for filename in unique_filelist: print(filename, file=TMPFILE)
TMPFILE.seek(0)
args = ['opt-jpg', '-p', '-M', opt_t, '-T', str(opt_T)]
if opt_a: args.append('-a')
if opt_d: args.extend(['-d', opt_d])
if opt_q: args.append('-q')
subprocess.run(args, stdin=TMPFILE)
TMPFILE.close()
sys.exit(0)
