import numpy as np
import cv2
from glob import glob
from os.path import join
import os

#
from utils import read_json,get_lines_chessboard,Timer,write_intri
from vis_base import plot_points2d


def read_chess(chessname):
    data = read_json(chessname)
    k3d = np.array(data['keypoints3d'], dtype=np.float32)
    k2d = np.array(data['keypoints2d'], dtype=np.float32)
    if k2d[:, -1].sum() < 0.01:
        return False, k2d, k3d
    return True, k2d, k3d


def calib_intri_share(path, step):
    camnames = sorted(os.listdir(join(path, 'images')))
    imagenames = sorted(glob(join(path, 'images', '*', '*.jpg')))
    chessnames = sorted(glob(join(path, 'chessboard', '*', '*.json')))
    k3ds_, k2ds_, imgs = [], [], []
    valid_idx = []
    for i, chessname in enumerate(chessnames):
        flag, k2d, k3d = read_chess(chessname)
        k3ds_.append(k3d)
        k2ds_.append(k2d)
        if not flag:
            continue
        valid_idx.append(i)
    MAX_ERROR_PIXEL = 1.
    lines, line_cols = get_lines_chessboard()
    valid_idx = valid_idx[::step]
    len_valid = len(valid_idx)
    cameras = {}
    while True:
        # sample
        imgs = [imagenames[i] for i in valid_idx]
        k3ds = [k3ds_[i] for i in valid_idx]
        k2ds = [np.ascontiguousarray(k2ds_[i][:, :-1]) for i in valid_idx]
        gray = cv2.imread(imgs[0], 0)
        print('>> Detect {:3d} frames'.format(len(valid_idx)))
        with Timer('calibrate'):
                ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(
                k3ds, k2ds, gray.shape[::-1], None, None)
        with Timer('check'):
            removed = []
            for i in range(len(imgs)):
                img = cv2.imread(imgs[i])
                points2d_repro, _ = cv2.projectPoints(
                    k3ds[i], rvecs[i], tvecs[i], K, dist)
                points2d_repro = points2d_repro.squeeze()
                points2d = k2ds_[valid_idx[i]]
                err = np.linalg.norm(points2d_repro - points2d[:, :2], axis=1).mean()
                plot_points2d(img, points2d_repro, lines, col=(0, 0, 255), lw=1, putText=False)
                plot_points2d(img, points2d, lines, lw=1, putText=False)
                print(imgs[i], err)
                # cv2.imshow('vis', img)
                # cv2.waitKey(0)
                if err > MAX_ERROR_PIXEL:
                    removed.append(i)
            for i in removed[::-1]:
                valid_idx.pop(i)
        if len_valid == len(valid_idx) or not args.remove:
            print(K)
            print(dist)
            for cam in camnames:
                cameras[cam] = {
                    'K': K,
                    'dist': dist  # dist: (1, 5)
                }
            break
        len_valid = len(valid_idx)
    write_intri(join(path, 'output', 'intri.yml'), cameras)


def calib_intri(path, step):
    camnames = sorted(os.listdir(join(path, 'images')))
    cameras = {}

    for ic, cam in enumerate(camnames):
        if '.' in cam:
            continue
        imagenames = sorted(glob(join(path, 'images', cam, '*.jpg')))
        chessnames = sorted(glob(join(path, 'chessboard', cam, '*.json')))
        k3ds, k2ds = [], []
        for chessname in chessnames[::step]:
            flag, k2d, k3d = read_chess(chessname)
            if not flag:continue
            k3ds.append(k3d)
            k2ds.append(np.ascontiguousarray(k2d[:, :-1]))
        gray = cv2.imread(imagenames[0], 0)
        print('>> Detect {}/{:3d} frames'.format(cam, len(k2ds)))
        with Timer('calibrate'):
            ret, K, dist, rvecs, tvecs = cv2.calibrateCamera(
                k3ds, k2ds, gray.shape[::-1], None, None)
            cameras[cam] = {
                'K': K,
                'dist': dist  # dist: (1, 5)
            }
    write_intri(join(path, 'output', 'intri.yml'), cameras)



