package com.sysu.admin.controller.crop;

import com.mysql.jdbc.StringUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.QBean;
import com.querydsl.core.types.QList;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAUpdateClause;
import com.sysu.admin.controller.city.*;
import com.sysu.admin.controller.crop.executor_table.ExecutorTable;
import com.sysu.admin.controller.crop.executor_table.ExecutorTableService;
import com.sysu.admin.controller.crop.executor_table.PKRP;
import com.sysu.admin.controller.crop.images.CropImage;
import com.sysu.admin.controller.crop.images.CropImageService;
import com.sysu.admin.controller.crop.images.ImageType;
import com.sysu.admin.controller.crop.interceptor.DynamicTableNames;
import com.sysu.admin.controller.crop.range.AutoTableNameInterceptor;
import com.sysu.admin.controller.crop.range.LandRangeIndexService;
import com.sysu.admin.controller.geo.land.LandTaskStatus;
import com.sysu.admin.support.base.BaseService;
import com.sysu.admin.support.system.config.ConfigContext;
import com.sysu.admin.support.system.user.User;
import com.sysu.admin.utils.TextUtil;
import com.sysu.admin.utils.file.FileUtil;
import com.sysu.admin.utils.shape.GeoCastUtil;
import com.sysu.admin.utils.shape.ShapeReader;
import com.xiesx.fastboot.base.result.BaseResult;
import com.xiesx.fastboot.core.jpa.JpaPlusRepository;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ObjectUtils;
import org.geolatte.geom.crs.CoordinateReferenceSystems;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.Point;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.persistence.Query;
import javax.transaction.Transactional;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;

@Service
public class CropLandService extends BaseService<CropLand, Long> {

    @Autowired
    CropImageService cropImageService;
    @Autowired
    ConfigContext configContext;
    @Autowired
    ProvinceRepository provinceRepository;
    @Autowired
    CityRepository cityRepository;
    @Autowired
    DistrictRepository districtRepository;
    @Autowired
    LandRangeIndexService landRangeIndexService;
    @Autowired
    ExecutorTableService executorTableService;

    @Override
    @DynamicTableNames(value = "aLong")
    public CropLand findOne(Long aLong) {
        CropLand bean = super.findOne(aLong);
        return bean;
    }

    @Override
    @DynamicTableNames(value = "entity")
    public <S extends CropLand> S save(S entity) {
        return super.save(entity);
    }

    @DynamicTableNames(value = "point")
    public List<CropPoint> findByBuffer(Double[] point, Integer meter){
        double degree = meter / (2 * Math.PI * 6371004) * 360;
        return mCropPointRepository.findByBufferAndStatus("Point("+point[0]+" "+point[1]+")",degree, new Integer[]{
                LandTaskStatus.published.ordinal(),LandTaskStatus.receive.ordinal()
        });
    }

    @DynamicTableNames(executor = "executor")
    public List<Object[]> findIdAndNameByExecutor(Long executor){
        Query query =mEntityManager.createQuery("select c.id,c.name,c.updateDate,c.confirm from CropLand c where c.executor = " + executor);
        return query.getResultList();
    }

    @DynamicTableNames(value = "point")
    public CropLand findByPoint(Double[] point){
        CropLand cityLand = mCropLandRepository.findByPoint("Point("+point[0]+" "+point[1]+")");
        return cityLand;
    }

    public String getBuffer(Double[] point, Integer meter){
        double degree = meter / (2 * Math.PI * 6371004) * 360;
        return mCropPointRepository.getBuffer("Point("+point[0]+" "+point[1]+")", degree);
    }

    @DynamicTableNames(value = "tableNames")
    public List<CropPoint> findCropLandNoGeomByTableNamesAndBBox(String tableNames, Double x1, Double y1, Double x2, Double y2, String statusString){
        String wkt = bboxToWkt(x1, y1 , x2, y2);
        String sql = " select  "+ CropPoint.selectColumns;
        sql += " from p_crop  where ST_Intersects(geom,st_geomfromtext('"+wkt+"',4326)) ";
        if(ObjectUtils.isNotEmpty(statusString)){
            sql += " and \"status\" in ("+statusString+") ";
        }
        List<CropPoint> cropPointList = mEntityManager.createNativeQuery(sql, CropPoint.class).getResultList();
        return cropPointList;
    }

    @DynamicTableNames(value = "id")
    @Transactional
    public void updateStatus(Long id, Integer status, Long updateUserId){
        CropLand bean = mCropLandRepository.findOne(id);
        bean.setStatus(status);
        if(status.equals(LandTaskStatus.receive.ordinal())) {
            bean.setReceiveDate(new Date());
            bean.setReceiver(updateUserId);
        }
        mCropLandRepository.saveAndFlush(bean);
    }

    public Point getCenterPoint(Geometry geometry){
        geometry.setSRID(4326);
        return (Point)GeoCastUtil.wktToGeom(mCropLandRepository.getCenterPoint(geometry));
    }



    public void setCity(CropLand bean, District district){
        bean.setProvince(((int)(Integer.valueOf(district.getCode()) / 10000)) * 10000);
        bean.setCity(((int)(Integer.valueOf(district.getCode()) / 100)) * 100);
        bean.setDistrict(Integer.valueOf(district.getCode()));
    }

