"""Script to generate the HELP homogenised SPIRE maps."""

from datetime import datetime
from itertools import product

import numpy as np

from astropy.io import fits
from astropy.table import Table

VERSION = "0.9"

spire_maps = Table.read("spire_maps.fits")
all_obsids = Table.read("spire_obsids.fits")


def get_band(filename):
    if "250" in filename or "psw" in filename.lower():
        return "250"
    if "350" in filename or "pmw" in filename.lower():
        return "350"
    if "500" in filename or "plw" in filename.lower():
        return "500"
    raise ValueError(f"Can't identify the band in '{filename}'.")


# HerMES
hermes_maps = spire_maps[spire_maps['survey'] == "HerMES"]

for row in hermes_maps:
    field = row['field']
    filename = row['filename']

    if "hers-helms-xmm" not in filename:
        obsids = sorted(list(all_obsids['ObsID'][
            all_obsids['field'] == field
        ]))
    else:
        obsids = sorted(list(all_obsids['ObsID'][
            (
                (all_obsids['field'] == field) |
                (all_obsids['field'] == 'XMM-LSS')
            ) & (all_obsids['not_in_hs82'] == 0)
        ]))
    assert len(obsids) > 0

    orig_hdu_list = fits.open(f"../dmu19_HerMES/data/{filename}")

    image_hdu = orig_hdu_list[1]
    assert image_hdu.header['EXTNAME'] == "image"
    image_hdu.header['EXTNAME'] = "IMAGE"
    image_hdu.header['BUNIT'] = "Jy / beam"

    if filename == 'hers-helms-xmm_itermap_20160623_PSW.fits':
        # FIXME The Herschel-Strip-82 SPIRE250 map is corrupted; we can only
        # access the image data.
        error_hdu = fits.ImageHDU()
        error_hdu.header['EXTNAME'] = "ERROR"
        error_hdu.header.add_comment("The error map is not available.")
        exposure_hdu = fits.ImageHDU()
        exposure_hdu.header['EXTNAME'] = "EXPOSURE"
        exposure_hdu.header.add_comment("The exposure map is not available.")
        mask_hdu = fits.ImageHDU()
        mask_hdu.header['EXTNAME'] = "MASK"
        mask_hdu.header.add_comment("The mask map is not available.")

    else:
        error_hdu = orig_hdu_list[2]
        assert error_hdu.header['EXTNAME'] == "error"
        error_hdu.header['EXTNAME'] = "ERROR"
        error_hdu.header['BUNIT'] = "Jy / beam"

        exposure_hdu = orig_hdu_list[3]
        assert exposure_hdu.header['EXTNAME'] == "exposure"
        exposure_hdu.header['EXTNAME'] = "EXPOSURE"
        exposure_hdu.header['BUNIT'] = "s"

        mask_hdu = orig_hdu_list[4]
        if "hers-helms-xmm" not in filename and "SSDF" not in filename:
            assert mask_hdu.header['EXTNAME'] == "flag"
        else:
            assert mask_hdu.header['EXTNAME'] == "mask"
            # The Herschel-Stripe-82 and SSDF masks are 0 for bad and 1 for
            # good while we use the reverse for HELP
            good_mask = mask_hdu.data == 1
            bad_mask = mask_hdu.data == 0
            mask_hdu.data[good_mask] = 0
            mask_hdu.data[bad_mask] = 1
        mask_hdu.header['EXTNAME'] = "MASK"


    nebfilt_map_name = filename.replace(".fits", "_nebfiltered.fits")
    nebfilt_map = fits.open(
        f"../dmu19_nebular_filtered_maps/data/{nebfilt_map_name}")
    nebfilt_hdu = fits.ImageHDU(header=nebfilt_map[0].header,
                                data=nebfilt_map[0].data)
    nebfilt_hdu.header['EXTNAME'] = "NEBFILT"
    nebfilt_hdu.header['BUNIT'] = "Jy / beam"

    primary_hdu = fits.PrimaryHDU()
    primary_hdu.header.append((
        "CREATOR", "Herschel Extragalactic Legacy Project"
    ))
    primary_hdu.header.append((
        "TIMESYS", "UTC", "All dates are in UTC time"
    ))
    primary_hdu.header.append((
        "DATE", datetime.now().replace(microsecond=0).isoformat(),
        "Date of file creation"
    ))
    primary_hdu.header.append((
        "VERSION", VERSION, "HELP product version"
    ))
    primary_hdu.header.append((
        "TELESCOP", "Herschel", "Name of the telescope"
    ))
    primary_hdu.header.append((
        "INSTRUME", "SPIRE", "Name of the instrument"
    ))
    primary_hdu.header.append((
        "FILTER", f"SPIRE-{get_band(filename)}", "Name of the filter"
    ))
    primary_hdu.header.append((
        "FIELD", field, "Name of the HELP field"
    ))
    for idx, obs_id in enumerate(obsids):
        keyword = "OBSID" + str(idx).zfill(3)
        primary_hdu.header.append((keyword, obs_id))

    if "hers-helms-xmm" in filename:
        primary_hdu.header.add_comment(
            "These maps also contain some observations on the XMM-LSS field.")

    hdu_list = fits.HDUList([primary_hdu, image_hdu, nebfilt_hdu, error_hdu,
                             exposure_hdu, mask_hdu])
    hdu_list.writeto(f"data/{field}_SPIRE{get_band(filename)}_v{VERSION}.fits",
                     checksum=True)
    print(f"{field} / {get_band(filename)} processed...")

