package com.sw.mes.issue.service.impl;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSON;
import com.sw.mes.core.response.ResponsePage;
import com.sw.mes.core.utils.StringUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sw.mes.base.workinprocess.entity.PlanProcessingOrder;
import com.sw.mes.base.workinprocess.service.PlanProcessingOrderService;
import com.sw.mes.base.workinprocess.service.WipStorageService;
import com.sw.mes.core.base.BaseServiceImpl;
import com.sw.mes.core.context.BaseContextHandler;
import com.sw.mes.core.enums.EnumValuesFactory;
import com.sw.mes.core.utils.DateUtil;
import com.sw.mes.inspect.entity.YbInspectData;
import com.sw.mes.inspect.service.YbInspectDataService;
import com.sw.mes.issue.dao.YbIssueDao;
import com.sw.mes.issue.dao.YbIssueDetialDao;
import com.sw.mes.issue.dto.McsYbIssueDetailDto;
import com.sw.mes.issue.dto.McsYbIssueDto;
import com.sw.mes.issue.dto.YbIssueDetialDto;
import com.sw.mes.issue.dto.YbIssueDto;
import com.sw.mes.issue.dto.YbIssueToDetailDto;
import com.sw.mes.issue.entity.YbIssue;
import com.sw.mes.issue.entity.YbIssueDetial;
import com.sw.mes.issue.entity.YbIssueRecord;
import com.sw.mes.issue.service.YbIssueDetialService;
import com.sw.mes.issue.service.YbIssueRecordService;
import com.sw.mes.issue.service.YbIssueService;
import com.sw.mes.wms.dto.ReportWmsRecordDto;
import com.sw.mes.wms.service.GenerateReportWms;
import com.sw.mes.wms.service.ReportWmsRecordService;

import lombok.extern.slf4j.Slf4j;

/**
 * @Author @Description：
 * @Date: Create in
 * @Modified By:
 */
@Slf4j
@Service
@Transactional
public class YbIssueServiceImpl extends BaseServiceImpl<YbIssue> implements YbIssueService {

    YbIssueDao dao;

    @Autowired
    YbIssueDetialDao detialDao;

    @Autowired
    YbIssueDetialService ybIssueDetialService;

    @Autowired
    YbIssueRecordService ybIssueRecordService;

    @Autowired
    private WipStorageService wipStorageService;

    @Autowired
    private PlanProcessingOrderService planProcessingOrderService;

    @Autowired
    private ReportWmsRecordService reportWmsRecordService;

    @Autowired
    private YbInspectDataService ybInspectDataService;

    @Autowired
    private GenerateReportWms generateReportWms;

    @Autowired
    RedisTemplate<String, Object> redisTemplate;

    @Autowired
    @Qualifier(value = "yBMesThreadPoolTaskExecutor")
    private ThreadPoolTaskExecutor ybMesThreadPoolExecutor;

    @Autowired
    public void setDao(YbIssueDao dao) {
        super.setBaseDao(dao);
        this.dao = dao;
    }

