# -*- coding: utf-8 -*-
"""
model_extractor.py - 从 SAP2000 提取几何数据

从 SAP2000 模型中提取单元几何信息，转换为标准的 Model3D 对象
"""

from typing import List, Optional
import math
from .element_geometry import Model3D, FrameElement3D, CableElement3D, Point3D


class ModelExtractor:
    """SAP2000 模型几何提取器"""
    
    def __init__(self, sap_model, unit_scale: float = 0.001):
        """
        Args:
            sap_model: SAP2000 的 SapModel 对象
            unit_scale: 单位缩放系数（默认 0.001 = mm -> m）
        """
        self.model = sap_model
        self.unit_scale = unit_scale
    
    def extract_frame_elements(
        self, 
        frame_names: List[str] = None,
        group_name: str = None
    ) -> Model3D:
        """
        提取框架单元几何
        
        Args:
            frame_names: 要提取的框架单元名称列表，None 表示全部
            group_name: 按组过滤
            
        Returns:
            Model3D 对象
            
        Example:
            extractor = ModelExtractor(sap_model)
            model_3d = extractor.extract_frame_elements()
            model_3d.to_json("model.json")
        """
        model_3d = Model3D(model_name="SAP2000_Frame_Model")
        
        # 获取框架单元列表
        if frame_names is None:
            if group_name:
                ret = self.model.GroupDef.GetAssignments(group_name, 0, [], [])
                if isinstance(ret, (list, tuple)) and len(ret) >= 3:
                    obj_types = list(ret[1]) if ret[1] else []
                    obj_names = list(ret[2]) if ret[2] else []
                    # 过滤出框架单元 (type=2)
                    frame_names = [
                        obj_names[i] for i in range(len(obj_types))
                        if obj_types[i] == 2
                    ]
                else:
                    frame_names = []
            else:
                ret = self.model.FrameObj.GetNameList(0, [])
                if isinstance(ret, (list, tuple)) and len(ret) >= 2 and ret[-1] == 0:
                    frame_names = list(ret[1]) if ret[1] else []
                else:
                    frame_names = []
        
        print(f"提取 {len(frame_names)} 个框架单元...")
        
        for frame_name in frame_names:
            try:
                # 获取端点
                ret = self.model.FrameObj.GetPoints(str(frame_name), "", "")
                if not isinstance(ret, (list, tuple)) or len(ret) < 2:
                    continue
                point_i_name = ret[0]
                point_j_name = ret[1]
                
                # 获取端点坐标
                ret_i = self.model.PointObj.GetCoordCartesian(point_i_name, 0, 0, 0)
                ret_j = self.model.PointObj.GetCoordCartesian(point_j_name, 0, 0, 0)
                
                if not isinstance(ret_i, (list, tuple)) or not isinstance(ret_j, (list, tuple)):
                    continue
                
                # 应用单位缩放（mm -> m）
                point_i = Point3D(
                    x=ret_i[0] * self.unit_scale, 
                    y=ret_i[1] * self.unit_scale, 
                    z=ret_i[2] * self.unit_scale
                )
                point_j = Point3D(
                    x=ret_j[0] * self.unit_scale, 
                    y=ret_j[1] * self.unit_scale, 
                    z=ret_j[2] * self.unit_scale
                )
                
                # 获取截面
                ret = self.model.FrameObj.GetSection(str(frame_name))
                section_name = ret[0] if isinstance(ret, (list, tuple)) and len(ret) > 0 else ""
                
                # 获取截面类型和参数（也需要缩放）
                section_type, section_params = self._get_section_info(section_name)
                # 缩放截面参数
                section_params = {k: v * self.unit_scale for k, v in section_params.items()}
                
                # 获取材料（某些版本可能没有此方法）
                try:
                    ret = self.model.FrameObj.GetMaterial(str(frame_name))
                    material = ret[0] if isinstance(ret, (list, tuple)) and len(ret) > 0 else ""
                except (AttributeError, Exception):
                    material = ""
                
                # 获取组
                ret = self.model.FrameObj.GetGroupAssign(str(frame_name), 0, [])
                groups = list(ret[1]) if isinstance(ret, (list, tuple)) and len(ret) >= 2 and ret[1] else []
                group = groups[0] if groups else ""
                
                # 创建框架单元对象
                frame_elem = FrameElement3D(
                    name=frame_name,
                    point_i=point_i,
                    point_j=point_j,
                    section_name=section_name,
                    section_type=section_type,
                    section_params=section_params,
                    material=material,
                    group=group,
                )
                
                model_3d.add_element(frame_elem)
                
            except Exception as e:
                print(f"警告: 提取框架 '{frame_name}' 失败: {e}")
                continue
        
        print(f"✓ 成功提取 {len(model_3d.elements)} 个框架单元")
        return model_3d
    
    def extract_cable_elements(
        self,
        cable_names: List[str] = None,
        group_name: str = None
    ) -> Model3D:
        """
        提取索单元几何
        
        Args:
            cable_names: 要提取的索单元名称列表，None 表示全部
            group_name: 按组过滤
            
        Returns:
            Model3D 对象
        """
        model_3d = Model3D(model_name="SAP2000_Cable_Model")
        
        # 获取索单元列表
        if cable_names is None:
            if group_name:
                ret = self.model.GroupDef.GetAssignments(group_name, 0, [], [])
                if isinstance(ret, (list, tuple)) and len(ret) >= 3:
                    obj_types = list(ret[1]) if ret[1] else []
                    obj_names = list(ret[2]) if ret[2] else []
                    # 过滤出索单元 (type=3)
                    cable_names = [
                        obj_names[i] for i in range(len(obj_types))
                        if obj_types[i] == 3
                    ]
                else:
                    cable_names = []
            else:
                ret = self.model.CableObj.GetNameList(0, [])
                if isinstance(ret, (list, tuple)) and len(ret) >= 2 and ret[-1] == 0:
                    cable_names = list(ret[1]) if ret[1] else []
                else:
                    cable_names = []
        
        print(f"提取 {len(cable_names)} 个索单元...")
        
        for cable_name in cable_names:
            try:
                # 获取端点
                ret = self.model.CableObj.GetPoints(str(cable_name), "", "")
                if not isinstance(ret, (list, tuple)) or len(ret) < 2:
                    continue
                point_i_name = ret[0]
                point_j_name = ret[1]
                
                # 获取端点坐标
                ret_i = self.model.PointObj.GetCoordCartesian(point_i_name, 0, 0, 0)
                ret_j = self.model.PointObj.GetCoordCartesian(point_j_name, 0, 0, 0)
                
                if not isinstance(ret_i, (list, tuple)) or not isinstance(ret_j, (list, tuple)):
                    continue
                
                # 应用单位缩放（mm -> m）
                point_i = Point3D(
                    x=ret_i[0] * self.unit_scale, 
                    y=ret_i[1] * self.unit_scale, 
                    z=ret_i[2] * self.unit_scale
                )
                point_j = Point3D(
                    x=ret_j[0] * self.unit_scale, 
                    y=ret_j[1] * self.unit_scale, 
                    z=ret_j[2] * self.unit_scale
                )
                
                # 获取截面
                ret = self.model.CableObj.GetProperty(str(cable_name), "")
                section_name = ret[0] if isinstance(ret, (list, tuple)) and len(ret) > 0 else ""
                
                # 获取截面属性
                ret = self.model.PropCable.GetProp(section_name, "", 0, 0)
                material = ""
                area = 0.0
                diameter = 0.0
                
                if isinstance(ret, (list, tuple)) and len(ret) >= 2:
                    material = ret[0] or ""
                    area_mm2 = float(ret[1]) if ret[1] else 0
                    # 面积也需要缩放：mm² -> m²
                    area = area_mm2 * (self.unit_scale ** 2)
                    # 根据面积计算等效直径（假设圆形）
                    if area > 0:
                        diameter = 2 * (area / 3.14159265359) ** 0.5
                    else:
                        diameter = 0.01 * self.unit_scale  # 默认 10mm，也要缩放
                
                # 获取组
                ret = self.model.CableObj.GetGroupAssign(str(cable_name), 0, [])
                groups = list(ret[1]) if isinstance(ret, (list, tuple)) and len(ret) >= 2 and ret[1] else []
                group = groups[0] if groups else ""
                
                # 创建索单元对象
                cable_elem = CableElement3D(
                    name=cable_name,
                    point_i=point_i,
                    point_j=point_j,
                    section_name=section_name,
                    material=material,
                    group=group,
                    diameter=diameter,
                    area=area,
                    section_type="Circle",  # 索截面默认为圆形
                    section_params={"diameter": diameter}  # 截面参数
                )
                
                model_3d.add_element(cable_elem)
                
            except Exception as e:
                print(f"警告: 提取索单元 '{cable_name}' 失败: {e}")
                continue
        
        print(f"✓ 成功提取 {len(model_3d.elements)} 个索单元")
        return model_3d
    
    def extract_all_elements(self, group_name: str = None) -> Model3D:
        """
        提取所有单元（框架 + 索）
        
        Args:
            group_name: 按组过滤
            
        Returns:
            Model3D 对象
        """
        model_3d = Model3D(model_name="SAP2000_Complete_Model")
        
        # 提取框架单元
        frame_model = self.extract_frame_elements(group_name=group_name)
        model_3d.elements.extend(frame_model.elements)
        
        # 提取索单元
        cable_model = self.extract_cable_elements(group_name=group_name)
        model_3d.elements.extend(cable_model.elements)
        
        print(f"✓ 总共提取 {len(model_3d.elements)} 个单元")
        return model_3d
    
    def _get_section_info(self, section_name: str) -> tuple:
        """
        获取截面类型和参数
        
        Returns:
            (section_type, section_params)
        """
        try:
            # 确保 section_name 是字符串
            section_name = str(section_name)
            
            # 获取截面类型
            ret = self.model.PropFrame.GetTypeOAPI(section_name)
            if not isinstance(ret, (list, tuple)) or len(ret) < 2 or ret[-1] != 0:
                return ("Unknown", {})
            
            type_val = ret[0]
            
            # 圆形截面 (9)
            if type_val == 9:
                ret = self.model.PropFrame.GetCircle(section_name)
                if isinstance(ret, (list, tuple)) and ret[-1] == 0 and len(ret) >= 3:
                    return ("Circle", {"diameter": ret[2]})
            
            # 矩形截面 (8)
            elif type_val == 8:
                ret = self.model.PropFrame.GetRectangle(section_name)
                if isinstance(ret, (list, tuple)) and ret[-1] == 0 and len(ret) >= 4:
                    return ("Rect", {"height": ret[2], "width": ret[3]})
            
            # 圆管截面 (7)
            elif type_val == 7:
                ret = self.model.PropFrame.GetPipe(section_name)
                if isinstance(ret, (list, tuple)) and ret[-1] == 0 and len(ret) >= 4:
                    return ("Pipe", {
                        "outer_diameter": ret[2],
                        "wall_thickness": ret[3]
                    })
            
            # 箱形截面 (6)
            elif type_val == 6:
                ret = self.model.PropFrame.GetTube_1(section_name)
                if isinstance(ret, (list, tuple)) and ret[-1] == 0 and len(ret) >= 6:
                    return ("Box", {
                        "height": ret[2],
                        "width": ret[3],
                        "flange_thickness": ret[4],
                        "web_thickness": ret[5]
                    })
            
            # 工字钢截面 (1)
            elif type_val == 1:
                ret = self.model.PropFrame.GetISection_1(section_name)
                if isinstance(ret, (list, tuple)) and ret[-1] == 0 and len(ret) >= 8:
                    return ("I", {
                        "height": ret[2],
                        "top_width": ret[3],
                        "flange_thickness": ret[4],
                        "web_thickness": ret[5],
                        "bottom_width": ret[6],
                        "bottom_flange_thickness": ret[7]
                    })
            
            # 槽钢截面 (2)
            elif type_val == 2:
                ret = self.model.PropFrame.GetChannel_2(section_name)
                if isinstance(ret, (list, tuple)) and ret[-1] == 0 and len(ret) >= 7:
                    return ("Channel", {
                        "height": ret[2],
                        "width": ret[3],
                        "flange_thickness": ret[4],
                        "web_thickness": ret[5],
                        "mirror": ret[6]
                    })
            
            # T型钢截面 (3)
            elif type_val == 3:
                ret = self.model.PropFrame.GetTee_1(section_name)
                if isinstance(ret, (list, tuple)) and ret[-1] == 0 and len(ret) >= 7:
                    return ("Tee", {
                        "height": ret[2],
                        "width": ret[3],
                        "flange_thickness": ret[4],
                        "web_thickness": ret[5],
                        "mirror": ret[6]
                    })
            
            # 角钢截面 (4)
            elif type_val == 4:
                ret = self.model.PropFrame.GetAngle_1(section_name)
                if isinstance(ret, (list, tuple)) and ret[-1] == 0 and len(ret) >= 6:
                    return ("Angle", {
                        "height": ret[2],
                        "width": ret[3],
                        "flange_thickness": ret[4],
                        "web_thickness": ret[5]
                    })
            
            # 默认返回未知类型
            return ("Unknown", {})
            
        except Exception as e:
            print(f"警告: 获取截面 '{section_name}' 信息失败: {e}")
            return ("Unknown", {})
