package com.inspect.infrared;

import com.inspect.base.core.sftp.SftpClient;
import com.inspect.base.core.utils.ByteUtil;
import com.inspect.infrared.domain.InfraredCoordinate;
import com.inspect.infrared.domain.InfraredEntity;
import com.inspect.infrared.mapper.InfraredMapper;
import com.inspect.infrared.selectArea.SelectArea;
import com.inspect.infrared.selectArea.SelectAreaFactory;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class InfraredImageParser {
    private final SftpClient sftpClient;
    private final InfraredMapper infraredMapper;
    private final String SUFFIX_INFRARED = ".fir";

    public InfraredImageParser(SftpClient sftpClient, InfraredMapper infraredMapper) {
        this.sftpClient = sftpClient;
        this.infraredMapper = infraredMapper;
    }

    public InfraredResponse parse(InfraredRequestParam infraredRequestParam) throws Exception {
        InfraredResponse infraredResponse = new InfraredResponse(infraredRequestParam.getRequestId());

        for (InfraredRequestParam.Param param : infraredRequestParam.getObjectList()) {
            InfraredResponse.Param responseParam = new InfraredResponse.Param(param.getObjectId());
            infraredResponse.addParam(responseParam);
            String objectId = param.getObjectId();
            List<String> imageUrlList = param.getImageUrlList();
            List<InfraredEntity> infraredEntities = this.infraredMapper.selectCoordinate(objectId);

            for (int i = 0; i < imageUrlList.size(); ++i) {
                InfraredEntity infraredEntity;
                if (null != infraredEntities && !infraredEntities.isEmpty()) {
                    infraredEntity = infraredEntities.get(i);
                } else {
                    infraredEntity = InfraredEntity.createEmpty();
                }

                InfraredResponse.Result result = new InfraredResponse.Result();
                responseParam.addResult(result);
                String imgUrl = imageUrlList.get(i);
                result.setResImageUrl(imgUrl);
                InfraredT infraredT = this.parse(imgUrl, infraredEntity.getCoordinate());
                result.compare(infraredEntity, infraredT);
            }
        }

        return infraredResponse;
    }

    public InfraredT parse(String imgUrl, String coordinate) throws Exception {
        ByteBuf byteBuf = Unpooled.buffer();
        this.sftpClient.downLoad(imgUrl, (inputStream) -> {
            byte[] bytes = new byte[1024];

            int index;
            while ((index = inputStream.read(bytes)) > -1) {
                byteBuf.writeBytes(Arrays.copyOfRange(bytes, 0, index));
            }

        });
        byte[] bytes = new byte[byteBuf.readableBytes()];
        byteBuf.readBytes(bytes);
        int offset = this.calculateOffset(bytes);
        InfraredImage infraredImage = new InfraredImage();
        infraredImage.load(bytes, offset);
        List<List<byte[]>> table = this.combineTable(infraredImage);
        InfraredCoordinate infraredCoordinate = new InfraredCoordinate(coordinate);
        infraredCoordinate.convert(infraredImage);
        SelectArea selectArea = SelectAreaFactory.create(infraredCoordinate);
        List<byte[]> pixelPoints = selectArea.scanPixelPoints(table);
        InfraredT infraredT = this.calculateInfraredT(pixelPoints);
        infraredT.setWeatherT(infraredImage.getWeatherT());
        return infraredT;
    }

    private int calculateOffset(byte[] bytes) {
        byte[] offsetBytes = Arrays.copyOfRange(bytes, bytes.length - 20, bytes.length - 16);
        return ByteUtil.getInt(offsetBytes);
    }

    private List<List<byte[]>> combineTable(InfraredImage infraredImage) {
        List<byte[]> column = new ArrayList<>();

        for (int i = 0; i < infraredImage.getByteNum(); i += 4) {
            column.add(Arrays.copyOfRange(infraredImage.getBytes(), i, i + 4));
        }

        List<List<byte[]>> table = new ArrayList<>();
        for (int i = 0; i < column.size(); ++i) {
            if (i % infraredImage.getWidth() == 0) {
                table.add(new ArrayList<>());
            }

            List bytes = table.get(table.size() - 1);
            bytes.add(column.get(i));
        }

        return table;
    }

    private InfraredT calculateInfraredT(List<byte[]> pixelPoints) throws Exception {
        double sum = 0.0D;
        double max = -1000.0D;
        double min = 1000.0D;

        for (byte[] pixelPoint : pixelPoints) {
            byte[] column = pixelPoint;
            float f = ByteUtil.getFloat(column);
            sum += f;
            if (max < (double) f) {
                max = f;
            }

            if (min > (double) f) {
                min = f;
            }
        }

        return new InfraredT(max, min, sum / (double) pixelPoints.size());
    }
}
