import json
import io
import unittest

from pycozmo.anim_encoder import AnimClips


class TestClips(unittest.TestCase):

    def test_no_clips(self):
        data = json.loads("""
{
  "clips": []
}       
        """)
        # Load from JSON
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips), 0)

        # Save to FB
        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        # Load from FB
        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips), 0)

        # Save to JSON
        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestClip(unittest.TestCase):

    def test_empty_clip(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips), 1)
        self.assertEqual(clips.clips[0].name, "test")
        self.assertEqual(len(clips.clips[0].keyframes), 0)

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips), 1)
        self.assertEqual(clips.clips[0].name, "test")
        self.assertEqual(len(clips.clips[0].keyframes), 0)

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestHeadAngle(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [
          {
            "triggerTime_ms": 1,
            "durationTime_ms": 2,
            "angle_deg": 3,
            "angleVariability_deg": 4
          }
        ],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        self.assertEqual(keyframe.angle_deg, 3)
        self.assertEqual(keyframe.variability_deg, 4)

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        self.assertEqual(keyframe.angle_deg, 3)
        self.assertEqual(keyframe.variability_deg, 4)

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestLiftHeight(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [
          {
            "triggerTime_ms": 1,
            "durationTime_ms": 2,
            "height_mm": 3,
            "heightVariability_mm": 4
          }
        ],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        self.assertEqual(keyframe.height_mm, 3)
        self.assertEqual(keyframe.variability_mm, 4)

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        self.assertEqual(keyframe.height_mm, 3)
        self.assertEqual(keyframe.variability_mm, 4)

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestRecordHeading(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [
          {
            "triggerTime_ms": 1
          }
        ],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestBodyMotion(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [
          {
            "triggerTime_ms": 1,
            "durationTime_ms": 2,
            "radius_mm": "3.0",
            "speed": 4
          }
        ],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        self.assertEqual(keyframe.radius_mm, 3)
        self.assertEqual(keyframe.speed, 4)

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        self.assertEqual(keyframe.radius_mm, 3)
        self.assertEqual(keyframe.speed, 4)

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestBackpackLights(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [
          {
            "triggerTime_ms": 1,
            "durationTime_ms": 2,
            "Left": [
              1.0,
              2.0,
              3.0,
              4.0
            ],
            "Right": [
              1.0,
              2.0,
              3.0,
              4.0
            ],
            "Front": [
              1.0,
              2.0,
              3.0,
              4.0
            ],
            "Middle": [
              1.0,
              2.0,
              3.0,
              4.0
            ],
            "Back": [
              1.0,
              2.0,
              3.0,
              4.0
            ]
          }
        ],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        for led in (keyframe.left, keyframe.front, keyframe.middle, keyframe.back, keyframe.right):
            self.assertEqual(led.red, 1)
            self.assertEqual(led.green, 2)
            self.assertEqual(led.blue, 3)
            self.assertEqual(led.ir, 4)

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        for led in (keyframe.left, keyframe.front, keyframe.middle, keyframe.back, keyframe.right):
            self.assertEqual(led.red, 1)
            self.assertEqual(led.green, 2)
            self.assertEqual(led.blue, 3)
            self.assertEqual(led.ir, 4)

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestTurnToRecordedHeading(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": [
          {
            "triggerTime_ms": 1,
            "durationTime_ms": 2,
            "offset_deg": 3,
            "speed_degPerSec": 4,
            "accel_degPerSec2": 5,
            "decel_degPerSec2": 6,
            "tolerance_deg": 7,
            "numHalfRevs": 8,
            "useShortestDir": true
          }
        ]
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        self.assertEqual(keyframe.offset_deg, 3)
        self.assertEqual(keyframe.speed_deg_per_sec, 4)
        self.assertEqual(keyframe.accel_deg_per_sec_2, 5)
        self.assertEqual(keyframe.decel_deg_per_sec_2, 6)
        self.assertEqual(keyframe.tolerance_deg, 7)
        self.assertEqual(keyframe.num_half_revs, 8)
        self.assertTrue(keyframe.use_shortest_dir)

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.duration_ms, 2)
        self.assertEqual(keyframe.offset_deg, 3)
        self.assertEqual(keyframe.speed_deg_per_sec, 4)
        self.assertEqual(keyframe.accel_deg_per_sec_2, 5)
        self.assertEqual(keyframe.decel_deg_per_sec_2, 6)
        self.assertEqual(keyframe.tolerance_deg, 7)
        self.assertEqual(keyframe.num_half_revs, 8)
        self.assertTrue(keyframe.use_shortest_dir)

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestFaceAnimation(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [
          {
            "triggerTime_ms": 1,
            "animName": "test"
          }
        ],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.anim_name, "test")

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.anim_name, "test")

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestProceduralFace(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [
          {
            "triggerTime_ms": 1,
            "faceAngle": 2.0,
            "faceCenterX": 3.0,
            "faceCenterY": 4.0,
            "faceScaleX": 5.0,
            "faceScaleY": 6.0,
            "leftEye": [
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              9.0,
              10.0,
              11.0,
              12.0,
              13.0,
              14.0,
              15.0,
              16.0,
              17.0,
              18.0,
              19.0
            ],
            "rightEye": [
              1.0,
              2.0,
              3.0,
              4.0,
              5.0,
              6.0,
              7.0,
              8.0,
              9.0,
              10.0,
              11.0,
              12.0,
              13.0,
              14.0,
              15.0,
              16.0,
              17.0,
              18.0,
              19.0
            ]
          }
        ],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.angle, 2.0)
        self.assertEqual(keyframe.center_x, 3.0)
        self.assertEqual(keyframe.center_y, 4.0)
        self.assertEqual(keyframe.scale_x, 5.0)
        self.assertEqual(keyframe.scale_y, 6.0)
        for eye in (keyframe.left_eye, keyframe.right_eye):
            for i in range(19):
                self.assertEqual(eye[i], float(i + 1))

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.angle, 2.0)
        self.assertEqual(keyframe.center_x, 3.0)
        self.assertEqual(keyframe.center_y, 4.0)
        self.assertEqual(keyframe.scale_x, 5.0)
        self.assertEqual(keyframe.scale_y, 6.0)
        for eye in (keyframe.left_eye, keyframe.right_eye):
            for i in range(19):
                self.assertEqual(eye[i], float(i + 1))

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestRobotAudio(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [
          {
            "triggerTime_ms": 1,
            "audioEventId": [
              2
            ],
            "volume": 3.0,
            "probability": [
              4.0
            ],
            "hasAlts": true
          }
        ],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.audio_event_ids[0], 2)
        self.assertEqual(keyframe.volume, 3.0)
        self.assertEqual(keyframe.probabilities[0], 4.0)
        self.assertTrue(keyframe.has_alts)

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.audio_event_ids[0], 2)
        self.assertEqual(keyframe.volume, 3.0)
        self.assertEqual(keyframe.probabilities[0], 4.0)
        self.assertTrue(keyframe.has_alts)

        data2 = clips.to_dict()
        self.assertEqual(data, data2)


class TestEvent(unittest.TestCase):

    def test_single(self):
        data = json.loads("""
{
  "clips": [
    {
      "Name": "test",
      "keyframes": {
        "LiftHeightKeyFrame": [],
        "ProceduralFaceKeyFrame": [],
        "HeadAngleKeyFrame": [],
        "RobotAudioKeyFrame": [],
        "BackpackLightsKeyFrame": [],
        "FaceAnimationKeyFrame": [],
        "EventKeyFrame": [
          {
            "triggerTime_ms": 1,
            "event_id": "test"
          }
        ],
        "BodyMotionKeyFrame": [],
        "RecordHeadingKeyFrame": [],
        "TurnToRecordedHeadingKeyFrame": []
      }
    }
  ]
}      
        """)
        clips = AnimClips.from_dict(data)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.event_id, "test")

        f = io.BytesIO()
        clips.to_fb_stream(f)
        f.seek(0, io.SEEK_SET)

        clips = clips.from_fb_stream(f)
        self.assertEqual(len(clips.clips[0].keyframes), 1)
        keyframe = clips.clips[0].keyframes[0]
        self.assertEqual(keyframe.trigger_time_ms, 1)
        self.assertEqual(keyframe.event_id, "test")

        data2 = clips.to_dict()
        self.assertEqual(data, data2)