from collections import OrderedDict
import cv2 as cv
import numpy as np

from .stitching_error import StitchingError


class CameraAdjuster:
    """https://docs.opencv.org/4.x/d5/d56/classcv_1_1detail_1_1BundleAdjusterBase.html"""  # noqa

    CAMERA_ADJUSTER_CHOICES = OrderedDict()
    CAMERA_ADJUSTER_CHOICES['ray'] = cv.detail_BundleAdjusterRay
    CAMERA_ADJUSTER_CHOICES['reproj'] = cv.detail_BundleAdjusterReproj
    CAMERA_ADJUSTER_CHOICES['affine'] = cv.detail_BundleAdjusterAffinePartial
    CAMERA_ADJUSTER_CHOICES['no'] = cv.detail_NoBundleAdjuster

    DEFAULT_CAMERA_ADJUSTER = list(CAMERA_ADJUSTER_CHOICES.keys())[0]
    DEFAULT_REFINEMENT_MASK = "xxxxx"

    def __init__(self,
                 adjuster=DEFAULT_CAMERA_ADJUSTER,
                 refinement_mask=DEFAULT_REFINEMENT_MASK):

        self.adjuster = CameraAdjuster.CAMERA_ADJUSTER_CHOICES[adjuster]()
        self.set_refinement_mask(refinement_mask)
        self.adjuster.setConfThresh(1)

    def set_refinement_mask(self, refinement_mask):
        mask_matrix = np.zeros((3, 3), np.uint8)
        if refinement_mask[0] == 'x':
            mask_matrix[0, 0] = 1
        if refinement_mask[1] == 'x':
            mask_matrix[0, 1] = 1
        if refinement_mask[2] == 'x':
            mask_matrix[0, 2] = 1
        if refinement_mask[3] == 'x':
            mask_matrix[1, 1] = 1
        if refinement_mask[4] == 'x':
            mask_matrix[1, 2] = 1
        self.adjuster.setRefinementMask(mask_matrix)

    def adjust(self, features, pairwise_matches, estimated_cameras):
        b, cameras = self.adjuster.apply(features,
                                         pairwise_matches,
                                         estimated_cameras)
        if not b:
            raise StitchingError("Camera parameters adjusting failed.")

        return cameras