    @DynamicTableNames(value = "bean")
    @Transactional
    public void saveCropAndImages(CropLand bean){
        CropImage fay = bean.getFay();
        CropImage center = bean.getCenter();
        CropImage near = bean.getNear();
        saveImage(fay, bean.getId(), ImageType.fay, bean.getExecutor());
        saveImage(center, bean.getId(), ImageType.center, bean.getExecutor());
        saveImage(near, bean.getId(), ImageType.near, bean.getExecutor());
        String tableName = AutoTableNameInterceptor.myTable.get();
        ExecutorTable executorTable = new ExecutorTable().setPk(new PKRP().tableName(tableName).executor(bean.getExecutor()));
        mCropLandRepository.saveAndFlush(bean);
        executorTableService.save(executorTable);
    }

    private void saveImage(CropImage image, Long cropId, ImageType type, Long userId){
        if(ObjectUtils.isNotEmpty(image)) {
            cropImageService.deleteAll(cropImageService.findByCropIdAndType(cropId, type));
            image.setType(type.ordinal());
            image.setFilename(decryptByBase64(image.getBase64(), type.name(), userId));
            image.setCropId(cropId);
            cropImageService.save(image);
        }
    }

    @DynamicTableNames(value = "tableName")
    public void batchPublishByIds(String tableName, List<Long> ids){
        QCropLand qCropLand = QCropLand.cropLand;
        JPAUpdateClause jpaUpdateClause = mJPAQueryFactory.update(qCropLand);
        jpaUpdateClause.set(qCropLand.status, LandTaskStatus.published.ordinal());
        jpaUpdateClause.set(qCropLand.updateDate, new Date());
        Predicate predicate = null;
        if(ObjectUtils.isNotEmpty(ids)){
            predicate = qCropLand.id.in(ids);
        }
        if(predicate != null){
            predicate = qCropLand.status.eq(LandTaskStatus.unpublished.ordinal()).and(predicate);
            jpaUpdateClause.where(predicate);
            jpaUpdateClause.execute();
        }
    }

    @DynamicTableNames(value = "tableName")
    public void batchPublishByTableName(String tableName){
        QCropLand qCropLand = QCropLand.cropLand;
        JPAUpdateClause jpaUpdateClause = mJPAQueryFactory.update(qCropLand);
        jpaUpdateClause.set(qCropLand.status, LandTaskStatus.published.ordinal());
        jpaUpdateClause.set(qCropLand.updateDate, new Date());
        if(tableName != null){
            Predicate predicate = qCropLand.status.eq(LandTaskStatus.unpublished.ordinal());
            jpaUpdateClause.where(predicate);
            jpaUpdateClause.execute();
        }
    }

    @DynamicTableNames(value = "tableNames")
    public List<CropLand> findAllByStatusAndDistrict(String tableNames, Integer status){
        Query query =mEntityManager.createQuery(
                "select new CropLand(c.id,c.cropType,c.growingPeriod,c.confirm,c.district,c.name,c.address,c.desc) from CropLand c where c.status = " + status);
        return query.getResultList();
    }

    /**
     * 把base64转化为文件.
     *
     * @param base64   base64
     * @return boolean isTrue
     */
    public String decryptByBase64(String base64, String sign, Long userId) {
        if (StringUtils.isNullOrEmpty(base64)) {
            return null;
        }
        int index = base64.indexOf(",");
        if(index > -1){
            base64 = base64.substring(index + 1);
        }
        String imageDirPath = configContext.getImageDirPath();
        String newFileName = System.currentTimeMillis() + "_" + userId+"_"+sign+".jpg";
        try {
            Files.write(Paths.get(imageDirPath+ newFileName),
                    Base64.decodeBase64(base64), StandardOpenOption.CREATE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return newFileName;
    }

    /**
     * 把base64转化为文件.
     *
     * @param base64   base64
     * @return boolean isTrue
     */
    public Path fileByBase64(String base64, String sign, Long userId) {
        if (StringUtils.isNullOrEmpty(base64)) {
            return null;
        }
        int index = base64.indexOf(",");
        if(index > -1){
            base64 = base64.substring(index + 1);
        }
        String imageDirPath = configContext.getImageDirPath();
        String newFileName = System.currentTimeMillis() + "_" + userId+"_"+sign;
        Path path = null;
        try {
            path = Files.write(Paths.get(imageDirPath+ newFileName),
                    Base64.decodeBase64(base64), StandardOpenOption.CREATE);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return path;
    }


    public static String bboxToWkt(Double x1, Double y1, Double x2, Double y2){
        return "POLYGON (("+x1+" "+y1+", "+x1+" "+y2+", "+x2+" "+y2+", "+x2+" "+y1+", "+x1+" "+y1+"))";
    }

    @Transactional
    public void exec(String sql){
        mEntityManager.createNativeQuery(sql).executeUpdate();
    }

    @Override
    public JpaPlusRepository<CropLand, Long> r() {
        return mCropLandRepository;
    }

}