# AKARI-NEP
akarinep_maps = spire_maps[spire_maps['survey'] == "AKARI-NEP"]

for row in akarinep_maps:
    field = row['field']
    filename = row['filename']

    obsids = sorted(list(all_obsids['ObsID'][all_obsids['field'] == field]))
    assert len(obsids) > 0

    orig_hdu_list = fits.open(f"../dmu19_AKARI-NEP/data/{filename}")

    image_hdu = orig_hdu_list[1]
    assert image_hdu.header['EXTNAME'] == "image"
    image_hdu.header['EXTNAME'] = "IMAGE"
    image_hdu.header['BUNIT'] = "Jy / beam"

    error_hdu = orig_hdu_list[2]
    assert error_hdu.header['EXTNAME'] == "error"
    error_hdu.header['EXTNAME'] = "ERROR"
    error_hdu.header['BUNIT'] = "Jy / beam"

    # The maps contain the coverage and where observed in normal mode.
    exposure_hdu = orig_hdu_list[3]
    assert exposure_hdu.header['EXTNAME'] == "coverage"
    exposure_hdu.data *= 1 / 18.6  # Conversion of coverage to exposure
    exposure_hdu.header['EXTNAME'] = "EXPOSURE"
    exposure_hdu.header['BUNIT'] = "s"
    exposure_hdu.header['QTTY____'] = "s"

    # The maps do not contain mask maps.
    mask_hdu = fits.ImageHDU()
    mask_hdu.header['EXTNAME'] = "MASK"
    mask_hdu.header.add_comment("The mask map is not available.")

    nebfilt_map_name = filename.replace(".fits", "_nebfiltered.fits")
    nebfilt_map = fits.open(
        f"../dmu19_nebular_filtered_maps/data/AKARI-NEP_{nebfilt_map_name}")
    nebfilt_hdu = fits.ImageHDU(header=nebfilt_map[0].header,
                                data=nebfilt_map[0].data)
    nebfilt_hdu.header['EXTNAME'] = "NEBFILT"
    nebfilt_hdu.header['BUNIT'] = "Jy / beam"

    primary_hdu = fits.PrimaryHDU()
    primary_hdu.header.append((
        "CREATOR", "Herschel Extragalactic Legacy Project"
    ))
    primary_hdu.header.append((
        "TIMESYS", "UTC", "All dates are in UTC time"
    ))
    primary_hdu.header.append((
        "DATE", datetime.now().replace(microsecond=0).isoformat(),
        "Date of file creation"
    ))
    primary_hdu.header.append((
        "VERSION", VERSION, "HELP product version"
    ))
    primary_hdu.header.append((
        "TELESCOP", "Herschel", "Name of the telescope"
    ))
    primary_hdu.header.append((
        "INSTRUME", "SPIRE", "Name of the instrument"
    ))
    primary_hdu.header.append((
        "FILTER", f"SPIRE-{get_band(filename)}", "Name of the filter"
    ))
    primary_hdu.header.append((
        "FIELD", field, "Name of the HELP field"
    ))
    for idx, obs_id in enumerate(obsids):
        keyword = "OBSID" + str(idx).zfill(3)
        primary_hdu.header.append((keyword, obs_id))

    hdu_list = fits.HDUList([primary_hdu, image_hdu, nebfilt_hdu, error_hdu,
                             exposure_hdu, mask_hdu])
    hdu_list.writeto(f"data/{field}_SPIRE{get_band(filename)}_v{VERSION}.fits",
                     checksum=True)
    print(f"{field} / {get_band(filename)} processed...")

# SPIRE-NEP
spirenep_maps = spire_maps[spire_maps['survey'] == "SPIRE-NEP"]

