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.CmcUTMPositive;
import com.ruoyi.web.calculate.service.ICmcUTMPositiveService;
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;

/**
 * UTM正算服务实现
 * 
 * @author ruoyi
 * @date 2026-04-21
 */
@Service
public class CmcUTMPositiveServiceImpl implements ICmcUTMPositiveService 
{
    @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);

            // 使用若依的ExcelUtil工具类
            ExcelUtil<CmcUTMPositive> util = new ExcelUtil<CmcUTMPositive>(CmcUTMPositive.class);
            List<CmcUTMPositive> dataList = util.importExcel(file.getInputStream());

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

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

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

            String message = String.format("导入成功！共解析%d条数据", dataList.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 {
            org.apache.poi.ss.usermodel.Workbook workbook = org.apache.poi.ss.usermodel.WorkbookFactory.create(file.getInputStream());
            org.apache.poi.ss.usermodel.Sheet sheet = workbook.getSheetAt(0);
            org.apache.poi.ss.usermodel.Row headerRow = sheet.getRow(0); // 第一行为表头
            
            if (headerRow != null) {
                for (int i = 0; i < headerRow.getPhysicalNumberOfCells(); i++) {
                    org.apache.poi.ss.usermodel.Cell cell = headerRow.getCell(i);
                    if (cell != null) {
                        String cellValue = cell.getStringCellValue();
                        if (cellValue != null && !cellValue.trim().isEmpty()) {
                            columns.add(cellValue.trim());
                        }
                    }
                }
            }
            workbook.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return columns;
    }
    
    /**
     * 读取原始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<>();
                for (int j = 0; j < columns.size(); 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;
        }
    }

    /**
     * 执行UTM正算
     */
    @Override
    public List<CmcUTMPositive> calculateUTMPositive(List<CmcUTMPositive> dataList)
    {
        List<CmcUTMPositive> resultList = new ArrayList<>();
        for (CmcUTMPositive item : dataList) {
            try {
                CmcUTMPositive result = calculateUTMPositiveSingle(item);
                resultList.add(result);
            } catch (Exception e) {
                e.printStackTrace();
                // 计算失败时，返回带有错误信息的对象
                CmcUTMPositive errorResult = new CmcUTMPositive();
                errorResult.setPointNumber(item.getPointNumber());
                errorResult.setUtmX(-1.0);
                errorResult.setUtmY(-1.0);
                errorResult.setMeridianConvergence(-1.0);
                resultList.add(errorResult);
            }
        }

        return resultList;
    }
    
    /**
     * 单条UTM正算
     */
    private CmcUTMPositive calculateUTMPositiveSingle(CmcUTMPositive cmcUTMPositive)
    {
        try {
            initEllipsoidParams(cmcUTMPositive.getCoordinateSystem());

            double longitude = cmcUTMPositive.getLongitude();
            String longitudePosition = cmcUTMPositive.getLongitudePosition();
            String latitudePosition = cmcUTMPositive.getLatitudePosition();
            double latitude = cmcUTMPositive.getLatitude();
            Double band = cmcUTMPositive.getBand();
            double projectionHeight = cmcUTMPositive.getProjectionHeight();
            String angleFormat = cmcUTMPositive.getAngleFormat();
            double B = latitude * Math.PI / 180;
            double L = longitude;
            // 如果输入格式为度（degree），先转换为度分秒格式
            // if ("degree".equalsIgnoreCase(angleFormat)) {
            //     longitude = convertDegreeToDMS(longitude);
            //     latitude = convertDegreeToDMS(latitude);
            // }

            // 转换为弧度
            if ("dms".equalsIgnoreCase(angleFormat)) {
                B = convertDecimalDegrees(latitude) * Math.PI / 180;
                L = convertDecimalDegrees(longitude);
            }
           
            // 计算经差
            double l = ((30-band)*6+3-L) * Math.PI / 180;
            // 处理西经情况
            if ("东".equals(longitudePosition) || "E".equalsIgnoreCase(longitudePosition)) {
                l = (L-(band-30)*6+3) * Math.PI / 180;
            }
            double m = Math.cos(B) * l;
            double ep2 = 1 / Math.pow(1 - F, 2) - 1;
            double eta2 = ep2 * Math.cos(B) * Math.cos(B);

            // 计算卯酉圈曲率半径
            double N = (A + projectionHeight) / ((1 - F) * Math.sqrt(1 + eta2));
            // 计算子午线弧长
            double X = calculateMeridianArc(B, projectionHeight);
            // 计算Y坐标的系数
            double t = Math.tan(B);
            double t2 = t * t;
            double t4 = t2 * t2;
            double m2 = m * m;
            double m4 = m2 * m2;

            // 计算X坐标
            double x = X + N * Math.cos(B) * Math.cos(B) * t * l * l / 2 +
                    N * Math.pow(Math.cos(B), 4) * t * (5 - t * t + 9 * eta2 + 4 * eta2 * eta2) * Math.pow(l, 4) / 24 +
                    N * Math.pow(Math.cos(B), 6) * t * (61 - 58 * t2 + t4 + 270 * eta2 - 330 * eta2 * t2)
                            * Math.pow(l, 6) / 720;
            // l 
            double term1 = m;
            //  l³ 
            double term3 = (1 - t2 + eta2) * m2 * m / 6;
            //  l⁵ 
            double term5 = (5 - 18 * t2 + t4 + 14 * eta2 - 58 * eta2 * t2) * m4 * m / 120;
            //计算y坐标
            double y = N * (term1 + term3 + term5);

            // 计算子午线收敛角
            double meridianConvergence = t * (m + (1 + 3 * eta2 + 2 * eta2 * eta2) * m2 * m / 3 + (2 - t2) * m4 * m * 12 / 180);
            meridianConvergence = Math.toDegrees(meridianConvergence);
            meridianConvergence = decimalToDMS(meridianConvergence);
            x= latitudePosition.equals("N") || latitudePosition.equals("北") ? 0.9996 * x :10000000 - 0.9996 * x ;
            // 加入东偏
            y = 500000 + 0.9996 * y;
            
            cmcUTMPositive.setUtmX(x);
            cmcUTMPositive.setUtmY(y);
            cmcUTMPositive.setMeridianConvergence(meridianConvergence);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cmcUTMPositive;
    }
    
    //选择椭球体参数
    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();
    }

    /**
     * 计算子午线弧长
     */
    private double calculateMeridianArc(double B, double projectionHeight) {
        double e2 = E2;
        double e4 = E2 * E2;
        double e6 = e4 * E2;
        double e8 = e6 * E2;
        double e10 = e8 * E2;
        double d = A + projectionHeight;
        // 计算子午线弧长系数
        double a0 = (d - d * e2) * (1
                + 3.0 / 4.0 * e2
                + 45.0 / 64.0 * e4
                + 175.0 / 256.0 * e6
                + 11025.0 / 16384.0 * e8
                + 43659.0 / 65536.0 * e10);

        double a2 = (d - d * e2) * (3.0 / 4.0 * e2
                + 45.0 / 64.0 * e4
                + 175.0 / 256.0 * e6
                + 11025.0 / 16384.0 * e8
                + 43659.0 / 65536.0 * e10);

        double a4 = (d - d * e2) * (15.0 / 32.0 * e4
                + 175.0 / 384.0 * e6
                + 3675.0 / 8192.0 * e8
                + 14553.0 / 32768.0 * e10);

        double a6 = (d - d * e2) * (35.0 / 96.0 * e6
                + 735.0 / 2048.0 * e8
                + 14553.0 / 40960.0 * e10);

        double a8 = (d - d * e2) * (315.0 / 1024.0 * e8
                + 6237.0 / 20480.0 * e10);

        double a10 = (d - d * e2) * (693.0/2560.0)* e10;

        // 计算子午线弧长
        double sinB = Math.sin(B);
        double cosB = Math.cos(B);

        double X = a0 * B - (a2 * sinB + a4 * Math.pow(sinB, 3) + a6 * Math.pow(sinB, 5) + a8 * Math.pow(sinB, 7)
                + a10 * Math.pow(sinB, 9)) * cosB;
        return X;
    }
    
    /**
     * 将十进制度数转换为特定格式（
     * @param decimalDegrees 十进制度数
     * @return 转换后的值
     */
    public static double convertDecimalDegrees(double decimalDegrees) {
        // 度的整数部分
        double degrees = Math.floor(decimalDegrees + 1.0E-12);

        // 分的计算
        double minutes = Math.floor(100 * (decimalDegrees - degrees) + 1.0E-12) / 60;

        // 秒的计算
        double seconds = (decimalDegrees * 100 - Math.floor(decimalDegrees * 100 + 1.0E-12)) / 36;

        return degrees + minutes + seconds;
    }
    
    /**
     * 将十进制度数转换为度分秒格式数值（DD.MMSSssss）
     * 
     * @param decimalDegrees 十进制度数（如 103.373428195）
     */
    public static double decimalToDMS(double decimalDegrees) {
        // 1. 提取度
        double degrees = Math.floor(decimalDegrees + 1.0E-12);

        // 2. 提取分
        double minutesRaw = 60 * (decimalDegrees - degrees);
        double minutes = Math.floor(minutesRaw + 1.0E-12) / 100;

        // 3. 提取秒
        double secondsRaw = 60 * (60 * decimalDegrees - Math.floor(60 * decimalDegrees + 1.0E-12))+ 1.0E-12;
        double seconds = secondsRaw / 10000;

        return degrees + minutes + seconds;
    }
    
    /**
     * 将十进制度数转换为度分秒格式数值（DD.MMSSssss）
     * 与 calculateDegreeToDMSSingle 方法逻辑相同
     * 
     * @param decimalDegrees 十进制度数（如 103.373428195）
     * @return 度分秒格式数值（如 103.22240038）
     */
    private double convertDegreeToDMS(double decimalDegrees) {
        // 1. 提取度
        double degrees = Math.floor(decimalDegrees + 1.0E-13);
        
        // 2. 提取分
        double minutesRaw = 60 * (decimalDegrees - degrees);
        double minutes = Math.floor(minutesRaw + 1.0E-13) / 100;
        
        // 3. 提取秒
        double secondsRaw = 60 * (60 * decimalDegrees - Math.floor(60 * decimalDegrees + 1.0E-13)) + 1.0E-13;
        double seconds = secondsRaw / 10000;
        
        return degrees + minutes + seconds;
    }
}