#!/usr/pkg/bin/ruby40
# frozen_string_literal: true

#
# A simple command-line tool to de- and encode Ascii85, modeled after `base64`
# from the GNU Coreutils.
#

require 'optparse'
require File.join(File.dirname(__FILE__), '..', 'lib', 'ascii85')
require File.join(File.dirname(__FILE__), '..', 'lib', 'Ascii85', 'version')

class CLI
  attr_reader :options

  def initialize(argv, stdin: $stdin, stdout: $stdout)
    @in = stdin
    @out = stdout

    @options = {
      wrap: 80,
      action: :encode
    }

    parse_options(argv)
  end

  def parse_options(argv)
    @parser = OptionParser.new do |opts|
      opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [OPTIONS] [FILE]\n" \
                    'Encodes or decodes FILE or STDIN using Ascii85 and writes to STDOUT.'

      opts.on('-w', '--wrap COLUMN', Integer,
              'Wrap lines at COLUMN. Default is 80, use 0 for no wrapping') do |opt|
        @options[:wrap] = opt.abs
        @options[:wrap] = false if opt.zero?
      end

      opts.on('-d', '--decode', 'Decode the input') do
        @options[:action] = :decode
      end

      opts.on('-h', '--help', 'Display this help and exit') do
        @options[:action] = :help
      end

      opts.on('-V', '--version', 'Output version information') do |_opt|
        @options[:action] = :version
      end

    end

    remaining_args = @parser.parse!(argv)

    case remaining_args.size
    when 0
      @options[:file] = '-'
    when 1
      @options[:file] = remaining_args.first
    else
      raise(OptionParser::ParseError, "Superfluous operand(s): \"#{remaining_args[1..].join('", "')}\"")
    end
  end
  
  def input
    fn = @options[:file]

    return @in.binmode if fn == '-'

    raise(StandardError, "File not found: \"#{fn}\"") unless File.exist?(fn)
    raise(StandardError, "File is not readable: \"#{fn}\"") unless File.readable_real?(fn)

    File.new(fn, 'rb')
  end

  def decode
    Ascii85.decode(input.read, out: @out)
  end

  def encode
    Ascii85.encode(input, @options[:wrap], out: @out)
  end

  def version 
    "Ascii85 v#{Ascii85::VERSION},\nwritten by Johannes Holzfuß"
  end

  def help
    @parser
  end

  def call
    case @options[:action]
    when :help then @out.puts help
    when :version then @out.puts version
    when :encode then encode
    when :decode then decode
    end
  end
end

if File.basename($PROGRAM_NAME) == "ascii85"
  begin
    CLI.new(ARGV).call
  rescue OptionParser::ParseError => e
    abort e.message
  rescue Ascii85::DecodingError => e
    abort "Decoding Error: #{e.message}"
  rescue StandardError => e
    abort "Error: #{e.message}"
  end
end
