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.CmcUTMBandChange;
import com.ruoyi.web.calculate.service.ICmcUTMBandChangeService;
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-06-05
 */
@Service
public class CmcUTMBandChangeServicelmpl implements ICmcUTMBandChangeService {
    @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<CmcUTMBandChange> util = new ExcelUtil<CmcUTMBandChange>(CmcUTMBandChange.class);
            List<CmcUTMBandChange> 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) {
                int lastCellNum = headerRow.getLastCellNum();
                for (int i = 0; i < lastCellNum; i++) {
                    org.apache.poi.ss.usermodel.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(org.apache.poi.ss.usermodel.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;
        }
    }

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

        return resultList;
    }

    /**
     * 单条UTM换带计算
     */
    private CmcUTMBandChange calculateUTMBandChangeSingle(CmcUTMBandChange cmcUTMBandChange) {
        try {
            initEllipsoidParams(cmcUTMBandChange.getCoordinateSystem());
            // 以大地坐标（纬度B，经度L）为桥梁，通过连续应用UTM反算和正算公式来实现换带。
            double utmX = cmcUTMBandChange.getUtmX(); // UTM X坐标
            double utmY = cmcUTMBandChange.getUtmY(); // UTM Y坐标
            String longitudePosition = cmcUTMBandChange.getLongitudePosition(); // 经度位置（东/西）
            String latitudePosition = cmcUTMBandChange.getLatitudePosition(); // 纬度位置（北/南）
            double projectionHeight = cmcUTMBandChange.getProjectionHeight(); // 投影面高程
            double band = cmcUTMBandChange.getBand();
            double newband = cmcUTMBandChange.getNewBand();
            
            // UTM带宽固定为6度
            int bandwidth = 6;

            // UTM坐标处理：南半球X坐标需要特殊处理
            // 处理x坐标（南半球特殊处理）
            double x = latitudePosition.equals("S") ? (10000000 - utmX) / 0.9996 : utmX / 0.9996;

            // 处理y坐标（减去东偏500000）
            double y0 = utmY > 1.0E+7 ? (utmY - (int) Math.floor(utmY / 1.0E+6) * 1.0E+6 - 500000) / 0.9996 + 500000
                    : (utmY - 500000)
                            / 0.9996 + 500000;
            double y = y0 > 1.0E+7 ? y0 - (int) Math.floor(y0 / 1.0E+6) * 1.0E+6 - 500000 : y0 - 500000;
            
            // 反算：UTM平面坐标 -> 大地坐标
            double bf = calculateBf(x, projectionHeight);
            double tf = Math.tan(bf);
            double ep2 = 1 / Math.pow(1 - F, 2) - 1;
            double etaf2 = ep2 * Math.pow(Math.cos(bf), 2);
            double Vf = Math.sqrt(1 + etaf2);
            double c = (A + projectionHeight) / (1 - F);
            double n = y * Vf / c;
            // 大地纬度 B
            double B = calculateB(bf, tf, etaf2, n);

            // 大地经度L
            double L = calculateL(bf, tf, etaf2, n, longitudePosition, bandwidth, band);
            
            // 将大地坐标转换为弧度
            double b = B / 180 * Math.PI;
            double l = "E".equals(longitudePosition)
                    ? (L-(newband-30) * 6 + 3)/180*Math.PI
                    : ((30-newband) * 6 + 3 - L)/180*Math.PI;
              
            // 将大地坐标代入目标带，投影计算新的平面坐标。
            double m = Math.cos(b) * l;
            double cosb2 = Math.pow(Math.cos(b), 2);
            double eta2 = new BigDecimal(ep2).multiply(new BigDecimal(cosb2)).doubleValue();
            // 计算卯酉圈曲率半径
            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坐标（UTM正算公式）
            double xnow = 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;
            
            // 计算Y坐标的各项
            double term1 = m;
            double term3 = (1 - t2 + eta2) * m2 * m / 6;
            double term5 = (5 - 18 * t2 + t4 + 14 * eta2 - 58 * eta2 * t2) * m4 * m / 120;
            
            // 计算y坐标（加上500,000米东偏移）
            double ynow = 0.9996 * (N * (term1 + term3 + term5)) + 500000;
            
            // 设置结果（南半球需要特殊处理）
            cmcUTMBandChange.setNewUtmX(latitudePosition.equals("S") ? 10000000 - 0.9996 *xnow : 0.9996 * xnow);
            cmcUTMBandChange.setNewUtmY(ynow);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cmcUTMBandChange;
    }

    // 选择椭球体参数
    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();
    }

    // 计算bf
    private double calculateBf(double x, 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 b = x / a0;
        double sinB = Math.sin(b);
        double cosB = Math.cos(b);
        double f = -(a2 * sinB + a4 * Math.pow(sinB, 3) + a6 * Math.pow(sinB, 5) + a8 * Math.pow(sinB, 7)) * cosB;

        for (int i = 0; i < 9; i++) {
            b = (x - f) / a0;
            sinB = Math.sin(b);
            cosB = Math.cos(b);
            f = -(a2 * sinB + a4 * Math.pow(sinB, 3) + a6 * Math.pow(sinB, 5) + a8 * Math.pow(sinB, 7)) * cosB;
            i++;
        }
        return b;
    }

    // 计算B
    private double calculateB(double bf, double tf, double etaf2, double n) {
        double n2 = n * n;
        double n4 = n2 * n2;
        double n6 = n4 * n2;
        // 计算底点纬度对应的值
        double term1 = bf * 180 / Math.PI; // 转为度
        // 计算括号内的各项
        double bracket = 90 * n2
                - 7.5 * (5 + 3 * tf * tf + etaf2 - 9 * etaf2 * tf * tf) * n4
                + 0.25 * (61 + 90 * tf * tf + 45 * tf * tf * tf * tf) * n6;
        // 计算第二项
        double term2 = (1 + etaf2) / Math.PI * tf * bracket;
        // 最终纬度 B
        double B = term1 - term2;
        return B;
    }

    // 计算l
    private double calculateL(double bf, double tf, double etaf2, double n, String longitudePosition,
            double bandwidth, double band) {
        double n2 = n * n;
        double n3 = n2 * n;
        double n5 = n3 * n2;
        double tf2 = tf * tf;
        double tf4 = tf2 * tf2;
        // 括号内计算
        double bracket2 = 180 * n
                - 30 * (1 + 2 * tf2 + etaf2) * n3
                + 1.5 * (5 + 28 * tf2 + 24 * tf4) * n5;
        double l = 1 / Math.PI / Math.cos(bf) * bracket2;

        double L = "W".equals(longitudePosition)
                ? (30 - band) * 6 + 3 - l
                : l + (band - 30) * 6 - 3;
        return L;
    }

    /**
     * 计算子午线弧长
     */
    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;
    }
}