    @Override
    public void saveYbIssue(YbIssueDto ybIssueDto) {

        ybIssueDto.setTrayNo(trayNo());
        ybIssueDto.setNum(0);
        // 当前登录人
        ybIssueDto.setName(BaseContextHandler.getUsername());
        ybIssueDto.setCurrentSite(EnumValuesFactory.WipStorageCode.圆棒检验.getValue());
        ybIssueDto.setStatus(EnumValuesFactory.IssueStatus.待出库.getKey());
        ybIssueDto.setTime(new Date());
        YbIssue ybIssue = new YbIssue();
        BeanUtils.copyProperties(ybIssueDto, ybIssue);
        this.saveOrUpdate(ybIssue);
        // 详情
        if (null != ybIssueDto.getDetialDtoList() && ybIssueDto.getDetialDtoList().size() > 0) {
            for (YbIssueDetialDto dto : ybIssueDto.getDetialDtoList()) {
                YbIssueDetial ybIssueDetial = new YbIssueDetial();
                // 等级为A1,A2的才能新增
                YbInspectData bySectionNo = ybInspectDataService.findBySectionNo(dto.getCrystalNo());
                if (null != bySectionNo) {
//                    if(null == bySectionNo.getProductType() || !bySectionNo.getGradeType().equals("正常"))
//                    {
//                        throw new RuntimeException("该单晶编号:" +dto.getCrystalNo() + "圆棒未评级");
//                    }
                    if (null == bySectionNo.getProductType() || null == bySectionNo.getProductLevel()) {
                        throw new RuntimeException("该单晶编号:" + dto.getCrystalNo() + "圆棒未评级");
                    }

                    //========================
                    // 增加卡控 ，防止晶棒类型（回收料、非回收料）与发料去向不匹配
                    if (StringUtils.isEmpty(bySectionNo.getGradeType())){
                        throw new RuntimeException("圆棒评级分类为空！");
                    }
                    if (dto.getDestination().equals("回收料")) {
                        if (!bySectionNo.getGradeType().contains("回收料")){
                            throw new RuntimeException(dto.getCrystalNo()+":该晶棒评级分类为:"+bySectionNo.getProductLevel()+",请选择去向：开方");
                        }
                    }else if (dto.getDestination().equals("开方")){
                        if (bySectionNo.getGradeType().contains("回收料")){
                            throw new RuntimeException(dto.getCrystalNo()+":该晶棒评级分类为:"+bySectionNo.getProductLevel()+",请选择去向：回收料");
                        }
                    }
                    //===========================
                }
                // 所有单晶编号
                String crystalNo = ybIssueDetialService.findByCrystalNoAndIssueId(dto.getCrystalNo());
                // 发料为 回收料 工单状态改为已作废
                if (dto.getDestination().equals("回收料")) {
                    PlanProcessingOrder planProcessingOrder = planProcessingOrderService.findByCode(dto.getCrystalNo());
                    if (planProcessingOrder != null) {
                        planProcessingOrder.setStatus(EnumValuesFactory.PlanProcessingOrderStatus.已作废.getKey());
                        planProcessingOrderService.saveOrUpdate(planProcessingOrder);
                    }
                }
                if (null == crystalNo) {
                    BeanUtils.copyProperties(dto, ybIssueDetial);
                    ybIssueDetial.setIssueId(ybIssue.getId());
                    ybIssueDetialService.saveOrUpdate(ybIssueDetial);
                    updatePlanProcessingOrder(ybIssueDetial.getCrystalNo(), dto.getDestination());
                    ybIssueRecordService.deleteErrorRecord(ybIssueDetial.getCrystalNo());
                } else if (null != crystalNo && null != dto.getId() && null != dto.getIssueId()) {
                    BeanUtils.copyProperties(dto, ybIssueDetial);
                    ybIssueDetialService.saveOrUpdate(ybIssueDetial);
                } else {
                    throw new RuntimeException("该单晶编号:" + crystalNo + "不是加工中的状态！");
                }
            }
        }
        // 保存数量
        List<YbIssueDetial> list = ybIssueDetialService.findByIssueId(ybIssue.getId());
        if (CollectionUtils.isNotEmpty(list))
            ybIssue.setNum(list.size());
        this.saveOrUpdate(ybIssue);

    }

    @Override
    public ResponsePage<YbIssueDto> getPageList(String trayNo, Integer status, String startTime, String endTime, String crystalNo, Pageable pageable) {
        Page<Map<String, Object>> pageList = dao.getPageList(trayNo, status, startTime, endTime, crystalNo, pageable);
        List<YbIssueDto> ybIssueDtos = JSON.parseArray(JSON.toJSONString(pageList.getContent()), YbIssueDto.class);
        return new ResponsePage<>(ybIssueDtos,pageList.getTotalElements());
    }

