#!/usr/bin/env python3

# Copyright (c) 2017-2023 California Institute of Technology ("Caltech"). U.S.
# Government sponsorship acknowledged. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0

r'''Display the imager resolution for a given model

SYNOPSIS

  $ mrcal-show-model-resolution camera.cameramodel
  ... a plot pops up showing the resolution of the imager in degrees/pixel

The angular resolution of an image varies dramatically from lens to lens and
also over the image. This tool visualizes this behavior.

'''

import sys
import argparse
import re
import os

def parse_args():

    parser = \
        argparse.ArgumentParser(description = __doc__,
                                formatter_class=argparse.RawDescriptionHelpFormatter)

    parser.add_argument('--gridn',
                        type=int,
                        default = (60,40),
                        nargs = 2,
                        help='''How densely we should sample the imager. By default we use a 60x40 grid''')

    parser.add_argument('--cbmin',
                        type=float,
                        help='''Minimum range of the colorbar. Autoscaling by
                        default''')
    parser.add_argument('--cbmax',
                        type=float,
                        help='''Maximum range of the colorbar. Autoscaling by
                        default''')

    parser.add_argument('--mode',
                        choices=('min','max','mean','eccentricity'),
                        default='mean',
                        help='''The resolution varies in different observation
                        directions. This selects the min or max or mean
                        resolution. "eccentricity" reports max/min. The default
                        is "mean"''')

    parser.add_argument('--title',
                        type=str,
                        default = None,
                        help='''Title string for the plot''')

    parser.add_argument('--hardcopy',
                        type=str,
                        help='''Write the output to disk, instead of making an interactive plot''')
    parser.add_argument('--terminal',
                        type=str,
                        help=r'''gnuplotlib terminal. The default is good almost always, so most people don't
                        need this option''')
    parser.add_argument('--set',
                        type=str,
                        action='append',
                        help='''Extra 'set' directives to gnuplotlib. Can be given multiple times''')
    parser.add_argument('--unset',
                        type=str,
                        action='append',
                        help='''Extra 'unset' directives to gnuplotlib. Can be given multiple times''')
    parser.add_argument('model',
                        type=str,
                        help='''The model to visualize''')

    args = parser.parse_args()

    return args

args = parse_args()



import numpy as np
import numpysane as nps
import gnuplotlib as gp
import mrcal




plot_options = dict(hardcopy = args.hardcopy,
                    terminal = args.terminal)
if args.set is not None:
    plot_options['set'] = args.set
if args.unset is not None:
    plot_options['unset'] = args.unset

if args.title is not None:
    plot_options['title'] = args.title

def openmodel(f):
    try:
        return mrcal.cameramodel(f)
    except Exception as e:
        print(f"Couldn't load camera model '{f}': {e}",
              file=sys.stderr)
        sys.exit(1)

model = openmodel(args.model)



W,H = model.imagersize()
q = np.ascontiguousarray( \
    nps.mv( nps.cat(*np.meshgrid( np.linspace(0, W-1, args.gridn[0]),
                                  np.linspace(0, H-1, args.gridn[1]) )),
            0,-1) )

resolution__deg_pixel = mrcal.model_resolution__deg_pixel(q, model,
                                                          mode = args.mode)

if args.cbmax is None:
    # ceil x * 10^n where x is an integer in [1,9] and n is an integer
    args.cbmax = np.max(resolution__deg_pixel)
    base10_floor = np.power(10., np.floor(np.log10(args.cbmax)))
    args.cbmax = np.ceil(args.cbmax / base10_floor) * base10_floor
if args.cbmin is None:
    # floor x * 10^n where x is an integer in [1,9] and n is an integer
    args.cbmin = np.min(resolution__deg_pixel)
    base10_floor = np.power(10., np.floor(np.log10(args.cbmin)))
    args.cbmin = np.floor(args.cbmin / base10_floor) * base10_floor

curve_options = \
    mrcal.visualization._options_heatmap_with_contours(
        # update these plot options
        plot_options,
        contour_min  = args.cbmin,
        contour_max  = args.cbmax,
        imagersize   = model.imagersize(),
        gridn_width  = args.gridn[0],
        gridn_height = args.gridn[1])

if args.mode != 'eccentricity':
    title = f"Camera resolution {args.mode} in deg/pixel"
else:
    title = "Camera resolution eccentricity"

gp.plot( (resolution__deg_pixel, curve_options),
         **plot_options,
         title = title,
         wait = args.hardcopy is None)
