from __future__ import annotations

import typing

try:
    import ezdxf
    HAS_EZDXF = True
except ImportError:
    HAS_EZDXF = False

from ..base import BaseCodec, BaseFileIterable


class DXFIterable(BaseFileIterable):
    datamode = 'binary'

    def __init__(self, filename: str = None, stream: typing.IO = None, codec: BaseCodec = None,
                 mode: str = 'r', options: dict = None):
        if options is None:
            options = {}
        if not HAS_EZDXF:
            raise ImportError("DXF support requires 'ezdxf' package")
        
        self.options = options
        super().__init__(filename, stream, codec=codec, binary=True, mode=mode, options=options)
        self.reset()

    def reset(self):
        """Reset iterable"""
        super().reset()
        self.pos = 0
        if self.mode == 'r':
            # ezdxf requires a filename
            if self.filename:
                try:
                    self.doc = ezdxf.readfile(self.filename)
                except Exception as e:
                    raise ValueError(f"Failed to read DXF file: {e}")
            elif hasattr(self.fobj, 'name'):
                try:
                    self.doc = ezdxf.readfile(self.fobj.name)
                except Exception as e:
                    raise ValueError(f"Failed to read DXF file: {e}")
            else:
                # Try to read from stream
                try:
                    self.doc = ezdxf.read(self.fobj)
                except Exception as e:
                    raise ValueError(f"Failed to read DXF from stream: {e}")
            
            self.iterator = self.__iterator()
        else:
            raise NotImplementedError("DXF writing not yet supported")

    def __iterator(self):
        """Iterator for reading DXF entities"""
        # Iterate through all entities in modelspace
        msp = self.doc.modelspace()
        
        for entity in msp:
            entity_dict = {
                'dxftype': entity.dxftype(),
                'layer': entity.dxf.layer if hasattr(entity.dxf, 'layer') else None,
                'color': entity.dxf.color if hasattr(entity.dxf, 'color') else None,
            }
            
            # Extract geometry based on entity type
            if entity.dxftype() == 'LINE':
                entity_dict['start'] = tuple(entity.dxf.start) if hasattr(entity.dxf, 'start') else None
                entity_dict['end'] = tuple(entity.dxf.end) if hasattr(entity.dxf, 'end') else None
            elif entity.dxftype() == 'CIRCLE':
                entity_dict['center'] = tuple(entity.dxf.center) if hasattr(entity.dxf, 'center') else None
                entity_dict['radius'] = entity.dxf.radius if hasattr(entity.dxf, 'radius') else None
            elif entity.dxftype() == 'ARC':
                entity_dict['center'] = tuple(entity.dxf.center) if hasattr(entity.dxf, 'center') else None
                entity_dict['radius'] = entity.dxf.radius if hasattr(entity.dxf, 'radius') else None
                entity_dict['start_angle'] = entity.dxf.start_angle if hasattr(entity.dxf, 'start_angle') else None
                entity_dict['end_angle'] = entity.dxf.end_angle if hasattr(entity.dxf, 'end_angle') else None
            elif entity.dxftype() == 'POINT':
                entity_dict['location'] = tuple(entity.dxf.location) if hasattr(entity.dxf, 'location') else None
            elif entity.dxftype() == 'TEXT':
                entity_dict['text'] = entity.dxf.text if hasattr(entity.dxf, 'text') else None
                entity_dict['insert'] = tuple(entity.dxf.insert) if hasattr(entity.dxf, 'insert') else None
                entity_dict['height'] = entity.dxf.height if hasattr(entity.dxf, 'height') else None
            elif entity.dxftype() == 'LWPOLYLINE':
                # Lightweight polyline
                try:
                    entity_dict['points'] = [tuple(p) for p in entity.get_points('xy')]
                    entity_dict['closed'] = entity.closed
                except Exception:
                    entity_dict['points'] = None
            elif entity.dxftype() == 'POLYLINE':
                try:
                    entity_dict['points'] = [tuple(v.dxf.location) for v in entity.vertices]
                    entity_dict['closed'] = entity.is_closed
                except Exception:
                    entity_dict['points'] = None
            
            # Add handle for reference
            if hasattr(entity.dxf, 'handle'):
                entity_dict['handle'] = entity.dxf.handle
            
            yield entity_dict

    @staticmethod
    def id() -> str:
        return 'dxf'

    @staticmethod
    def is_flatonly() -> bool:
        return False

    @staticmethod
    def has_totals():
        return True

    def totals(self):
        if hasattr(self, 'doc'):
            try:
                msp = self.doc.modelspace()
                return len(list(msp))
            except Exception:
                return 0
        return 0

    def close(self):
        if hasattr(self, 'doc'):
            self.doc.close()
        super().close()

    def read(self) -> dict:
        entity = next(self.iterator)
        self.pos += 1
        return entity

    def read_bulk(self, num: int = 10) -> list[dict]:
        chunk = []
        for _n in range(0, num):
            try:
                chunk.append(self.read())
            except StopIteration:
                break
        return chunk