    private List<ReportWmsRecordDto> buildReportWmsRecordDto(List<YbIssueDetial> list) {
        List<ReportWmsRecordDto> dtoList = new ArrayList<>();
        for (YbIssueDetial ybIssueDetial : list) {
            YbInspectData data = ybInspectDataService.findBySectionNo(ybIssueDetial.getCrystalNo());
            ReportWmsRecordDto dto = new ReportWmsRecordDto();
            dto.setNo(data.getSectionNo());
            dto.setType(2);
            dto.setCreateTime(new Date());
            dto.setCreateUser(BaseContextHandler.getUsername());
            dto.setPlanNo(generateReportWms.getPlanNo(dto.getType()));
            dto.setMaterialNo(data.getMaterialCode());
            dto.setMaterialQty(data.getTheoryWeight());
            dto.setSpecification(data.getProductTypeSecond());
            dto.setUnit("kg");
            dto.setLength(data.getActualLength());
            dto.setWidth(data.getTheoryWeight());
            dto.setRemark("");
            dtoList.add(dto);
        }
        return dtoList;
    }

    @Override
    public Map<String, Object> getNo(String no) {
        return dao.findByCode(no);
    }

    @Override
    @Transactional
    public void updateState(YbIssueDto dto) {
        List<YbIssueDetial> byIssueId = ybIssueDetialService.findByIssueId(dto.getId());
        if (CollectionUtils.isNotEmpty(byIssueId)) {
            for (YbIssueDetial ybIssueDetial : byIssueId) {
                // 查询是否处于圆棒检验进站站点
                wipStorageService.selectWipStorage(ybIssueDetial.getCrystalNo(),
                        EnumValuesFactory.WipStorageCode.圆棒检验.getKey(), true,
                        EnumValuesFactory.PlanProcessingOrderStatus.组托.getKey());
            }

            for (YbIssueDetial ybIssueDetial : byIssueId) {
                // 查询是否处于圆棒检验进站站点
                wipStorageService.outStation(ybIssueDetial.getCrystalNo(), "B",
                        EnumValuesFactory.WipStorageStatus.待进站.getKey());
                // 单晶状态改为加工中
                updatePlanProcessingOrder1(ybIssueDetial.getCrystalNo());
            }
        }

        ybMesThreadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                // 报工给wms
                try {
                    List<ReportWmsRecordDto> dtoList = buildReportWmsRecordDto(byIssueId);
                    reportWmsRecordService.report(dtoList);
                } catch (Exception e) { // 报工不要影响主业务，如果报工失败可以用报工记录表做补偿。
                    e.printStackTrace();
                    log.error("圆棒报工", e);
                }
            }
        });
    }

    @Override
    public void updateIssueStatus(YbIssue entity) {
        dao.updateIssueStatus(entity.getStatus(), entity.getId());
    }

    @Override
    public void exportExcel(HttpServletRequest request, HttpServletResponse response, List<YbIssueToDetailDto> list) {
        try {
            getExportExcel(list, request, response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public List<Map<String, Object>> queryListByParam(String trayNo, String startTime, String endTime) {

        List<Map<String, Object>> ybIssueToDetailDtos = dao.queryListByParam(trayNo, startTime, endTime);
        return ybIssueToDetailDtos;
    }

    @Override
    public void creations(McsYbIssueDto dto) {
        // 1. 加载晶段编号集合（区分已入和其他）
        if (dto.getDetails() != null && dto.getDetails().isEmpty()) {
            throw new RuntimeException("圆棒发料明细为空！");
        }
        List<String> sectionNos = new ArrayList<>();
        List<String> errorSectionNos = new ArrayList<>();
        for (Iterator<McsYbIssueDetailDto> iterator = dto.getDetails().iterator(); iterator.hasNext();) {
            McsYbIssueDetailDto detailDto = iterator.next();
            if (detailDto.getStatus() != null && detailDto.getStatus() == 1) {
                sectionNos.add(detailDto.getSectionNo());
            } else {
                errorSectionNos.add(detailDto.getSectionNo());
            }
        }
        if (sectionNos.isEmpty()) {
            throw new RuntimeException("发料的圆棒为空！");
        }
        // 2. 符合要求的晶段查询产品等级 组装晶段编号与产品等级的映射关系
        List<Map<String, Object>> tempMapList = dao.findByCodes(sectionNos);
        Map<String, String> tempMapFinal = new HashMap<>(tempMapList.size());
        for (Iterator<Map<String, Object>> iterator = tempMapList.iterator(); iterator.hasNext();) {
            Map<String, Object> tempMap = iterator.next();
            tempMapFinal.put(String.valueOf(tempMap.get("sectionNo")), String.valueOf(tempMap.get("productLevel")));
        }
        // 3. 创建托号
        String trayNo = trayNo();
        Date nowDate = new Date();
        // 4. 创建YbIssue
        YbIssue ybIssue = new YbIssue();
        ybIssue.setTrayNo(trayNo).setNum(0).setName(dto.getUpdateBy()).setTime(nowDate)
                .setCurrentSite(EnumValuesFactory.WipStorageCode.圆棒检验.getValue())
                .setDestination(EnumValuesFactory.WipStorageCode.开方.getValue())
                .setStatus(EnumValuesFactory.IssueStatus.待出库.getKey());
        this.saveOrUpdate(ybIssue);
        // 5. 创建YbIssueDetial
        List<YbIssueDetial> detials = new ArrayList<>(sectionNos.size());
        for (Iterator<String> iterator = sectionNos.iterator(); iterator.hasNext();) {
            String sectionNo = iterator.next();
            YbIssueDetial tempDetial = new YbIssueDetial();
            tempDetial.setCurrentSite(EnumValuesFactory.WipStorageCode.圆棒检验.getValue()).setCrystalNo(sectionNo)
                    .setIssueId(ybIssue.getId()).setProductLevel(tempMapFinal.get(sectionNo))
                    .setDestination(EnumValuesFactory.WipStorageCode.开方.getValue());
            detials.add(tempDetial);
        }
        ybIssueDetialService.saveAll(detials);
        // 6. 更新YbIssue中num的属性值
        ybIssue.setNum(detials.size());
        this.saveOrUpdate(ybIssue);
        // 7. 创建YbIssueRecord
        List<YbIssueRecord> records = new ArrayList<>(dto.getDetails().size());
        for (Iterator<McsYbIssueDetailDto> iterator = dto.getDetails().iterator(); iterator.hasNext();) {
            McsYbIssueDetailDto detailDto = iterator.next();
            YbIssueRecord tempRecord = new YbIssueRecord();
            tempRecord.setSectionNo(detailDto.getSectionNo())
            .setNumNo(detailDto.getNumNo())
            .setStatus(detailDto.getStatus())
            .setTrayNo(trayNo)
            .setIsDeleted(0)
            .setGmtCreate(nowDate)
            .setBranchCode(dto.getBranchCode())
            .setSendStatus(0)
            .setGmtUpdate(nowDate);
            records.add(tempRecord);
        }
        ybIssueRecordService.saveAll(records);
    }

    /**
     * 圆棒发料过站到开方待进站
     * @param section
     */
    @Override
    public void updateState(String section) {
        // 查询是否处于圆棒检验进站站点
        wipStorageService.selectWipStorage(section,
                EnumValuesFactory.WipStorageCode.圆棒检验.getKey(), true,
                null);

        // 查询是否处于圆棒检验进站站点
        wipStorageService.outStation(section, "B",
                EnumValuesFactory.WipStorageStatus.待进站.getKey());
        // 单晶状态改为加工中
        updatePlanProcessingOrder1(section);

//        YbIssueDetial byIssueIdByCrystalNo = ybIssueDetialService.findByIssueIdByCrystalNo(section);
//        YbIssue entity = new YbIssue();
//        entity.setId(byIssueIdByCrystalNo.getIssueId());
//        entity.setStatus(3);
//        updateIssueStatus(entity);
    }

    public String trayNo() {
        // 规则匹配托号
        StringBuilder trayNo = new StringBuilder();
        String order5Num = null;
        String num5Value;
        String key5 = "FBOrderNum5";
        // 日期获取
        long time = new DateUtil().getBeginShiftsTime().getTime();
        Calendar cal = Calendar.getInstance();
        if (System.currentTimeMillis() < time) {
            cal.add(Calendar.DATE, -1);
        } else {
            cal.add(Calendar.DATE, 0);
        }
        String date = new SimpleDateFormat("yyMMdd").format(cal.getTime());
        if (redisTemplate.hasKey(key5)) {
            // 先获取redis中的顺序号
            num5Value = (String) redisTemplate.opsForValue().get(key5);
            order5Num = String.valueOf(Integer.parseInt(num5Value) + 1);
            if (order5Num.length() == 1) {
                order5Num = "0000" + order5Num;
            } else if (order5Num.length() == 2) {
                order5Num = "000" + order5Num;
            } else if (order5Num.length() == 3) {
                order5Num = "00" + order5Num;
            } else if (order5Num.length() == 4) {
                order5Num = "0" + order5Num;
            }
        } else {
            order5Num = "00001";
        }
        redisTemplate.opsForValue().set(key5, order5Num, redisKeyExpireTime(), TimeUnit.SECONDS);
        trayNo.append("YB").append(date).append(order5Num);
        if (null != dao.findByTrayNo(trayNo.toString())) {
            throw new RuntimeException("此托号:" + trayNo + "已存在！");
        }
        return trayNo.toString();
    }

    // 8点半之后自动删除当前key
    private int redisKeyExpireTime() {
        Calendar calendar = Calendar.getInstance();
        // 判断当前时间时分秒是否大于8:30
        if (timeCompare("08:30:00")) {
            calendar.add(Calendar.DAY_OF_YEAR, 1);
        }
        calendar.set(Calendar.HOUR_OF_DAY, 8);
        calendar.set(Calendar.MINUTE, 30);
        calendar.set(Calendar.SECOND, 0);
        long expireTime = ((calendar.getTimeInMillis() - System.currentTimeMillis()) / 1000);
        return (int) expireTime;
    }

    public static boolean timeCompare(String time) {
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        LocalTime localTime = LocalTime.parse(time, dateTimeFormatter);
        return LocalTime.now().isAfter(localTime);
    }

    //
    public void updatePlanProcessingOrder(String code, String destination) {
        PlanProcessingOrder byCode = planProcessingOrderService.findByCode(code);
        if (null != byCode) {
            if (!destination.equals("回收料")) {
                byCode.setStatus(EnumValuesFactory.PlanProcessingOrderStatus.组托.getKey());
            }
            planProcessingOrderService.saveOrUpdate(byCode);
        }
    }

    public void updatePlanProcessingOrder1(String code) {
        PlanProcessingOrder byCode = planProcessingOrderService.findByCode(code);
        if (null != byCode) {
            byCode.setStatus(EnumValuesFactory.PlanProcessingOrderStatus.加工中.getKey());
            planProcessingOrderService.saveOrUpdate(byCode);
        }
    }

    private void getExportExcel(List<YbIssueToDetailDto> list, HttpServletRequest request, HttpServletResponse resp)
            throws IOException {
        if (list.isEmpty()) {
            return;
        }
        HSSFWorkbook wb = new HSSFWorkbook();
        HSSFSheet sheet = wb.createSheet("导出圆棒发料记录");
        HSSFRow row;
        row = sheet.createRow(0);
        row.createCell(0).setCellValue("序号");
        row.createCell(1).setCellValue("托号");
        row.createCell(2).setCellValue("单晶编号");
        row.createCell(3).setCellValue("物料编码");
        row.createCell(4).setCellValue("入库状态");
        row.createCell(5).setCellValue("规格");
        row.createCell(6).setCellValue("掺杂剂");
        row.createCell(7).setCellValue("导电型号");
        row.createCell(8).setCellValue("产品等级");
        row.createCell(9).setCellValue("入库长度");
        row.createCell(10).setCellValue("入库重量");
        row.createCell(11).setCellValue("最大直径");
        row.createCell(12).setCellValue("最小直径");
        row.createCell(13).setCellValue("订单号");
        row.createCell(14).setCellValue("操作时间");

        for (int i = 0; i < list.size(); i++) {
            row = sheet.createRow(i + 1);
            YbIssueToDetailDto ybIssueToDetailDto = list.get(i);
            row.createCell(0).setCellValue(i + 1);
            if (ybIssueToDetailDto.getTrayNo() != null) {
                row.createCell(1).setCellValue(ybIssueToDetailDto.getTrayNo());
            }
            if (ybIssueToDetailDto.getCrystalNo() != null) {
                row.createCell(2).setCellValue(ybIssueToDetailDto.getCrystalNo());
            }
            if (ybIssueToDetailDto.getMaterialCode() != null) {
                row.createCell(3).setCellValue(ybIssueToDetailDto.getMaterialCode());
            }
            if (ybIssueToDetailDto.getStatus() != null) {
                String rawStatusName = "";
                switch (ybIssueToDetailDto.getStatus()) {
                    case 0:
                        rawStatusName = "待出库";
                        break;
                    case 1:
                        rawStatusName = "取消";
                        break;
                    case 2:
                        rawStatusName = "待确认";
                        break;
                    case 3:
                        rawStatusName = "已确认";
                }
                row.createCell(4).setCellValue(rawStatusName);
            }
            if (ybIssueToDetailDto.getProductTypeSecond() != null) {
                row.createCell(5).setCellValue(ybIssueToDetailDto.getProductTypeSecond());
            }
            if (ybIssueToDetailDto.getDopant() != null) {
                row.createCell(6).setCellValue(ybIssueToDetailDto.getDopant());
            }
            if (ybIssueToDetailDto.getConductiveType() != null) {
                row.createCell(7).setCellValue(ybIssueToDetailDto.getConductiveType());
            }
            if (ybIssueToDetailDto.getProductLevel() != null) {
                row.createCell(8).setCellValue(ybIssueToDetailDto.getProductLevel());
            }
            if (ybIssueToDetailDto.getActualLength() != null) {
                row.createCell(9).setCellValue(String.valueOf(ybIssueToDetailDto.getActualLength()));
            }
            if (ybIssueToDetailDto.getTheoryWeight() != null) {
                row.createCell(10).setCellValue(String.valueOf(ybIssueToDetailDto.getTheoryWeight()));
            }
            if (ybIssueToDetailDto.getDiameterMax() != null) {
                row.createCell(11).setCellValue(String.valueOf(ybIssueToDetailDto.getDiameterMax()));
            }
            if (ybIssueToDetailDto.getDiameterMin() != null) {
                row.createCell(12).setCellValue(String.valueOf(ybIssueToDetailDto.getDiameterMin()));
            }
            if (ybIssueToDetailDto.getOrderNo() != null) {
                row.createCell(13).setCellValue(ybIssueToDetailDto.getOrderNo());
            }
            if (ybIssueToDetailDto.getTime() != null) {
                row.createCell(14).setCellValue(DateUtil.formatDate(ybIssueToDetailDto.getTime(), DateUtil.longFormat));
            }
        }
        sheet.setDefaultRowHeight((short) (20 * 20));
        // 列宽自适应
        int num = 16;
        for (int i = 0; i <= num; i++) {
            sheet.autoSizeColumn(i);
        }
        String fileName = "圆棒发料记录表.xls";
        // 乱码解决
        String userAgent = request.getHeader("user-agent").toLowerCase();
        if (userAgent.contains("msie") || userAgent.contains("like gecko")) {
            // win10 ie edge 浏览器 和其他系统的ie
            fileName = URLEncoder.encode(fileName, "UTF-8");
        } else {
            // fe
            fileName = new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
        }
        // 输出Excel文件
        OutputStream output = resp.getOutputStream();
        resp.reset();
        resp.setHeader("Content-disposition", "attachment; filename=" + fileName);
        resp.setContentType("application/msexcel");
        wb.write(output);
        output.flush();
        output.close();
    }
}