def _construct_CCD(hdulist, headers, filename):
    SEG_DATASIZE = headers['SEG_DATASIZE']
    SEG_SIZE = headers['SEG_SIZE']

    # Traverse the amplifier headers
    new_data = np.zeros(shape=(headers['DATASIZE']['y'], headers['DATASIZE']['x']), dtype=np.float32)
    for amps_row in headers['BOUNDARY']:
        for amp in amps_row:
            hdu = hdulist[amp['index']]
            data_sec = getCoord((hdu.header)['DATASEC'])
            det_sec = getCoord((hdu.header)['DETSEC'])
            data_slice_x = convert_slice(data_sec['start_X'], data_sec['end_X'])
            data_slice_y = convert_slice(data_sec['start_Y'], data_sec['end_Y'])
            slice_x = convert_slice(det_sec['start_X'], det_sec['end_X'])
            slice_y = convert_slice(det_sec['start_Y'], det_sec['end_Y'])
            new_data[slice_y, slice_x] = (hdu.data)[data_slice_y, data_slice_x]
    new_hdu = fits.PrimaryHDU(new_data)
    new_hdulist = fits.HDUList([new_hdu])
    new_hdulist.writeto(filename+"_mosaicked_trimmed"+".fits", clobber=True)

    new_data = np.zeros(shape=(headers['DETSIZE']['y'], headers['DETSIZE']['x']), dtype=np.float32)
    for amps_row in headers['BOUNDARY_OVERSCAN']:
        for amp in amps_row:
            data_slice_x = slice(SEG_SIZE['x']-1, None, -1) if amp['reverse_slice']['x'] else slice(0, SEG_SIZE['x'])
            data_slice_y = slice(SEG_SIZE['y']-1, None, -1) if amp['reverse_slice']['y'] else slice(0, SEG_SIZE['y'])
            start_X = (amp['x']//SEG_SIZE['x'])*SEG_SIZE['x']
            start_Y = (amp['y']//SEG_SIZE['y'])*SEG_SIZE['y']
            slice_x = slice(start_X, start_X+SEG_SIZE['x'])
            slice_y = slice(start_Y, start_Y+SEG_SIZE['y'])
            new_data[slice_y, slice_x] = (hdulist[amp['index']].data)[data_slice_y, data_slice_x]
    new_hdu = fits.PrimaryHDU(new_data)
    new_hdulist = fits.HDUList([new_hdu])
    new_hdulist.writeto(filename+"_mosaicked_untrimmed"+".fits", clobber=True)
    return ["Created single-extension FITS file."]
def _get_Header_Info(imgHDUs):
    if not imgHDUs:
        raise RuntimeError("The given file is not a multi-extension FITS image.")
    # Get info from the first amplifier header.
    first_seg = imgHDUs[0]
    seg_dimension = [first_seg["NAXIS1"], first_seg["NAXIS2"]]
    DETSIZE = getDim(getCoord(first_seg["DETSIZE"]))  # Assume 'DETSIZE' is same for all segments/amplifiers.
    # Sanity Check
    if (DETSIZE[0] % seg_dimension[0] != 0) and (DETSIZE[1] % seg_dimension[1] != 0):
        raise ValueError("Incorrect segment/amplifier dimension.")
    num_X, num_Y = DETSIZE[0] // seg_dimension[0], DETSIZE[1] // seg_dimension[1]
    num_amps = num_X * num_Y

    boundary = [[{} for x in range(num_X)] for y in range(num_Y)]
    boundary_overscan = [[{} for x in range(num_X)] for y in range(num_Y)]
    # Traverse the list of headers
    for i in range(num_amps):
        header = imgHDUs[i]
        seg_dimension = [header["NAXIS1"], header["NAXIS2"]]
        seg_detsec = getCoord(header["DETSEC"])
        seg_datasec = getCoord(header["DATASEC"])
        seg_datadim = getDim(seg_datasec)
        seg_bias_Size = getDim(getCoord(header["BIASSEC"]))
        # Segment/amplifier coordinates in a CCD.
        # Format: amplifier[Y][X] (origin at top left corner).
        # +----+----+----+----+----+----+----+----+
        # | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 |
        # +----+----+----+----+----+----+----+----+
        # | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
        # +----+----+----+----+----+----+----+----+
        seg_X, seg_Y = (seg_detsec["start_X"]) // seg_datadim[0], (seg_detsec["start_Y"]) // seg_datadim[1]
        seg_Y_converted = num_Y - 1 - seg_Y
        boundary[seg_Y_converted][seg_X] = convert_to_Box(seg_detsec)
        # Condition about X & Y slicing in segment data.
        is_Slice_Reverse = check_Reverse_Slicing(seg_detsec, seg_datasec)
        # Add correct offset for each segment.
        boundary[seg_Y_converted][seg_X]["EXTNAME"] = header["EXTNAME"]
        boundary_overscan[seg_Y_converted][seg_X] = {
            # NOTE: DS9 box region start from top left corner
            "x": seg_X * seg_dimension[0],
            "y": seg_Y * seg_dimension[1] + seg_datadim[1] - 1,
            "width": seg_datadim[0],
            "height": seg_datadim[1],
            "EXTNAME": header["EXTNAME"],
        }
        boundary_overscan[seg_Y_converted][seg_X]["x"] += (
            seg_bias_Size[0] if is_Slice_Reverse["x"] else (min(seg_datasec["start_X"], seg_datasec["end_X"]))
        )
        boundary_overscan[seg_Y_converted][seg_X]["y"] += (
            (seg_dimension[1] - seg_datadim[1]) if is_Slice_Reverse["y"] else 0
        )
        boundary[seg_Y_converted][seg_X]["index"] = boundary_overscan[seg_Y_converted][seg_X]["index"] = i
        boundary[seg_Y_converted][seg_X]["reverse_slice"] = boundary_overscan[seg_Y_converted][seg_X][
            "reverse_slice"
        ] = is_Slice_Reverse

    return {
        "DETSIZE": {"x": DETSIZE[0], "y": DETSIZE[1]},
        "DATASIZE": {"x": num_X * seg_datadim[0], "y": num_Y * seg_datadim[1]},
        "NUM_AMPS": {"num": num_amps, "x": num_X, "y": num_Y},
        "SEG_SIZE": {"x": seg_dimension[0], "y": seg_dimension[1]},
        "SEG_DATASIZE": {"x": seg_datadim[0], "y": seg_datadim[1]},
        "BOUNDARY": boundary,
        "BOUNDARY_OVERSCAN": boundary_overscan,
    }