package com.imdroid.sideslope.bd;

import io.netty.buffer.ByteBuf;
import lombok.Data;

import java.nio.charset.Charset;

@Data
public class Gga {
    public static int FIX_RESULT = 4;
    public static int FLOAT_RESULT = 5;
    final static byte[] GGA_FLAG =  {'$', 'X', 'X', 'G', 'G', 'A'};//$xxGGA

    final static byte[] d331_flag = {(byte) 0xd3, (byte)0x31};
    final static byte rtcm_flag = (byte)0xd3;
    final static byte[] d341_flag = {(byte) 0xd3, (byte)0x41};
    final static byte[] b562_flag = {(byte) 0xb5, (byte)0x62};

    /**
     * 纬度
     */
    private double latitude=0;

    /**
     * 经度
     */
    private double longitude=0;

    /**
     * 海拔
     */
    private double altitude=0;

    /**
     * GPS状态，0初始化，1单点定位，2码差分，3无效PPS，4固定解，5浮点解，6正在估算7，人工输入固定值，8模拟模式，9WAAS差分
     */
    private int quality=0;
    private int satellitesInUsed=0;

    public Gga() {
    }

    public static Gga getFrom(ByteBuf data){
        int msgFlag = data.getUnsignedShort(0);
        if(msgFlag != 0xD341 && msgFlag != 0xD331){
            return null;
        }
        int pos = 26; // 从惯导之后开始
        boolean found = false;
        while(pos<data.readableBytes()){
            byte d0 = data.getByte(pos);
            byte d1 = data.getByte(pos+1);
            if(GGA_FLAG[0] == d0){
                found = true;
                break;
            }
            else if(d0 == rtcm_flag){
                if(d1 == 0x31) { // d331粘包
                    pos += 26;
                }
                else {
                    // +----------+----------+----------+
                    // |    d3    |  6bit |   10bits    |
                    // +----------+----------+----------+
                    // d3 xx payload len(10bits)

                    pos++;
                    pos += ((data.getUnsignedShort(pos)&0x03FF) + 5);//length(2byte)+payload+crc(3byte)
                }
            }
            else if(b562_flag[0] == d0 && b562_flag[1] == d1){
                pos += 4; // type, class id
                pos += data.getUnsignedShortLE(pos) + 4;//length+payload+crc
            }
            else{
                pos++;
            }
        }
        if(found){
            return getFromGGAString(data, pos);
        }
        else return null;
    }

    static Gga getFromGGAString(ByteBuf data, int pos){
        String ggaStr = null;
        int length = data.readableBytes();
        for(int i=pos; i<length-1; i++) {
            byte d0 = data.getByte(i);
            byte d1 = data.getByte(i + 1);
            if ((byte)0xd == d0 && (byte)0xa == d1) {
                ggaStr = (String)data.getCharSequence(pos, i-pos, Charset.defaultCharset());
                break;
            }
        }
        if(ggaStr != null){
            String[] params = ggaStr.split(",",11);
            if(params.length == 11){
                Gga gga = new Gga();
                try {
                    gga.setLatitude(transGGAPos(Double.parseDouble(params[2])));
                    gga.setLongitude(transGGAPos(Double.parseDouble(params[4])));
                    gga.setAltitude(Double.parseDouble(params[9]));
                    gga.setQuality(Integer.parseInt(params[6]));
                    gga.setSatellitesInUsed(Integer.parseInt(params[7]));
                    return gga;
                }
                catch (Exception e){

                }

            }
        }
        return null;
    }

    static double transGGAPos(double pos){
        int degree = (int) (pos/100);
        return degree + (pos-degree*100)/60;
    }

    public Gga(double latitude, double longitude) {
        this.latitude = latitude;
        this.longitude = longitude;
    }

    public Gga(double latitude, double longitude, double altitude, int status) {
        this.latitude = latitude;
        this.longitude = longitude;
        this.altitude = altitude;
        this.quality = status;
    }

    public boolean isFixed(){
        //Possible values for quality: 0 = No fix, 1 = autonomous GNSS fix, 2 = differential GNSS fix, 4 = RTK fixed, 5 = RTK float, 6 =
        //estimated/dead reckoning fix
        //return (quality==1 || quality==2 || quality==4);
        return (quality==FIX_RESULT);
    }
}
