package com.ruoyi.web.calculate.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.web.calculate.domain.CmcSpatialToGeodetic;
import com.ruoyi.web.calculate.service.ICmcSpatialToGeodeticService;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;

/**
 * 空间直角坐标转大地坐标服务实现
 * 
 * @author ruoyi
 * @date 2026-06-10
 */
@Service
public class CmcSpatialToGeodeticServiceImpl implements ICmcSpatialToGeodeticService {
    @Autowired
    // 扁率直接计算精度不够，在后面的椭球参数选择中用BigDecimal计算，下面的F是分母
    private static final double A_WGS84 = 6378137.0;
    private static final String F_WGS84 = "298.257223563";

    private static final double A_CGCS2000 = 6378137.0;
    private static final String F_CGCS2000 = "298.257222101";

    private static final double A_BJ54 = 6378245.0;
    private static final String F_BJ54 = "298.3";

    private static final double A_XA80 = 6378140.0;
    private static final String F_XA80 = "298.257";

    private static double A;
    private static double F;
    private static double E2;

    /**
     * 导入Excel数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult importExcelData(MultipartFile file) {
        try {
            // 获取Excel表头信息
            List<String> excelColumns = getExcelColumns(file);

            // 返回原始Excel数据格式（使用表头作为key）
            List<Map<String, Object>> rawData = readRawExcelData(file, excelColumns);

            // 检查是否有数据
            if (rawData == null || rawData.isEmpty()) {
                return AjaxResult.error("Excel文件中没有数据！");
            }

            // 构建返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("total", rawData.size());
            result.put("validCount", rawData.size());
            result.put("data", rawData);
            result.put("columns", excelColumns); // 返回Excel表头

            String message = String.format("导入成功！共解析%d条数据", rawData.size());
            return AjaxResult.success(message, result);

        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.error("导入失败：" + e.getMessage());
        }
    }
    
    /**
     * 获取Excel表头信息
     */
    private List<String> getExcelColumns(MultipartFile file) {
        List<String> columns = new ArrayList<>();
        try {
            Workbook workbook = WorkbookFactory.create(file.getInputStream());
            Sheet sheet = workbook.getSheetAt(0);
            Row headerRow = sheet.getRow(0); // 第一行为表头
            
            if (headerRow != null) {
                int lastCellNum = headerRow.getLastCellNum();
                for (int i = 0; i < lastCellNum; i++) {
                    Cell cell = headerRow.getCell(i);
                    String cellValue = null;
                    if (cell != null) {
                        cellValue = getCellStringValue(cell);
                    }
                    if (cellValue != null && !cellValue.trim().isEmpty()) {
                        columns.add(cellValue.trim());
                    } else {
                        columns.add("列" + (i + 1));
                    }
                }
            }
            workbook.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return columns;
    }
    
    /**
     * 获取单元格的字符串值（处理多种类型）
     */
    private String getCellStringValue(Cell cell) {
        if (cell == null) {
            return null;
        }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getLocalDateTimeCellValue().toString();
                } else {
                    double value = cell.getNumericCellValue();
                    if (value == Math.floor(value)) {
                        return String.valueOf((long) value);
                    }
                    return String.valueOf(value);
                }
            case BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case FORMULA:
                try {
                    return cell.getStringCellValue();
                } catch (Exception e) {
                    try {
                        return String.valueOf(cell.getNumericCellValue());
                    } catch (Exception ex) {
                        return cell.getCellFormula();
                    }
                }
            default:
                return null;
        }
    }
    
    /**
     * 读取原始Excel数据（使用表头作为key）
     */
    private List<Map<String, Object>> readRawExcelData(MultipartFile file, List<String> columns) {
        List<Map<String, Object>> dataList = new ArrayList<>();
        try {
            Workbook workbook = WorkbookFactory.create(file.getInputStream());
            Sheet sheet = workbook.getSheetAt(0);
            int rows = sheet.getLastRowNum();
            
            // 从第二行开始读取数据（第一行为表头）
            for (int i = 1; i <= rows; i++) {
                Row row = sheet.getRow(i);
                if (row == null || isRowEmpty(row)) {
                    continue;
                }
                
                Map<String, Object> rowMap = new HashMap<>();
                int lastCellNum = Math.min(row.getLastCellNum(), columns.size());
                for (int j = 0; j < lastCellNum; j++) {
                    Cell cell = row.getCell(j);
                    Object value = getCellValue(cell);
                    rowMap.put(columns.get(j), value);
                }
                dataList.add(rowMap);
            }
            workbook.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataList;
    }
    
    /**
     * 判断行是否为空
     */
    private boolean isRowEmpty(Row row) {
        if (row == null) {
            return true;
        }
        for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
            Cell cell = row.getCell(i);
            if (cell != null && cell.getCellType() != CellType.BLANK) {
                // 使用getCellValue方法处理多种类型，避免数字类型调用getStringCellValue抛出异常
                Object value = getCellValue(cell);
                if (value != null) {
                    String strValue = String.valueOf(value).trim();
                    if (!strValue.isEmpty()) {
                        return false;
                    }
                }
            }
        }
        return true;
    }
    
    /**
     * 获取单元格值
     */
    private Object getCellValue(Cell cell) {
        if (cell == null) {
            return null;
        }
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getLocalDateTimeCellValue();
                } else {
                    // 避免科学计数法
                    double value = cell.getNumericCellValue();
                    if (value == Math.floor(value)) {
                        return (long) value;
                    }
                    return value;
                }
            case BOOLEAN:
                return cell.getBooleanCellValue();
            case FORMULA:
                try {
                    return cell.getStringCellValue();
                } catch (Exception e) {
                    return cell.getNumericCellValue();
                }
            default:
                return null;
        }
    }

    /**
     * 执行空间直角坐标转大地坐标计算
     */
    @Override
    public List<CmcSpatialToGeodetic> calculateSpatialToGeodetic(List<CmcSpatialToGeodetic> dataList) {
        List<CmcSpatialToGeodetic> resultList = new ArrayList<>();
        for (CmcSpatialToGeodetic item : dataList) {
            try {
                CmcSpatialToGeodetic result = calculateSpatialToGeodeticSingle(item);
                resultList.add(result);
            } catch (Exception e) {
                e.printStackTrace();
                // 计算失败时，返回带有错误信息的对象
                CmcSpatialToGeodetic errorResult = new CmcSpatialToGeodetic();
                errorResult.setPointNumber(item.getPointNumber());
                errorResult.setLongitude(-1.0);
                errorResult.setLatitude(-1.0);
                errorResult.setHeight(-1.0);
                resultList.add(errorResult);
            }
        }

        return resultList;
    }

    /**
     * 单条空间直角坐标转大地坐标计算
     */
    private CmcSpatialToGeodetic calculateSpatialToGeodeticSingle(CmcSpatialToGeodetic cmcSpatialToGeodetic) {
        // 获取输入值
        String coordinateSystem = cmcSpatialToGeodetic.getCoordinateSystem();
        double spatialX = cmcSpatialToGeodetic.getSpatialX();
        Double spatialY = cmcSpatialToGeodetic.getSpatialY();
        double spatialZ = cmcSpatialToGeodetic.getSpatialZ();
        double projectionHeight = cmcSpatialToGeodetic.getProjectionHeight();
        String outputFormat = cmcSpatialToGeodetic.getOutputFormat();

        initEllipsoidParams(coordinateSystem);

        double a = A + projectionHeight;
        double L = Math.acos(spatialX / Math.sqrt(spatialX * spatialX + spatialY * spatialY));
        double L_degree = Math.toDegrees(L);
        double t = spatialZ * E2;
        double b = Math.asin(
                (spatialZ + t) / Math.sqrt(Math.pow(spatialX, 2) + Math.pow(spatialY, 2) + Math.pow(spatialZ + t, 2)));

        //循环计算
        double N=0;
        for (int i = 0; i < 8; i++) {
            N = a / Math.sqrt(1 - E2 * Math.pow(Math.sin(b), 2));
            t = E2 * N * Math.sin(b);
            b = Math.asin((spatialZ + t)
                    / Math.sqrt(Math.pow(spatialX, 2) + Math.pow(spatialY, 2) + Math.pow(spatialZ + t, 2)));
        }

        double B = Math.toDegrees(b);

        // 大地经度和大地纬度
        double longitude = L_degree;
        double latitude = B;
        
        // 如果输出格式为度分秒（dms），则转换为度分秒格式
        if ("dms".equalsIgnoreCase(outputFormat)) {
            longitude = convertDecimalDegrees(longitude);
            latitude = convertDecimalDegrees(latitude);
        }
        
        String longitudePosition = spatialY < 0 ? "W" : "E";
        String latitudePosition = spatialZ < 0 ? "S" : "N";
        double height = Math.sqrt(Math.pow(spatialX, 2) + Math.pow(spatialY, 2) + Math.pow(spatialZ + t, 2)) - N;
        cmcSpatialToGeodetic.setLongitude(longitude);
        cmcSpatialToGeodetic.setLatitude(latitude);
        cmcSpatialToGeodetic.setLongitudePosition(longitudePosition);
        cmcSpatialToGeodetic.setLatitudePosition(latitudePosition);
        cmcSpatialToGeodetic.setHeight(height);
        return cmcSpatialToGeodetic;
    }
    
    /**
     * 将十进制度数转换为度分秒格式数值（DD.MMSSssss）
     * 与高斯反算中相同的方法
     * 
     * @param decimalDegrees 十进制度数
     * @return 度分秒格式数值
     */
    private static double convertDecimalDegrees(double decimalDegrees) {
        final double PRECISION = 1.2e-12;
        final double SEC_PRECISION = 1e-11;

        // 提取度
        double degrees = Math.floor(decimalDegrees + PRECISION);
        // 提取分
        double minutesRaw = 60 * (decimalDegrees - degrees);
        double minutes = Math.floor(minutesRaw + PRECISION) / 100;
        // 提取秒（高精度）
        double secondsRaw = (decimalDegrees * 60 - Math.floor(decimalDegrees * 60 + PRECISION)) * 60;
        double seconds = Math.floor((secondsRaw + SEC_PRECISION) * 1000000) / 10000000000.0;
        
        return degrees + minutes + seconds;
    }

    // 选择椭球体参数
    private void initEllipsoidParams(String coordinateSystem) {
        String F_str;
        if ("CGCS2000".equalsIgnoreCase(coordinateSystem) || "国家2000".equalsIgnoreCase(coordinateSystem)
                || "2000".equalsIgnoreCase(coordinateSystem)) {
            A = A_CGCS2000;
            F_str = F_CGCS2000;
        } else if ("1954".equalsIgnoreCase(coordinateSystem) || "北京54".equalsIgnoreCase(coordinateSystem)
                || "Beijing54".equalsIgnoreCase(coordinateSystem)
                || "54".equalsIgnoreCase(coordinateSystem)) {
            A = A_BJ54;
            F_str = F_BJ54;
        } else if ("1980".equalsIgnoreCase(coordinateSystem) || "西安80".equalsIgnoreCase(coordinateSystem)
                || "Xian80".equalsIgnoreCase(coordinateSystem)
                || "80".equalsIgnoreCase(coordinateSystem)) {
            A = A_XA80;
            F_str = F_XA80;
        } else {
            A = A_WGS84;
            F_str = F_WGS84;
        }
        BigDecimal one = BigDecimal.ONE;
        BigDecimal divisor = new BigDecimal(F_str);
        BigDecimal db_F = one.divide(divisor, 20, RoundingMode.HALF_UP);

        // 计算 2 * F
        BigDecimal two = new BigDecimal("2");
        BigDecimal twoF = two.multiply(db_F);

        // 计算 F * F
        BigDecimal F_squared = db_F.multiply(db_F);

        // 计算 e2 = 2*F - F*F
        BigDecimal e2 = twoF.subtract(F_squared);
        E2 = e2.doubleValue();
        F = db_F.doubleValue();
    }
}