for row in spirenep_maps:
    field = row['field']
    filename = row['filename']

    obsids = sorted(list(all_obsids['ObsID'][all_obsids['field'] == field]))
    assert len(obsids) > 0

    orig_hdu_list = fits.open(
        f"../dmu19_SPIRE-NEP-calibration/data/{filename}")

    image_hdu = orig_hdu_list[1]
    assert image_hdu.header['EXTNAME'] == "image"
    image_hdu.header['EXTNAME'] = "IMAGE"
    image_hdu.header['BUNIT'] = "Jy / beam"

    mask_hdu = orig_hdu_list[2]
    assert mask_hdu.header['EXTNAME'] == "flag"
    mask_hdu.header['EXTNAME'] = "MASK"

    # The maps contain the coverage and where observed in normal mode.
    exposure_hdu = orig_hdu_list[3]
    assert exposure_hdu.header['EXTNAME'] == "coverage"
    exposure_hdu.data *= 1 / 18.6  # Conversion of coverage to exposure
    exposure_hdu.header['EXTNAME'] = "EXPOSURE"
    exposure_hdu.header['BUNIT'] = "s"
    exposure_hdu.header['QTTY____'] = "s"

    error_hdu = orig_hdu_list[4]
    assert error_hdu.header['EXTNAME'] == "error"
    error_hdu.header['EXTNAME'] = "ERROR"
    error_hdu.header['BUNIT'] = "Jy / beam"

    nebfilt_map_name = filename.replace(".fits", "_nebfiltered.fits")
    nebfilt_map = fits.open(
        f"../dmu19_nebular_filtered_maps/data/SPIRE-NEP_{nebfilt_map_name}")
    nebfilt_hdu = fits.ImageHDU(header=nebfilt_map[0].header,
                                data=nebfilt_map[0].data)
    nebfilt_hdu.header['EXTNAME'] = "NEBFILT"
    nebfilt_hdu.header['BUNIT'] = "Jy / beam"

    primary_hdu = fits.PrimaryHDU()
    primary_hdu.header.append((
        "CREATOR", "Herschel Extragalactic Legacy Project"
    ))
    primary_hdu.header.append((
        "TIMESYS", "UTC", "All dates are in UTC time"
    ))
    primary_hdu.header.append((
        "DATE", datetime.now().replace(microsecond=0).isoformat(),
        "Date of file creation"
    ))
    primary_hdu.header.append((
        "VERSION", VERSION, "HELP product version"
    ))
    primary_hdu.header.append((
        "TELESCOP", "Herschel", "Name of the telescope"
    ))
    primary_hdu.header.append((
        "INSTRUME", "SPIRE", "Name of the instrument"
    ))
    primary_hdu.header.append((
        "FILTER", f"SPIRE-{get_band(filename)}", "Name of the filter"
    ))
    primary_hdu.header.append((
        "FIELD", field, "Name of the HELP field"
    ))
    for idx, obs_id in enumerate(obsids):
        keyword = "OBSID" + str(idx).zfill(3)
        primary_hdu.header.append((keyword, obs_id))

    hdu_list = fits.HDUList([primary_hdu, image_hdu, nebfilt_hdu, error_hdu,
                             exposure_hdu, mask_hdu])
    hdu_list.writeto(f"data/{field}_SPIRE{get_band(filename)}_v{VERSION}.fits",
                     checksum=True)
    print(f"{field} / {get_band(filename)} processed...")

# H-ATLAS
hatlas_field_basenames = {
    "GAMA-09": "HATLAS_GAMA9_DR1_",
    "GAMA-12": "HATLAS_GAMA12_DR1_",
    "GAMA-15": "HATLAS_GAMA15_DR1_",
    "HATLAS-NGP": "HATLAS_NGP_DR2_",
    "HATLAS-SGP": "HATLAS_SGP_DR2_",
}

hatlas_coverage_maps = {
    "GAMA-12_500": "GAMA12-PLWmap-mosaic-20141007_coverage.fits",
    "GAMA-12_350": "GAMA12-PMWmap-mosaic-20141007_coverage.fits",
    "GAMA-12_250": "GAMA12-PSWmap-mosaic-20141007_coverage.fits",
    "GAMA-15_500": "GAMA15-PLWmap-mosaic-20130218_coverage.fits",
    "GAMA-15_350": "GAMA15-PMWmap-mosaic-20130218_coverage.fits",
    "GAMA-15_250": "GAMA15-PSWmap-mosaic-20130218_coverage.fits",
    "GAMA-09_500": "GAMA9-PLWmap-mosaic-20130218_coverage.fits",
    "GAMA-09_350": "GAMA9-PMWmap-mosaic-20130218_coverage.fits",
    "GAMA-09_250": "GAMA9-PSWmap-mosaic-20130218_coverage.fits",
    "HATLAS-NGP_500": "NGP-PLWmap-mosaic_MS-20131121-full_coverage.fits",
    "HATLAS-NGP_350": "NGP-PMWmap-mosaic_MS-20131121-full_coverage.fits",
    "HATLAS-NGP_250": "NGP-PSWmap-mosaic_MS-20131121-full_coverage.fits",
    "HATLAS-SGP_500": "SGP-PLWmap-mosaic_MS-full_coverage.fits",
    "HATLAS-SGP_350": "SGP-PMWmap-mosaic_MS-full_coverage.fits",
    "HATLAS-SGP_250": "SGP-PSWmap-mosaic_MS-full_coverage.fits",
}

