import dataclasses
import json
from dataclasses import dataclass
from enum import Enum, auto
from pathlib import Path
from typing import Annotated, get_type_hints

# from attr import dataclass

COMMIT_KEY = 'COMMIT'


class RfMeta:
    @classmethod
    def p2a(clz):
        gh = get_type_hints(clz, include_extras=True)
        return {k: gh[k].__metadata__[0] for k in gh}

    @classmethod
    def p2t(clz):
        return get_type_hints(clz)

    @classmethod
    def a2p(clz):
        return {v: k for k, v in clz.p2a().items()}

    @property
    def name(self):
        p2a = self.p2a()
        p2t = self.p2t()
        a2p = self.a2p()
        arr = []
        if COMMIT_KEY in a2p:
            cp = a2p[COMMIT_KEY]
            del p2a[cp]
            arr.append(f'{self.__getattribute__(cp)}')
        for p in p2a:
            t = p2t[p]
            v = self.__getattribute__(p)
            if issubclass(t, Enum):
                vs = v.name
            elif issubclass(t, tuple):
                vs = f'({' '.join([str(vv) for vv in v])})'
            else:
                vs = str(v)
            if v is not None:
                arr.append(f'{p2a[p]}={vs}')
        return ",".join(arr)

    @property
    def filename(self):
        return self.name + '.bin'

    @classmethod
    def from_path(clz, path: Path | str):
        path = Path(path)
        if not path.exists():
            raise FileNotFoundError(path)
        if path.is_file():
            return clz.from_name(path.stem)
        elif path.is_dir():
            return clz.from_name(path.name)
        else:
            raise FileNotFoundError

    @classmethod
    def from_name(clz, name: str):
        if Path(name).suffix == '.zip':
            name = Path(name).stem
        p2t = clz.p2t()
        a2p = clz.a2p()
        c = clz()
        sp = name.split(',')
        if COMMIT_KEY in a2p:
            c.__setattr__(a2p[COMMIT_KEY], sp.pop(0))
        for pv in sp:
            a, v = pv.split('=')
            p = a2p[a]
            t = p2t[p]
            if issubclass(t, Enum):
                c.__setattr__(p, t[v])
            elif issubclass(t, tuple):
                c.__setattr__(p, tuple(int(i) for i in v[1:-1].split(' ')))
            else:
                c.__setattr__(p, t(v))

        return c


@dataclass
class RfFrameMeta(RfMeta):
    encoder: Annotated[int, 'E'] = None
    sequence_id: Annotated[int, 'S'] = None  # test3
    robot_x: Annotated[int, 'X'] = None
    robot_y: Annotated[int, 'Y'] = None
    robot_z: Annotated[int, 'Z'] = None

    robot_roll: Annotated[int, 'RX'] = None
    robot_pitch: Annotated[int, 'RY'] = None
    robot_yal: Annotated[int, 'RZ'] = None

    robot_force_x: Annotated[int, 'FX'] = None
    robot_force_y: Annotated[int, 'FY'] = None
    robot_force_z: Annotated[int, 'FZ'] = None

    robot_force_roll: Annotated[int, 'FRX'] = None
    robot_force_pitch: Annotated[int, 'FRY'] = None
    robot_force_yal: Annotated[int, 'FRZ'] = None

    blake2b: Annotated[str, 'B2B'] = None


@dataclass
class RfSequenceMeta(RfMeta):
    class RfSequenceMode(Enum):
        PWI = auto()
        TFM = auto()

    commit: Annotated[str, COMMIT_KEY] = None
    shape: Annotated[tuple, 'S'] = None
    mode: Annotated[RfSequenceMode, 'M'] = RfSequenceMode.PWI
    us: Annotated[int, 'U'] = None

    def prod(self):
        res = 1
        for i in self.shape:
            res *= int(i)
        return res

    def d(self):
        return self.shape.__len__()


if __name__ == '__main__':
    # r = RfSequenceMeta.from_name('asdasd1,S=(64 1501),M=PWI,U=30')
    # print(r)
    # print(r.json_str)
    #
    # r2 = RfSequenceMeta.from_json_str(r.json_str)
    # print(r2)

    r = RfFrameMeta.from_name('E=1,S=123')
    print(r)
    print(r.json_str)

    r2 = RfFrameMeta.from_json_str(r.json_str)
    print(r2)
