package com.imdroid.sideslope.bd;

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

import java.nio.charset.Charset;

@Data
public class Gga {
    final static byte[] GGA_FLAG =  {'$', 'G'};//$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;

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

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

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

    public Gga() {
    }

    static public Gga getFrom(ByteBuf data){
        int msgFlag = data.getUnsignedShort(0);
        if(msgFlag == 0xD341){
            return getFromD341(data);
        }
        else if(msgFlag == 0xD331){
            return getFromD331(data);
        }
        else return null;

    }

    static Gga getFromD331(ByteBuf data){
        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 && GGA_FLAG[1] == d1){
                found = true;
                break;
            }
            else if(d0 == rtcm_flag){
                if(d1 == 0x31) { // d331粘包
                    pos += 26;
                }
                else {
                    pos++;
                    pos += data.getUnsignedShort(pos) + 5;//length+payload+crc
                }
            }
            else{
                break;
            }
        }
        if(found){
            return getFromGGAString(data, pos);
        }
        else return null;
    }

    static Gga getFromD341(ByteBuf data){
        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 && GGA_FLAG[1] == d1){
                found = true;
                break;
            }
            else if(b562_flag[0] == d0 && b562_flag[1] == d1){
                pos += 4; // type, class id
                pos += data.getUnsignedShortLE(pos) + 4;//length+payload+crc
            }
            else{
                break;
            }
        }
        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;
    }

}