for field, band in product(hatlas_field_basenames, ["250", "350", "500"]):
    basename = hatlas_field_basenames[field]

    obsids = sorted(list(all_obsids['ObsID'][all_obsids['field'] == field]))
    assert len(obsids) > 0

    image_map = fits.open(f"../dmu19_HATLAS/data/{basename}RAW{band}.FITS")
    image_hdu = fits.ImageHDU(header=image_map[0].header,
                              data=image_map[0].data)
    image_hdu.data -= np.nanmean(image_hdu.data)  # Background substration
    image_hdu.header['EXTNAME'] = "IMAGE"
    image_hdu.header['BUNIT'] = "Jy / beam"

    mask_map = fits.open(f"../dmu19_HATLAS/data/{basename}MASK{band}.FITS")
    mask_hdu = fits.ImageHDU(header=mask_map[0].header,
                             data=mask_map[0].data)
    # H-ATLAS mask is 0 for bad and 1 for good while we use the reverse for
    # HELP
    good_mask = mask_hdu.data == 1
    bad_mask = mask_hdu.data == 0
    mask_hdu.data[good_mask] = 0
    mask_hdu.data[bad_mask] = 1
    mask_hdu.header['EXTNAME'] = "MASK"

    # The maps contain the coverage and where observed in parallel mode.
    coverage_map_name = hatlas_coverage_maps[f"{field}_{band}"]
    exposure_map = fits.open(
        f"../dmu19_HATLAS/data/coverages/{coverage_map_name}")
    exposure_hdu = exposure_map[1]
    assert exposure_hdu.header['EXTNAME'] == "coverage"
    exposure_hdu.data *= 1 / 10.  # Conversion of coverage to exposure
    exposure_hdu.header['EXTNAME'] = "EXPOSURE"
    exposure_hdu.header['BUNIT'] = "s"
    exposure_hdu.header['QTTY____'] = "s"

    error_map = fits.open(f"../dmu19_HATLAS/data/{basename}SIGMA{band}.FITS")
    error_hdu = fits.ImageHDU(header=error_map[0].header,
                              data=error_map[0].data)
    error_hdu.header['EXTNAME'] = "ERROR"
    error_hdu.header['BUNIT'] = "Jy / beam"

    nebfilt_map = fits.open(f"../dmu19_HATLAS/data/{basename}BACKSUB{band}.FITS")
    nebfilt_hdu = fits.ImageHDU(header=nebfilt_map[0].header,
                                data=nebfilt_map[0].data)
    nebfilt_hdu.header['EXTNAME'] = "NEBFILT"
    nebfilt_hdu.header['BUNIT'] = "Jy / beam"

    primary_hdu = fits.PrimaryHDU()
    primary_hdu.header.append((
        "CREATOR", "Herschel Extragalactic Legacy Project"
    ))
    primary_hdu.header.append((
        "TIMESYS", "UTC", "All dates are in UTC time"
    ))
    primary_hdu.header.append((
        "DATE", datetime.now().replace(microsecond=0).isoformat(),
        "Date of file creation"
    ))
    primary_hdu.header.append((
        "VERSION", VERSION, "HELP product version"
    ))
    primary_hdu.header.append((
        "TELESCOP", "Herschel", "Name of the telescope"
    ))
    primary_hdu.header.append((
        "INSTRUME", "SPIRE", "Name of the instrument"
    ))
    primary_hdu.header.append((
        "FILTER", f"SPIRE-{band}", "Name of the filter"
    ))
    primary_hdu.header.append((
        "FIELD", field, "Name of the HELP field"
    ))
    for idx, obs_id in enumerate(obsids):
        keyword = "OBSID" + str(idx).zfill(3)
        primary_hdu.header.append((keyword, obs_id))

    hdu_list = fits.HDUList([primary_hdu, image_hdu, nebfilt_hdu, error_hdu,
                             exposure_hdu, mask_hdu])
    hdu_list.writeto(f"data/{field}_SPIRE{band}_v{VERSION}.fits",
                     checksum=True)
    print(f"{field} / {band} processed...")
