import copy
import json
from pathlib import Path
import unittest
from review_data import Settings,bodies,candidate_pairs,spatial_groups,hull,association,arm_evidence,Observer,strict_people,verify_baseline
from engines import Engine
from test_engines import person,row,EngineTest
from review_draw import group_layer,affected_orders,image_panel
from PIL import Image
import render_review


class ReviewTest(unittest.TestCase):
    def setUp(self):self.cfg=Settings()
    def test_chain_group(self):
        r=row(0);r['persons']=[person(150,1),person(300,2,1),person(450,3,2)]
        bs,_=bodies(r,self.cfg);pairs=candidate_pairs(bs,self.cfg)
        self.assertEqual(len(pairs),2)
        groups=spatial_groups(bs,pairs,1000,800)
        self.assertEqual(groups[0]['orders'],[0,1,2])
    def test_separate_groups(self):
        r=row(0);r['width']=2000;r['persons']=[person(150,1),person(300,2,1),person(1000,3,2),person(1150,4,3)]
        bs,_=bodies(r,self.cfg);g=spatial_groups(bs,candidate_pairs(bs,self.cfg),2000,800)
        self.assertEqual([x['orders'] for x in g],[[0,1],[2,3]])
    def test_no_singleton(self):
        r=row(0);r['persons']=r['persons'][:1]
        bs,_=bodies(r,self.cfg);self.assertEqual(spatial_groups(bs,[],1000,800),[])
    def test_hull(self):
        self.assertEqual(len(hull([(0,0),(10,0),(10,10),(0,10),(5,5)])),4)
    def test_overlap_does_not_merge(self):
        g=[dict(polygon=[(0,0),(60,0),(60,60),(0,60)]),dict(polygon=[(40,40),(90,40),(90,90),(40,90)])]
        _,pixels=group_layer(Image.new('RGB',(100,100)),g)
        self.assertGreater(pixels,0);self.assertEqual(len(g),2)
    def pair(self,ids,token='S1'):
        return dict(token=token,ids=ids,orders=[0,1])
    def old(self,ids=(1,2),time=0):
        return {'S1':dict(time=time,bodies=[dict(tid=x,center=[i*10,20]) for i,x in enumerate(ids)])}
    def test_id_change(self):
        a=association(self.pair([3,2]),self.old(),.1,self.cfg)
        self.assertEqual(a['kind'],'T_CHANGE_B_CONTINUES');self.assertEqual(affected_orders(a),[0])
    def test_id_swap(self):
        a=association(self.pair([2,1]),self.old(),.1,self.cfg)
        self.assertEqual(a['kind'],'T_SWAP_B_CONTINUES');self.assertEqual(affected_orders(a),[0,1])
    def test_same_t_reset(self):
        a=association(self.pair([1,2],'S2'),self.old(),.1,self.cfg)
        self.assertEqual(a['kind'],'B_RESET_SAME_T_PAIR');self.assertTrue(a['difference'])
    def test_partner_new_pair_not_disagreement(self):
        a=association(self.pair([1,3],'S2'),self.old(),.1,self.cfg)
        self.assertFalse(a['difference'])
    def test_gap_not_disagreement(self):
        for token in ('S1','S2'):
            a=association(self.pair([1,2],token),self.old(),.3,self.cfg)
            self.assertEqual(a['kind'],'OBSERVATION_GAP');self.assertFalse(a['difference'])
    def test_no_t_uncomparable(self):
        a=association(self.pair([None,2]),self.old(),.1,self.cfg)
        self.assertEqual(a['kind'],'UNCOMPARABLE_T_MISSING');self.assertFalse(a['difference'])
    def test_numbers_not_compared(self):
        a=association(self.pair([1,2]),self.old(),.1,self.cfg)
        self.assertEqual(a['kind'],'AGREEMENT')
    def test_levels(self):
        arm=dict(actor=0,side='R',valid=True,relative_motion=.18,toward=.1,approach=.04,hand_distance=1.25,strike=True,dynamic=False)
        self.assertEqual(arm_evidence(arm,[],self.cfg)['level'],2)
        self.assertEqual(arm_evidence(arm,['STRIKE'],self.cfg)['level'],3)
        arm.update(strike=False,toward=.09)
        self.assertEqual(arm_evidence(arm,[],self.cfg)['level'],1)
        arm['valid']=False
        self.assertEqual(arm_evidence(arm,[],self.cfg)['level'],0)
    def test_joint_visibility(self):
        r=row(0);r['persons'][0]['keypoints'][9][2]=.099999;r['persons'][0]['keypoints'][10][2]=.1
        out=strict_people(r,{1:(255,0,0),2:(0,255,0)})
        self.assertEqual(out[0]['joint_states'][9],'BELOW');self.assertEqual(out[0]['joint_states'][10],'HIGH')
        r['persons'][0]['score']=.249999
        self.assertEqual(len(strict_people(r,{1:(255,0,0),2:(0,255,0)})),1)
    def test_observer_does_not_change_core(self):
        reference=Engine('B');observed=Engine('B');observer=Observer(self.cfg)
        for i in range(35):
            r=row(i,'strike',switch=True);prepared=bodies(r,self.cfg);saved=copy.deepcopy(prepared)
            before=observer.capture(observed);out=observed.step(r,prepared);out['displayed']=True
            expected=reference.step(r,prepared);expected['displayed']=True
            view=observer.explain(r,prepared,out,before)
            self.assertEqual(out,expected);self.assertEqual(prepared,saved)
            self.assertEqual(view['source_frame'],r['source_frame'])
    def test_shared_person_pairs_drawn(self):
        r=row(0);r['persons']=[person(150,1),person(300,2,1),person(450,3,2)]
        e=Engine('B');o=Observer(self.cfg);p=bodies(r,self.cfg);before=o.capture(e);out=e.step(r,p);out['displayed']=True
        v=o.explain(r,p,out,before)
        for x in v['pairs']:x['level']=1
        panel,meta=image_panel(Image.new('RGB',(1000,800)),r,v,{1:(255,0,0),2:(0,255,0),3:(0,0,255)},'motion')
        self.assertEqual(len(meta['visible_pairs']),2);self.assertEqual(len(set(meta['visible_pairs'])),2)
    def test_render_smoke(self):
        e=Engine('B');o=Observer(self.cfg);palette={1:(255,0,0),2:(0,255,0),3:(0,0,255)}
        render_review.clip='Synthetic_only'
        for i in range(12):
            r=row(i,'strike',switch=True);p=bodies(r,self.cfg);before=o.capture(e);out=e.step(r,p);out['displayed']=True
            v=o.explain(r,p,out,before)
        for kind in ('groups','motion','association'):
            im,meta=render_review.page(Image.new('RGB',(1000,800)),r,v,palette,kind,0,1)
            self.assertEqual(im.size,(3200,1800))
    def test_hold_lost_correspondence(self):
        e=Engine('B');o=Observer(self.cfg)
        for i in range(12):
            r=row(i,'strike');p=bodies(r,self.cfg);before=o.capture(e);out=e.step(r,p);out['displayed']=True
            o.explain(r,p,out,before)
        r=row(12);r['persons']=[];p=bodies(r,self.cfg);before=o.capture(e);out=e.step(r,p);out['displayed']=True
        v=o.explain(r,p,out,before);self.assertEqual(v['held_support'],[])


if __name__=='__main__':
    verify_baseline()
    unittest.main(verbosity=2)
